Skip to content

Commit 7ea9087

Browse files
authored
Merge pull request #843 from TwelfthFace/feature/mute-button
Feature/mute button
2 parents bcaef2c + 5891f4a commit 7ea9087

File tree

4 files changed

+216
-70
lines changed

4 files changed

+216
-70
lines changed

BGMApp/BGMApp.xcodeproj/xcshareddata/xcschemes/Background Music.xcscheme

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,13 @@
129129
</EnvironmentVariables>
130130
<AdditionalOptions>
131131
<AdditionalOption
132-
key = "NSZombieEnabled"
133-
value = "YES"
132+
key = "MallocScribble"
133+
value = ""
134134
isEnabled = "YES">
135135
</AdditionalOption>
136136
<AdditionalOption
137-
key = "MallocScribble"
138-
value = ""
137+
key = "NSZombieEnabled"
138+
value = "YES"
139139
isEnabled = "YES">
140140
</AdditionalOption>
141141
</AdditionalOptions>

BGMApp/BGMApp/BGMAppVolumes.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
//
2020
// Copyright © 2016, 2017 Kyle Neideck
2121
// Copyright © 2021 Marcus Wu
22+
// Copyright © 2026 TwelfthFace
2223
//
2324

2425
// Local Includes
@@ -63,6 +64,13 @@
6364

6465
// Custom classes for the UI elements in the app volume menu items
6566

67+
@interface BGMAVM_VolumeMute: NSButton <BGMAppVolumeMenuItemSubview>
68+
69+
- (void) bgm_syncForVolume:(int)vol;
70+
71+
@end
72+
73+
6674
@interface BGMAVM_AppIcon : NSImageView <BGMAppVolumeMenuItemSubview>
6775
@end
6876

BGMApp/BGMApp/BGMAppVolumes.m

Lines changed: 124 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
// BGMAppVolumes.m
1818
// BGMApp
1919
//
20-
// Copyright © 2016-2020 Kyle Neideck
20+
// Copyright © 2016-2020, 2026 Kyle Neideck
2121
// Copyright © 2017 Andrew Tonner
2222
// Copyright © 2021 Marcus Wu
2323
// Copyright © 2022 Jon Egan
24+
// Copyright © 2026 TwelfthFace
2425
//
2526

2627
// Self Include
@@ -423,20 +424,132 @@ - (void) setUpWithApp:(NSRunningApplication*)app
423424

424425
@end
425426

