mirror of
https://github.com/kyleneideck/BackgroundMusic
synced 2024-11-22 20:23:04 +00:00
Refactor non-UI code out of BGMAppVolumes.
This commit is contained in:
parent
fb0740c4c1
commit
1171bee102
25 changed files with 392 additions and 217 deletions
|
@ -36,7 +36,7 @@
|
|||
1C3D36721ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; };
|
||||
1C3D36731ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; };
|
||||
1C3D36741ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; };
|
||||
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */; };
|
||||
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */; };
|
||||
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C4699461BD5C0E400F78043 /* BGMiTunes.m */; };
|
||||
1C46994E1BD7694C00F78043 /* BGMDeviceControlSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */; };
|
||||
1C50FF631EC9F4490031A6EA /* BGMAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CF5423A1EAAEE4300445AD8 /* BGMAudioDevice.cpp */; };
|
||||
|
@ -65,6 +65,9 @@
|
|||
1CCC4F4E1E581C40008053E4 /* Mock_CAHALAudioObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F4C1E581C40008053E4 /* Mock_CAHALAudioObject.cpp */; };
|
||||
1CCC4F621E584100008053E4 /* BGMAppUITests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F611E584100008053E4 /* BGMAppUITests.mm */; };
|
||||
1CD1FD301BDDEAF2004F7E1B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; };
|
||||
1CD410D41F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */; };
|
||||
1CD410D51F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */; };
|
||||
1CD410D61F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */; };
|
||||
1CD989341ECFFC9E0014BBBF /* BGM_Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27FB8C2E1DE468320084DB9D /* BGM_Utils.cpp */; };
|
||||
1CD989351ECFFC9E0014BBBF /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7D1BE5068A00FB8FE4 /* CACFArray.cpp */; };
|
||||
1CD989361ECFFC9E0014BBBF /* CACFDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7F1BE5068A00FB8FE4 /* CACFDictionary.cpp */; };
|
||||
|
@ -79,7 +82,7 @@
|
|||
1CD9893F1ECFFC9E0014BBBF /* CAHALAudioSystemObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962F11BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp */; };
|
||||
1CD989401ECFFCC50014BBBF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; };
|
||||
1CD989411ECFFCD10014BBBF /* BGMAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33C1BBA75EF000E2DD1 /* BGMAppDelegate.mm */; };
|
||||
1CD989421ECFFCFC0014BBBF /* BGMAppVolumes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */; };
|
||||
1CD989421ECFFCFC0014BBBF /* BGMAppVolumes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */; };
|
||||
1CD989431ECFFCFC0014BBBF /* BGMAudioDeviceManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */; };
|
||||
1CD989441ECFFCFC0014BBBF /* BGMAutoPauseMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C457E51CF2BC2600A6C9A6 /* BGMAutoPauseMenuItem.m */; };
|
||||
1CD989451ECFFCFC0014BBBF /* BGMAutoPauseMusic.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C1465B71BCC3A73003AEFE6 /* BGMAutoPauseMusic.mm */; };
|
||||
|
@ -240,7 +243,7 @@
|
|||
1C2FC31D1EC723A100A76592 /* BGMASOutputDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMASOutputDevice.h; path = Scripting/BGMASOutputDevice.h; sourceTree = "<group>"; };
|
||||
1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlsList.cpp; sourceTree = "<group>"; };
|
||||
1C3D36711ED90E8600F98E66 /* BGMDeviceControlsList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlsList.h; sourceTree = "<group>"; };
|
||||
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAppVolumes.mm; sourceTree = "<group>"; };
|
||||
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BGMAppVolumes.m; sourceTree = "<group>"; };
|
||||
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumes.h; sourceTree = "<group>"; };
|
||||
1C4699461BD5C0E400F78043 /* BGMiTunes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMiTunes.m; path = "Music Players/BGMiTunes.m"; sourceTree = "<group>"; };
|
||||
1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlSync.cpp; sourceTree = "<group>"; };
|
||||
|
@ -277,6 +280,8 @@
|
|||
1CCC4F5F1E5840EF008053E4 /* BGMAppUITests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "BGMAppUITests-Info.plist"; path = "UITests/BGMAppUITests-Info.plist"; sourceTree = "<group>"; };
|
||||
1CCC4F611E584100008053E4 /* BGMAppUITests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAppUITests.mm; path = BGMAppTests/UITests/BGMAppUITests.mm; sourceTree = SOURCE_ROOT; };
|
||||
1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
1CD410D21F9EDDAD0070A094 /* BGMAppVolumesController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumesController.h; sourceTree = "<group>"; };
|
||||
1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAppVolumesController.mm; sourceTree = "<group>"; };
|
||||
1CE7064A1BF1EC0600BFC06D /* BGMOutputDevicePrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMOutputDevicePrefs.h; path = Preferences/BGMOutputDevicePrefs.h; sourceTree = "<group>"; };
|
||||
1CE7064B1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMOutputDevicePrefs.mm; path = Preferences/BGMOutputDevicePrefs.mm; sourceTree = "<group>"; };
|
||||
1CEACF4E1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mock_CAHALAudioSystemObject.cpp; path = UnitTests/Mock_CAHALAudioSystemObject.cpp; sourceTree = "<group>"; };
|
||||
|
@ -527,7 +532,9 @@
|
|||
1C837DD61F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.h */,
|
||||
1C837DD71F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.mm */,
|
||||
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */,
|
||||
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */,
|
||||
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */,
|
||||
1CD410D21F9EDDAD0070A094 /* BGMAppVolumesController.h */,
|
||||
1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */,
|
||||
1CED616A1C316E1A002CAFCF /* BGMAudioDeviceManager.h */,
|
||||
1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */,
|
||||
1CF5423B1EAAEE4300445AD8 /* BGMAudioDevice.h */,
|
||||
|
@ -921,6 +928,7 @@
|
|||
files = (
|
||||
1C86DA6A1F91EE3B000C8CCF /* CAPThread.cpp in Sources */,
|
||||
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */,
|
||||
1CD410D41F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */,
|
||||
1C1962E41BC94E15008A4DF7 /* CARingBuffer.cpp in Sources */,
|
||||
273F10DF1CC3D0B900C1C6DA /* BGMVOX.m in Sources */,
|
||||
1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */,
|
||||
|
@ -931,7 +939,7 @@
|
|||
1C2336DA1BEAB6E7004C1C4E /* BGMMusicPlayer.m in Sources */,
|
||||
1C1962F61BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp in Sources */,
|
||||
1CC1DF821BE5068A00FB8FE4 /* CACFDictionary.cpp in Sources */,
|
||||
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.mm in Sources */,
|
||||
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.m in Sources */,
|
||||
1C0BD0A51BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm in Sources */,
|
||||
1C1963061BCAF468008A4DF7 /* CAMutex.cpp in Sources */,
|
||||
1CE7064C1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm in Sources */,
|
||||
|
@ -974,11 +982,12 @@
|
|||
files = (
|
||||
1CACCF3A1F334447007F86CA /* BGMBackgroundMusicDevice.cpp in Sources */,
|
||||
1CC6593D1F91DEB400B0CCDC /* BGMTermination.mm in Sources */,
|
||||
1CD410D51F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */,
|
||||
1CD989571ECFFD250014BBBF /* CAHostTimeBase.cpp in Sources */,
|
||||
1CD989581ECFFD250014BBBF /* CAMutex.cpp in Sources */,
|
||||
1CD989591ECFFD250014BBBF /* CAPThread.cpp in Sources */,
|
||||
1CD9895A1ECFFD250014BBBF /* CARingBuffer.cpp in Sources */,
|
||||
1CD989421ECFFCFC0014BBBF /* BGMAppVolumes.mm in Sources */,
|
||||
1CD989421ECFFCFC0014BBBF /* BGMAppVolumes.m in Sources */,
|
||||
1CD989431ECFFCFC0014BBBF /* BGMAudioDeviceManager.mm in Sources */,
|
||||
1CD989441ECFFCFC0014BBBF /* BGMAutoPauseMenuItem.m in Sources */,
|
||||
1CD989451ECFFCFC0014BBBF /* BGMAutoPauseMusic.mm in Sources */,
|
||||
|
@ -1079,6 +1088,7 @@
|
|||
2743CA031D86D41C0089613B /* BGMScriptingBridge.m in Sources */,
|
||||
1CEACF4F1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp in Sources */,
|
||||
2743CA041D86D41C0089613B /* BGMMusicPlayer.m in Sources */,
|
||||
1CD410D61F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */,
|
||||
2743CA051D86D41C0089613B /* BGMDecibel.m in Sources */,
|
||||
2743CA061D86D41C0089613B /* BGMSpotify.m in Sources */,
|
||||
2743CA071D86D41C0089613B /* BGMVLC.m in Sources */,
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#import "BGMMusicPlayers.h"
|
||||
#import "BGMAutoPauseMusic.h"
|
||||
#import "BGMAutoPauseMenuItem.h"
|
||||
#import "BGMAppVolumes.h"
|
||||
#import "BGMAppVolumesController.h"
|
||||
#import "BGMPreferencesMenu.h"
|
||||
#import "BGMXPCListener.h"
|
||||
#import "BGMOutputVolumeMenuItem.h"
|
||||
|
@ -55,7 +55,7 @@ static float const kStatusBarIconPadding = 0.25;
|
|||
BGMAutoPauseMusic* autoPauseMusic;
|
||||
BGMAutoPauseMenuItem* autoPauseMenuItem;
|
||||
BGMMusicPlayers* musicPlayers;
|
||||
BGMAppVolumes* appVolumes;
|
||||
BGMAppVolumesController* appVolumes;
|
||||
BGMPreferencesMenu* prefsMenu;
|
||||
BGMXPCListener* xpcListener;
|
||||
}
|
||||
|
@ -182,9 +182,9 @@ static float const kStatusBarIconPadding = 0.25;
|
|||
atIndex:([self.bgmMenu indexOfItemWithTag:kVolumesHeadingMenuItemTag] + 1)];
|
||||
|
||||
|
||||
appVolumes = [[BGMAppVolumes alloc] initWithMenu:self.bgmMenu
|
||||
appVolumeView:self.appVolumeView
|
||||
audioDevices:audioDevices];
|
||||
appVolumes = [[BGMAppVolumesController alloc] initWithMenu:self.bgmMenu
|
||||
appVolumeView:self.appVolumeView
|
||||
audioDevices:audioDevices];
|
||||
|
||||
prefsMenu = [[BGMPreferencesMenu alloc] initWithBGMMenu:self.bgmMenu
|
||||
audioDevices:audioDevices
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
// BGMAppVolumes.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Copyright © 2016, 2017 Kyle Neideck
|
||||
//
|
||||
|
||||
// Local Includes
|
||||
|
@ -26,6 +26,7 @@
|
|||
// System Includes
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
@interface BGMAppVolumes : NSObject
|
||||
|
||||
|
@ -33,6 +34,15 @@
|
|||
appVolumeView:(NSView*)view
|
||||
audioDevices:(BGMAudioDeviceManager*)audioDevices;
|
||||
|
||||
// Pass -1 for initialVolume or initialPan to leave the volume/pan at its default level.
|
||||
- (void) insertMenuItemForApp:(NSRunningApplication*)app
|
||||
initialVolume:(int)volume
|
||||
initialPan:(int)pan;
|
||||
|
||||
- (void) removeMenuItemForApp:(NSRunningApplication*)app;
|
||||
|
||||
- (void) removeAllAppVolumeMenuItems;
|
||||
|
||||
@end
|
||||
|
||||
// Protocol for the UI custom classes
|
||||
|
@ -58,13 +68,16 @@
|
|||
|
||||
@interface BGMAVM_VolumeSlider : NSSlider <BGMAppVolumeMenuItemSubview>
|
||||
|
||||
- (void) setRelativeVolume:(NSNumber*)relativeVolume;
|
||||
- (void) setRelativeVolume:(int)relativeVolume;
|
||||
|
||||
@end
|
||||
|
||||
@interface BGMAVM_PanSlider : NSSlider <BGMAppVolumeMenuItemSubview>
|
||||
|
||||
- (void) setPanPosition:(NSNumber*)panPosition;
|
||||
- (void) setPanPosition:(int)panPosition;
|
||||
|
||||
@end
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
|
||||
|
|
|
@ -30,9 +30,7 @@
|
|||
#import "BGMAppDelegate.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#import "CACFDictionary.h"
|
||||
#import "CACFArray.h"
|
||||
#import "CACFString.h"
|
||||
#import "CADebugMacros.h"
|
||||
|
||||
|
||||
static float const kSlidersSnapWithin = 5;
|
||||
|
@ -52,7 +50,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
NSInteger numMenuItems;
|
||||
}
|
||||
|
||||
- (id) initWithMenu:(NSMenu*)menu appVolumeView:(NSView*)view audioDevices:(BGMAudioDeviceManager*)devices {
|
||||
- (id) initWithMenu:(NSMenu*)menu
|
||||
appVolumeView:(NSView*)view
|
||||
audioDevices:(BGMAudioDeviceManager*)devices {
|
||||
if ((self = [super init])) {
|
||||
bgmMenu = menu;
|
||||
moreAppsMenu = [[NSMenu alloc] initWithTitle:kMoreAppsMenuTitle];
|
||||
|
@ -60,9 +60,6 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
appVolumeViewFullHeight = appVolumeView.frame.size.height;
|
||||
audioDevices = devices;
|
||||
numMenuItems = 0;
|
||||
|
||||
// Create the menu items for controlling app volumes
|
||||
[self insertMenuItemsForApps:[[NSWorkspace sharedWorkspace] runningApplications]];
|
||||
|
||||
// Add the More Apps menu to the main menu.
|
||||
NSMenuItem* moreAppsMenuItem =
|
||||
|
@ -79,21 +76,11 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
|
||||
[bgmMenu insertItem:spacer atIndex:[self lastMenuItemIndex]];
|
||||
numMenuItems++;
|
||||
|
||||
// Register for notifications when the user opens or closes apps, so we can update the menu
|
||||
[[NSWorkspace sharedWorkspace] addObserver:self
|
||||
forKeyPath:@"runningApplications"
|
||||
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
|
||||
context:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[[NSWorkspace sharedWorkspace] removeObserver:self forKeyPath:@"runningApplications" context:nil];
|
||||
}
|
||||
|
||||
// This method allows the Interface Builder Custom Classes for controls (below) to send their values
|
||||
// directly to BGMDevice. Not public to other classes.
|
||||
- (BGMAudioDeviceManager*) audioDevices {
|
||||
|
@ -102,37 +89,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
|
||||
#pragma mark UI Modifications
|
||||
|
||||
// Adds a volume control menu item for each given app.
|
||||
- (void) insertMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
|
||||
NSAssert([NSThread isMainThread], @"insertMenuItemsForApps is not thread safe");
|
||||
|
||||
// TODO: Handle the C++ exceptions this method can throw. They can cause crashes because this
|
||||
// method is called in a KVO handler.
|
||||
|
||||
// Get the app volumes currently set on the device
|
||||
CACFArray appVols([audioDevices bgmDevice].GetAppVolumes(), false);
|
||||
|
||||
for (NSRunningApplication* app in apps) {
|
||||
if ([self shouldBeIncludedInMenu:app]) {
|
||||
[self insertMenuItemForApp:app appVolumesOnDevice:appVols];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) shouldBeIncludedInMenu:(NSRunningApplication*)app {
|
||||
// Ignore hidden apps and Background Music itself.
|
||||
// TODO: Would it be better to only show apps that are registered as HAL clients?
|
||||
BOOL isHidden = app.activationPolicy != NSApplicationActivationPolicyRegular &&
|
||||
app.activationPolicy != NSApplicationActivationPolicyAccessory;
|
||||
|
||||
NSString* bundleID = app.bundleIdentifier;
|
||||
BOOL isBGMApp = bundleID && [@kBGMAppBundleID isEqualToString:BGMNN(bundleID)];
|
||||
|
||||
return !isHidden && !isBGMApp;
|
||||
}
|
||||
|
||||
- (void) insertMenuItemForApp:(NSRunningApplication*)app
|
||||
appVolumesOnDevice:(const CACFArray&)appVols {
|
||||
initialVolume:(int)volume
|
||||
initialPan:(int)pan {
|
||||
NSMenuItem* appVolItem = [self createBlankAppVolumeMenuItem];
|
||||
|
||||
// Look through the menu item's subviews for the ones we want to set up
|
||||
|
@ -148,7 +107,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
appVolItem.representedObject = app;
|
||||
|
||||
// Set the slider to the volume for this app if we got one from the driver
|
||||
[self setVolumeOfMenuItem:appVolItem fromAppVolumes:appVols];
|
||||
[self setVolumeOfMenuItem:appVolItem relativeVolume:volume panPosition:pan];
|
||||
|
||||
// NSMenuItem didn't implement NSAccessibility before OS X SDK 10.12.
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 // MAC_OS_X_VERSION_10_12
|
||||
|
@ -180,6 +139,19 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
return menuItem;
|
||||
}
|
||||
|
||||
- (void) setVolumeOfMenuItem:(NSMenuItem*)menuItem relativeVolume:(int)volume panPosition:(int)pan {
|
||||
// Update the sliders.
|
||||
for (NSView* subview in menuItem.view.subviews) {
|
||||
if (volume != -1 && [subview isKindOfClass:[BGMAVM_VolumeSlider class]]) {
|
||||
[(BGMAVM_VolumeSlider*)subview setRelativeVolume:volume];
|
||||
}
|
||||
|
||||
if (pan != -1 && [subview isKindOfClass:[BGMAVM_PanSlider class]]) {
|
||||
[(BGMAVM_PanSlider*)subview setPanPosition:pan];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger) firstMenuItemIndex {
|
||||
return [self lastMenuItemIndex] - numMenuItems + 1;
|
||||
}
|
||||
|
@ -188,81 +160,34 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
return [bgmMenu indexOfItemWithTag:kSeparatorBelowVolumesMenuItemTag] - 1;
|
||||
}
|
||||
|
||||
- (void) removeMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
|
||||
NSAssert([NSThread isMainThread], @"removeMenuItemsForApps is not thread safe");
|
||||
|
||||
- (void) removeMenuItemForApp:(NSRunningApplication*)app {
|
||||
// Subtract two extra positions to skip the More Apps menu and the spacer menu item above it.
|
||||
NSInteger lastAppVolumeMenuItemIndex = [self lastMenuItemIndex] - 2;
|
||||
|
||||
// Check each app volume menu item, removing the items that control one of the given apps
|
||||
for (NSRunningApplication* appToBeRemoved in apps) {
|
||||
BOOL didRemoveItem = NO;
|
||||
// Check each app volume menu item and remove the item that controls the given app.
|
||||
|
||||
// Look through the main menu.
|
||||
for (NSInteger i = [self firstMenuItemIndex]; i <= lastAppVolumeMenuItemIndex; i++) {
|
||||
NSMenuItem* item = [bgmMenu itemAtIndex:i];
|
||||
NSRunningApplication* itemApp = item.representedObject;
|
||||
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
|
||||
// Look through the main menu.
|
||||
for (NSInteger i = [self firstMenuItemIndex]; i <= lastAppVolumeMenuItemIndex; i++) {
|
||||
NSMenuItem* item = [bgmMenu itemAtIndex:i];
|
||||
NSRunningApplication* itemApp = item.representedObject;
|
||||
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
|
||||
|
||||
if ([itemApp isEqual:appToBeRemoved]) {
|
||||
[bgmMenu removeItem:item];
|
||||
numMenuItems--;
|
||||
|
||||
didRemoveItem = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Look through the More Apps menu.
|
||||
if (!didRemoveItem) {
|
||||
for (NSInteger i = 0; i < [moreAppsMenu numberOfItems]; i++) {
|
||||
NSMenuItem* item = [moreAppsMenu itemAtIndex:i];
|
||||
NSRunningApplication* itemApp = item.representedObject;
|
||||
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
|
||||
|
||||
if ([itemApp isEqual:appToBeRemoved]) {
|
||||
[moreAppsMenu removeItem:item];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ([itemApp isEqual:app]) {
|
||||
[bgmMenu removeItem:item];
|
||||
numMenuItems--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setVolumeOfMenuItem:(NSMenuItem*)menuItem fromAppVolumes:(const CACFArray&)appVolumes {
|
||||
// Set menuItem's volume slider to the volume of the app in appVolumes that menuItem represents
|
||||
// Leaves menuItem unchanged if it doesn't match any of the apps in appVolumes
|
||||
NSRunningApplication* representedApp = menuItem.representedObject;
|
||||
|
||||
for (UInt32 i = 0; i < appVolumes.GetNumberItems(); i++) {
|
||||
CACFDictionary appVolume(false);
|
||||
appVolumes.GetCACFDictionary(i, appVolume);
|
||||
|
||||
// Match the app to the menu item by pid or bundle id
|
||||
CACFString bundleID;
|
||||
bundleID.DontAllowRelease();
|
||||
appVolume.GetCACFString(CFSTR(kBGMAppVolumesKey_BundleID), bundleID);
|
||||
|
||||
pid_t pid;
|
||||
appVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), pid);
|
||||
|
||||
if ((representedApp.processIdentifier == pid) ||
|
||||
[representedApp.bundleIdentifier isEqualToString:(__bridge NSString*)bundleID.GetCFString()]) {
|
||||
CFTypeRef relativeVolume;
|
||||
appVolume.GetCFType(CFSTR(kBGMAppVolumesKey_RelativeVolume), relativeVolume);
|
||||
|
||||
CFTypeRef panPosition;
|
||||
appVolume.GetCFType(CFSTR(kBGMAppVolumesKey_PanPosition), panPosition);
|
||||
|
||||
// Update the slider
|
||||
for (NSView* subview in menuItem.view.subviews) {
|
||||
if ([subview respondsToSelector:@selector(setRelativeVolume:)]) {
|
||||
[subview performSelector:@selector(setRelativeVolume:) withObject:(__bridge NSNumber*)relativeVolume];
|
||||
}
|
||||
if ([subview respondsToSelector:@selector(setPanPosition:)]) {
|
||||
[subview performSelector:@selector(setPanPosition:) withObject:(__bridge NSNumber*)panPosition];
|
||||
}
|
||||
}
|
||||
// Look through the More Apps menu.
|
||||
for (NSInteger i = 0; i < [moreAppsMenu numberOfItems]; i++) {
|
||||
NSMenuItem* item = [moreAppsMenu itemAtIndex:i];
|
||||
NSRunningApplication* itemApp = item.representedObject;
|
||||
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
|
||||
|
||||
if ([itemApp isEqual:app]) {
|
||||
[moreAppsMenu removeItem:item];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -281,8 +206,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
#if DEBUG
|
||||
const char* appName = [((NSRunningApplication*)menuItem.representedObject).localizedName UTF8String];
|
||||
#endif
|
||||
|
||||
auto nearEnough = [](CGFloat x, CGFloat y) { // Shouldn't be necessary, but just in case.
|
||||
|
||||
// Using this function (instead of just ==) shouldn't be necessary, but just in case.
|
||||
BOOL(^nearEnough)(CGFloat x, CGFloat y) = ^BOOL(CGFloat x, CGFloat y) {
|
||||
return fabs(x - y) < 0.01; // We don't need much precision.
|
||||
};
|
||||
|
||||
|
@ -293,7 +219,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
BGMAssert(nearEnough(height, appVolumeViewFullHeight), "Extra controls were already hidden");
|
||||
|
||||
// Make the menu item shorter to hide the extra controls. Keep the width unchanged.
|
||||
menuItem.view.frameSize = { width, kAppVolumeViewInitialHeight };
|
||||
menuItem.view.frameSize = NSMakeSize(width, kAppVolumeViewInitialHeight);
|
||||
// Turn the button upside down so the arrowhead points down.
|
||||
button.frameCenterRotation = 180.0;
|
||||
// Move the button up slightly so it aligns with the volume slider.
|
||||
|
@ -315,7 +241,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
BGMAssert(nearEnough(height, kAppVolumeViewInitialHeight), "Extra controls were already shown");
|
||||
|
||||
// Make the menu item taller to show the extra controls. Keep the width unchanged.
|
||||
menuItem.view.frameSize = { width, appVolumeViewFullHeight };
|
||||
menuItem.view.frameSize = NSMakeSize(width, appVolumeViewFullHeight);
|
||||
// Turn the button rightside up so the arrowhead points up.
|
||||
button.frameCenterRotation = 0.0;
|
||||
// Move the button down slightly, back to it's original position.
|
||||
|
@ -328,38 +254,16 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark KVO
|
||||
|
||||
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
#pragma unused (object, context)
|
||||
|
||||
// KVO callback for the apps currently running on the system. Adds/removes the associated menu items.
|
||||
if ([keyPath isEqualToString:@"runningApplications"]) {
|
||||
NSArray<NSRunningApplication*>* newApps = change[NSKeyValueChangeNewKey];
|
||||
NSArray<NSRunningApplication*>* oldApps = change[NSKeyValueChangeOldKey];
|
||||
|
||||
int changeKind = [[change valueForKey:NSKeyValueChangeKindKey] intValue];
|
||||
switch (changeKind) {
|
||||
case NSKeyValueChangeInsertion:
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeRemoval:
|
||||
[self removeMenuItemsForApps:oldApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeReplacement:
|
||||
[self removeMenuItemsForApps:oldApps];
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeSetting:
|
||||
[bgmMenu removeAllItems];
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
}
|
||||
- (void) removeAllAppVolumeMenuItems {
|
||||
// Remove all of the menu items this class adds to the menu except for the last two, which are
|
||||
// the More Apps menu item and the invisible spacer above it.
|
||||
while (numMenuItems > 2) {
|
||||
[bgmMenu removeItemAtIndex:[self firstMenuItemIndex]];
|
||||
numMenuItems--;
|
||||
}
|
||||
|
||||
// The More Apps menu only contains app volume menu items, so we can just remove everything.
|
||||
[moreAppsMenu removeAllItems];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -429,7 +333,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
@implementation BGMAVM_VolumeSlider {
|
||||
// Will be set to -1 for apps without a pid
|
||||
pid_t appProcessID;
|
||||
NSString* appBundleID;
|
||||
NSString* __nullable appBundleID;
|
||||
BGMAppVolumes* context;
|
||||
}
|
||||
|
||||
|
@ -459,14 +363,14 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
// changes how the slider looks.
|
||||
- (void) snap {
|
||||
// Snap to the 50% point.
|
||||
float midPoint = static_cast<float>((self.maxValue + self.minValue) / 2);
|
||||
float midPoint = (float)((self.maxValue + self.minValue) / 2);
|
||||
if (self.floatValue > (midPoint - kSlidersSnapWithin) && self.floatValue < (midPoint + kSlidersSnapWithin)) {
|
||||
self.floatValue = midPoint;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setRelativeVolume:(NSNumber*)relativeVolume {
|
||||
self.intValue = relativeVolume.intValue;
|
||||
- (void) setRelativeVolume:(int)relativeVolume {
|
||||
self.intValue = relativeVolume;
|
||||
[self snap];
|
||||
}
|
||||
|
||||
|
@ -479,9 +383,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
|
||||
// The values from our sliders are in
|
||||
// [kAppRelativeVolumeMinRawValue, kAppRelativeVolumeMaxRawValue] already.
|
||||
context.audioDevices.bgmDevice.SetAppVolume(self.intValue,
|
||||
appProcessID,
|
||||
(__bridge_retained CFStringRef)appBundleID);
|
||||
[context.audioDevices setVolume:self.intValue
|
||||
forAppWithProcessID:appProcessID
|
||||
bundleID:appBundleID];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -489,7 +393,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
@implementation BGMAVM_PanSlider {
|
||||
// Will be set to -1 for apps without a pid
|
||||
pid_t appProcessID;
|
||||
NSString* appBundleID;
|
||||
NSString* __nullable appBundleID;
|
||||
BGMAppVolumes* context;
|
||||
}
|
||||
|
||||
|
@ -515,8 +419,8 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
}
|
||||
}
|
||||
|
||||
- (void) setPanPosition:(NSNumber *)panPosition {
|
||||
self.intValue = panPosition.intValue;
|
||||
- (void) setPanPosition:(int)panPosition {
|
||||
self.intValue = panPosition;
|
||||
}
|
||||
|
||||
- (void) appPanPositionChanged {
|
||||
|
@ -525,9 +429,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
|
|||
DebugMsg("BGMAppVolumes::appPanPositionChanged: App pan position for %s changed to %d", appBundleID.UTF8String, self.intValue);
|
||||
|
||||
// The values from our sliders are in [kAppPanLeftRawValue, kAppPanRightRawValue] already.
|
||||
context.audioDevices.bgmDevice.SetAppPanPosition(self.intValue,
|
||||
appProcessID,
|
||||
(__bridge_retained CFStringRef)appBundleID);
|
||||
[context.audioDevices setPanPosition:self.intValue
|
||||
forAppWithProcessID:appProcessID
|
||||
bundleID:appBundleID];
|
||||
}
|
||||
|
||||
@end
|
41
BGMApp/BGMApp/BGMAppVolumesController.h
Normal file
41
BGMApp/BGMApp/BGMAppVolumesController.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// BGMAppVolumesController.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
//
|
||||
|
||||
// Local Includes
|
||||
#import "BGMAudioDeviceManager.h"
|
||||
|
||||
// System Includes
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
@interface BGMAppVolumesController : NSObject
|
||||
|
||||
- (id) initWithMenu:(NSMenu*)menu
|
||||
appVolumeView:(NSView*)view
|
||||
audioDevices:(BGMAudioDeviceManager*)audioDevices;
|
||||
|
||||
@end
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
195
BGMApp/BGMApp/BGMAppVolumesController.mm
Normal file
195
BGMApp/BGMApp/BGMAppVolumesController.mm
Normal file
|
@ -0,0 +1,195 @@
|
|||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// BGMAppVolumesController.mm
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
// Copyright © 2017 Andrew Tonner
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMAppVolumesController.h"
|
||||
|
||||
// Local Includes
|
||||
#import "BGM_Types.h"
|
||||
#import "BGM_Utils.h"
|
||||
#import "BGMAppVolumes.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#import "CACFArray.h"
|
||||
#import "CACFDictionary.h"
|
||||
#import "CACFString.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
typedef struct BGMAppVolumeAndPan {
|
||||
int volume;
|
||||
int pan;
|
||||
} BGMAppVolumeAndPan;
|
||||
|
||||
@implementation BGMAppVolumesController {
|
||||
// The App Volumes UI.
|
||||
BGMAppVolumes* appVolumes;
|
||||
BGMAudioDeviceManager* audioDevices;
|
||||
}
|
||||
|
||||
- (id) initWithMenu:(NSMenu*)menu
|
||||
appVolumeView:(NSView*)view
|
||||
audioDevices:(BGMAudioDeviceManager*)devices {
|
||||
if ((self = [super init])) {
|
||||
audioDevices = devices;
|
||||
appVolumes = [[BGMAppVolumes alloc] initWithMenu:menu
|
||||
appVolumeView:view
|
||||
audioDevices:devices];
|
||||
|
||||
// Create the menu items for controlling app volumes.
|
||||
NSArray<NSRunningApplication*>* apps = [[NSWorkspace sharedWorkspace] runningApplications];
|
||||
[self insertMenuItemsForApps:apps];
|
||||
|
||||
// Register for notifications when the user opens or closes apps, so we can update the menu.
|
||||
auto opts = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
|
||||
[[NSWorkspace sharedWorkspace] addObserver:self
|
||||
forKeyPath:@"runningApplications"
|
||||
options:opts
|
||||
context:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[[NSWorkspace sharedWorkspace] removeObserver:self
|
||||
forKeyPath:@"runningApplications"
|
||||
context:nil];
|
||||
}
|
||||
|
||||
// Adds a volume control menu item for each given app.
|
||||
- (void) insertMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
|
||||
NSAssert([NSThread isMainThread], @"insertMenuItemsForApps is not thread safe");
|
||||
|
||||
// TODO: Handle the C++ exceptions this method can throw. They can cause crashes because this
|
||||
// method is called in a KVO handler.
|
||||
|
||||
// Get the app volumes currently set on the device
|
||||
CACFArray volumesFromBGMDevice([audioDevices bgmDevice].GetAppVolumes(), false);
|
||||
|
||||
for (NSRunningApplication* app in apps) {
|
||||
if ([self shouldBeIncludedInMenu:app]) {
|
||||
BGMAppVolumeAndPan initial = [self getVolumeAndPanForApp:app
|
||||
fromVolumes:volumesFromBGMDevice];
|
||||
[appVolumes insertMenuItemForApp:app
|
||||
initialVolume:initial.volume
|
||||
initialPan:initial.pan];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication*)app
|
||||
fromVolumes:(const CACFArray&)volumes {
|
||||
BGMAppVolumeAndPan volumeAndPan = {
|
||||
.volume = -1,
|
||||
.pan = -1
|
||||
};
|
||||
|
||||
for (UInt32 i = 0; i < volumes.GetNumberItems(); i++) {
|
||||
CACFDictionary appVolume(false);
|
||||
volumes.GetCACFDictionary(i, appVolume);
|
||||
|
||||
// Match the app to the volume/pan by pid or bundle ID.
|
||||
CACFString bundleID;
|
||||
bundleID.DontAllowRelease();
|
||||
appVolume.GetCACFString(CFSTR(kBGMAppVolumesKey_BundleID), bundleID);
|
||||
|
||||
pid_t pid;
|
||||
appVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), pid);
|
||||
|
||||
if ((app.processIdentifier == pid) ||
|
||||
[app.bundleIdentifier isEqualToString:(__bridge NSString*)bundleID.GetCFString()]) {
|
||||
// Found a match, so read the volume and pan.
|
||||
appVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), volumeAndPan.volume);
|
||||
appVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_PanPosition), volumeAndPan.pan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return volumeAndPan;
|
||||
}
|
||||
|
||||
- (BOOL) shouldBeIncludedInMenu:(NSRunningApplication*)app {
|
||||
// Ignore hidden apps and Background Music itself.
|
||||
// TODO: Would it be better to only show apps that are registered as HAL clients?
|
||||
BOOL isHidden = app.activationPolicy != NSApplicationActivationPolicyRegular &&
|
||||
app.activationPolicy != NSApplicationActivationPolicyAccessory;
|
||||
|
||||
NSString* bundleID = app.bundleIdentifier;
|
||||
BOOL isBGMApp = bundleID && [@kBGMAppBundleID isEqualToString:BGMNN(bundleID)];
|
||||
|
||||
return !isHidden && !isBGMApp;
|
||||
}
|
||||
|
||||
- (void) removeMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
|
||||
NSAssert([NSThread isMainThread], @"removeMenuItemsForApps is not thread safe");
|
||||
|
||||
for (NSRunningApplication* app in apps) {
|
||||
[appVolumes removeMenuItemForApp:app];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark KVO
|
||||
|
||||
- (void) observeValueForKeyPath:(NSString* __nullable)keyPath
|
||||
ofObject:(id __nullable)object
|
||||
change:(NSDictionary* __nullable)change
|
||||
context:(void* __nullable)context
|
||||
{
|
||||
#pragma unused (object, context)
|
||||
|
||||
// KVO callback for the apps currently running on the system. Adds/removes the associated menu
|
||||
// items.
|
||||
if (keyPath && change && [keyPath isEqualToString:@"runningApplications"]) {
|
||||
NSArray<NSRunningApplication*>* newApps = change[NSKeyValueChangeNewKey];
|
||||
NSArray<NSRunningApplication*>* oldApps = change[NSKeyValueChangeOldKey];
|
||||
|
||||
int changeKind = [change[NSKeyValueChangeKindKey] intValue];
|
||||
|
||||
switch (changeKind) {
|
||||
case NSKeyValueChangeInsertion:
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeRemoval:
|
||||
[self removeMenuItemsForApps:oldApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeReplacement:
|
||||
[self removeMenuItemsForApps:oldApps];
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeSetting:
|
||||
[appVolumes removeAllAppVolumeMenuItems];
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
#if defined(__cplusplus)
|
||||
|
||||
// Local Includes
|
||||
#include "BGMBackgroundMusicDevice.h"
|
||||
#import "BGMBackgroundMusicDevice.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#import "CAHALAudioDevice.h"
|
||||
|
@ -40,9 +40,9 @@
|
|||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
const int kBGMErrorCode_BGMDeviceNotFound = 1;
|
||||
const int kBGMErrorCode_OutputDeviceNotFound = 2;
|
||||
const int kBGMErrorCode_ReturningEarly = 3;
|
||||
static const int kBGMErrorCode_BGMDeviceNotFound = 1;
|
||||
static const int kBGMErrorCode_OutputDeviceNotFound = 2;
|
||||
static const int kBGMErrorCode_ReturningEarly = 3;
|
||||
|
||||
@interface BGMAudioDeviceManager : NSObject
|
||||
|
||||
|
@ -62,6 +62,13 @@ const int kBGMErrorCode_ReturningEarly = 3;
|
|||
- (CAHALAudioDevice) outputDevice;
|
||||
#endif
|
||||
|
||||
- (void) setVolume:(SInt32)volume
|
||||
forAppWithProcessID:(pid_t)processID
|
||||
bundleID:(NSString* __nullable)bundleID;
|
||||
- (void) setPanPosition:(SInt32)pan
|
||||
forAppWithProcessID:(pid_t)processID
|
||||
bundleID:(NSString* __nullable)bundleID;
|
||||
|
||||
- (BOOL) isOutputDevice:(AudioObjectID)deviceID;
|
||||
- (BOOL) isOutputDataSource:(UInt32)dataSourceID;
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "CAAutoDisposer.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
@implementation BGMAudioDeviceManager {
|
||||
BGMBackgroundMusicDevice bgmDevice;
|
||||
BGMAudioDevice outputDevice;
|
||||
|
@ -52,7 +54,7 @@
|
|||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
- (instancetype) initWithError:(NSError**)error {
|
||||
- (instancetype) initWithError:(NSError** __nullable)error {
|
||||
if ((self = [super init])) {
|
||||
stateLock = [NSRecursiveLock new];
|
||||
bgmXPCHelperConnection = nil;
|
||||
|
@ -238,6 +240,18 @@
|
|||
return outputDevice;
|
||||
}
|
||||
|
||||
- (void) setVolume:(SInt32)volume
|
||||
forAppWithProcessID:(pid_t)processID
|
||||
bundleID:(NSString* __nullable)bundleID {
|
||||
bgmDevice.SetAppVolume(volume, processID, (__bridge_retained CFStringRef)bundleID);
|
||||
}
|
||||
|
||||
- (void) setPanPosition:(SInt32)pan
|
||||
forAppWithProcessID:(pid_t)processID
|
||||
bundleID:(NSString* __nullable)bundleID {
|
||||
bgmDevice.SetAppPanPosition(pan, processID, (__bridge_retained CFStringRef)bundleID);
|
||||
}
|
||||
|
||||
- (BOOL) isOutputDevice:(AudioObjectID)deviceID {
|
||||
@try {
|
||||
[stateLock lock];
|
||||
|
@ -503,3 +517,6 @@
|
|||
|
||||
@end
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ void BGMBackgroundMusicDevice::SetAppVolume(SInt32 inVolume,
|
|||
|
||||
void BGMBackgroundMusicDevice::SetAppPanPosition(SInt32 inPanPosition,
|
||||
pid_t inAppProcessID,
|
||||
CFStringRef inAppBundleID)
|
||||
CFStringRef __nullable inAppBundleID)
|
||||
{
|
||||
BGMAssert((kAppPanLeftRawValue <= inPanPosition) && (inPanPosition <= kAppPanRightRawValue),
|
||||
"BGMBackgroundMusicDevice::SetAppPanPosition: Pan position out of bounds");
|
||||
|
@ -167,7 +167,7 @@ void BGMBackgroundMusicDevice::SetAppPanPosition(SInt32 inPanPosition,
|
|||
void BGMBackgroundMusicDevice::SendAppVolumeOrPanToBGMDevice(SInt32 inNewValue,
|
||||
CFStringRef inVolumeTypeKey,
|
||||
pid_t inAppProcessID,
|
||||
CFStringRef inAppBundleID)
|
||||
CFStringRef __nullable inAppBundleID)
|
||||
{
|
||||
CACFArray appVolumeChanges(true);
|
||||
|
||||
|
|
|
@ -118,13 +118,13 @@ public:
|
|||
*/
|
||||
void SetAppPanPosition(SInt32 inPanPosition,
|
||||
pid_t inAppProcessID,
|
||||
CFStringRef inAppBundleID);
|
||||
CFStringRef __nullable inAppBundleID);
|
||||
|
||||
private:
|
||||
void SendAppVolumeOrPanToBGMDevice(SInt32 inNewValue,
|
||||
CFStringRef inVolumeTypeKey,
|
||||
pid_t inAppProcessID,
|
||||
CFStringRef inAppBundleID);
|
||||
CFStringRef __nullable inAppBundleID);
|
||||
|
||||
static std::vector<CACFString>
|
||||
ResponsibleBundleIDsOf(CACFString inParentBundleID);
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
#import "BGMScriptingBridge.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#import "BGMScriptingBridge.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
#import "BGMMusicPlayer.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
#import "BGMScriptingBridge.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
#import "BGMScriptingBridge.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
#import "BGMScriptingBridge.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#import "BGMScriptingBridge.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#import "BGMScriptingBridge.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
#import "BGM_Types.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
#import "BGMXPCHelperService.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
// System Includes
|
||||
|
|
|
@ -48,8 +48,6 @@
|
|||
#import "BGMXPCListenerDelegate.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
// System Includes
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
- (void) testStartOutputDeviceWithoutBGMAppConnected {
|
||||
dispatch_semaphore_t replySemaphore = dispatch_semaphore_create(0);
|
||||
|
||||
|
||||
// Unregister BGMXPCHelper's connection to BGMApp in case BGMApp didn't shutdown cleanly the last time it ran.
|
||||
[[connection remoteObjectProxy] unregisterAsBGMApp];
|
||||
|
||||
|
@ -68,8 +68,9 @@
|
|||
|
||||
dispatch_semaphore_signal(replySemaphore);
|
||||
} forUISoundsDevice:NO];
|
||||
|
||||
if (0 != dispatch_semaphore_wait(replySemaphore, dispatch_time(DISPATCH_TIME_NOW, kStartIOTimeoutNsec))) {
|
||||
|
||||
// Very long timeout to make it less likely to fail on Travis CI when there's high contention.
|
||||
if (0 != dispatch_semaphore_wait(replySemaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * 60 * NSEC_PER_SEC))) {
|
||||
XCTFail(@"Timed out waiting for BGMXPCHelper");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,9 +61,18 @@
|
|||
// CADebugger
|
||||
//=============================================================================
|
||||
|
||||
// BGM edit: Added extern "C" so CADebugger (and headers that include it) can be used in Obj-C.
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if TARGET_API_MAC_OSX
|
||||
extern bool CAIsDebuggerAttached(void);
|
||||
#endif
|
||||
extern void CADebuggerStop(void);
|
||||
extern void CADebuggerStop(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -68,7 +68,7 @@ void LogError(const char *fmt, ...)
|
|||
va_list args;
|
||||
va_start(args, fmt);
|
||||
// BGM edit: vprintf leaves args in an undefined state, which can cause a crash in
|
||||
// vsyslog. Also added CADebuggerStop(). Original code commented out below.
|
||||
// vsyslog. Also added __ASSERT_STOP. Original code commented out below.
|
||||
//#if DEBUG
|
||||
// vprintf(fmt, args);
|
||||
//#endif
|
||||
|
@ -83,7 +83,7 @@ void LogError(const char *fmt, ...)
|
|||
vsyslog(LOG_ERR, fmt, args);
|
||||
#endif
|
||||
#if DEBUG
|
||||
CADebuggerStop();
|
||||
__ASSERT_STOP;
|
||||
#endif
|
||||
// BGM edit end
|
||||
va_end(args);
|
||||
|
@ -94,7 +94,7 @@ void LogWarning(const char *fmt, ...)
|
|||
va_list args;
|
||||
va_start(args, fmt);
|
||||
// BGM edit: vprintf leaves args in an undefined state, which can cause a crash in
|
||||
// vsyslog. Also added CADebuggerStop(). Original code commented out below.
|
||||
// vsyslog. Also added __ASSERT_STOP. Original code commented out below.
|
||||
//#if DEBUG
|
||||
// vprintf(fmt, args);
|
||||
//#endif
|
||||
|
@ -109,7 +109,7 @@ void LogWarning(const char *fmt, ...)
|
|||
vsyslog(LOG_WARNING, fmt, args);
|
||||
#endif
|
||||
#if DEBUG
|
||||
//CADebuggerStop(); // TODO: Add a toggle for this to the project file (under "Preprocessor Macros"). Default to off.
|
||||
//__ASSERT_STOP; // TODO: Add a toggle for this to the project file (under "Preprocessor Macros"). Default to off.
|
||||
#endif
|
||||
// BGM edit end
|
||||
va_end(args);
|
||||
|
|
|
@ -131,7 +131,7 @@
|
|||
|
||||
#pragma mark C Utility Functions
|
||||
|
||||
dispatch_queue_t BGMGetDispatchQueue_PriorityUserInteractive();
|
||||
dispatch_queue_t BGMGetDispatchQueue_PriorityUserInteractive(void);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
|
|
Loading…
Reference in a new issue