427+
@implementation BGMAVM_VolumeMute {
428+
pid_t appProcessID;
429+
NSString* __nullable appBundleID;
430+
BGMAppVolumesController* controller;
431+
}
432+
433+
- (NSString*) lastNonZeroVolumeDefaultsKey {
434+
if (appBundleID.length > 0) {
435+
return [NSString stringWithFormat:@"BGMAVM_LastNonZeroVolume_%@", appBundleID];
436+
}
437+
return [NSString stringWithFormat:@"BGMAVM_LastNonZeroVolume_pid_%d", appProcessID];
438+
}
439+
440+
- (BOOL) isMuted:(int)value {
441+
return value <= kAppRelativeVolumeMinRawValue;
442+
}
443+
444+
- (int) defaultRestoreVolume {
445+
return (int)((kAppRelativeVolumeMaxRawValue + kAppRelativeVolumeMinRawValue) / 2);
446+
}
447+
448+
- (BGMAVM_VolumeSlider* __nullable) findSiblingVolumeSlider {
449+
for (NSView* view in self.superview.subviews) {
450+
if ([view isKindOfClass:[BGMAVM_VolumeSlider class]]) {
451+
return (BGMAVM_VolumeSlider*)view;
452+
}
453+
}
454+
return nil;
455+
}
456+
457+
- (void) updateButtonForVolume:(int)volume {
458+
BOOL muted = [self isMuted:volume];
459+
460+
if ([NSImage respondsToSelector:@selector(imageWithSystemSymbolName:accessibilityDescription:)]) {
461+
#pragma clang diagnostic push
462+
#pragma clang diagnostic ignored "-Wpartial-availability"
463+
NSString* symbol = muted ? @"speaker.slash.fill" : @"speaker.wave.2.fill";
464+
NSString* description = muted ? @"Unmute" : @"Mute";
465+
self.image = [NSImage imageWithSystemSymbolName:symbol accessibilityDescription:description];
466+
#pragma clang diagnostic pop
467+
self.imagePosition = NSImageOnly;
468+
self.title = @"";
469+
} else {
470+
self.title = muted ? @"Unmute" : @"Mute";
471+
}
472+
}
473+
474+
- (void) bgm_syncForVolume:(int)volume {
475+
[self updateButtonForVolume:volume];
476+
}
477+
478+
- (void) setUpWithApp:(NSRunningApplication*)app
479+
context:(BGMAppVolumes*)ctx
480+
controller:(BGMAppVolumesController*)ctrl
481+
menuItem:(NSMenuItem*)menuItem {
482+
#pragma unused (ctx, menuItem)
483+
484+
controller = ctrl;
485+
appProcessID = app.processIdentifier;
486+
appBundleID = app.bundleIdentifier;
487+
488+
self.target = self;
489+
self.action = @selector(mutePressed:);
490+
491+
BGMAVM_VolumeSlider* slider = [self findSiblingVolumeSlider];
492+
int currentVol = slider ? slider.intValue : kAppRelativeVolumeMinRawValue;
493+
[self updateButtonForVolume:currentVol];
494+
}
495+
496+
- (IBAction) mutePressed:(id)sender {
497+
#pragma unused(sender)
498+
499+
BGMAVM_VolumeSlider* slider = [self findSiblingVolumeSlider];
500+
if (!slider) {
501+
DebugMsg("Mute button: no slider found");
502+
return;
503+
}
504+
505+
int currentVol = slider.intValue;
506+
BOOL mutedNow = [self isMuted:currentVol];
507+
508+
if (!mutedNow) {
509+
// Store last volume
510+
[[NSUserDefaults standardUserDefaults] setInteger:currentVol
511+
forKey:[self lastNonZeroVolumeDefaultsKey]];
512+
513+
[slider setRelativeVolume:kAppRelativeVolumeMinRawValue];
514+
} else {
515+
NSInteger last = [[NSUserDefaults standardUserDefaults] integerForKey:[self lastNonZeroVolumeDefaultsKey]];
516+
int restoreVol = (int)last;
517+
518+
if (restoreVol <= kAppRelativeVolumeMinRawValue ||
519+
restoreVol > kAppRelativeVolumeMaxRawValue) {
520+
restoreVol = [self defaultRestoreVolume];
521+
}
522+
523+
[slider setRelativeVolume:restoreVol];
524+
}
525+
526+
[controller setVolume:slider.intValue
527+
forAppWithProcessID:appProcessID
528+
bundleID:appBundleID];
529+
530+
[self updateButtonForVolume:slider.intValue];
531+
}
532+
533+
@end
534+
426535
@implementation BGMAVM_VolumeSlider {
427536
// Will be set to -1 for apps without a pid
428537
pid_t appProcessID;
429538
NSString* __nullable appBundleID;
430539
BGMAppVolumesController* controller;
540+
541+
// Keep the menu item so we can sync the mute button when the slider changes.
542+
__weak NSMenuItem* menuItem;
431543
}
432544

433545
- (void) setUpWithApp:(NSRunningApplication*)app
434546
context:(BGMAppVolumes*)ctx
435547
controller:(BGMAppVolumesController*)ctrl
436-
menuItem:(NSMenuItem*)menuItem {
437-
#pragma unused (ctx, menuItem)
548+
menuItem:(NSMenuItem*)inMenuItem {
549+
#pragma unused (ctx)
438550

439551
controller = ctrl;
552+
menuItem = inMenuItem;
440553

441554
self.target = self;
442555
self.action = @selector(appVolumeChanged);
@@ -483,6 +596,13 @@ - (void) appVolumeChanged {
483596
// The values from our sliders are in
484597
// [kAppRelativeVolumeMinRawValue, kAppRelativeVolumeMaxRawValue] already.
485598
[controller setVolume:self.intValue forAppWithProcessID:appProcessID bundleID:appBundleID];
599+
600+
// Sync the mute button so it reflects muted/unmuted when the user drags the slider.
601+
for (NSView* subview in menuItem.view.subviews) {
602+
if ([subview isKindOfClass:[BGMAVM_VolumeMute class]]) {
603+
[(BGMAVM_VolumeMute*)subview bgm_syncForVolume:self.intValue];
604+
}
605+
}
486606
}
487607

488608
@end
@@ -533,3 +653,4 @@ - (void) appPanPositionChanged {
533653
}
534654

535655
@end
656+

0 commit comments

Comments
 (0)