diff --git a/PowerMate/PowerMate.csproj b/PowerMate/PowerMate.csproj
index f9681e6..a47b2cf 100644
--- a/PowerMate/PowerMate.csproj
+++ b/PowerMate/PowerMate.csproj
@@ -13,7 +13,7 @@
true
git
Apache-2.0
- griffin powermate hid rotary-encoder
+ griffin powermate hid rotary-encoder usb knob dial
icon.jpg
Readme.md
diff --git a/PowerMate/PowerMateClient.cs b/PowerMate/PowerMateClient.cs
index fa35c59..87dde5d 100644
--- a/PowerMate/PowerMateClient.cs
+++ b/PowerMate/PowerMateClient.cs
@@ -107,16 +107,22 @@ public int LightPulseSpeed {
// ExceptionAdjustment: M:System.Array.Copy(System.Array,System.Int32,System.Array,System.Int32,System.Int32) -T:System.RankException
// ExceptionAdjustment: M:System.Array.Copy(System.Array,System.Int32,System.Array,System.Int32,System.Int32) -T:System.ArrayTypeMismatchException
private void SetFeature(PowerMateFeature feature, params byte[] payload) {
- _mostRecentFeatureSetTime = DateTime.Now;
byte[] featureData = { 0x00, 0x41, 0x01, (byte) feature, 0x00, /* payload copied here */ 0x00, 0x00, 0x00, 0x00 };
Array.Copy(payload, 0, featureData, 5, Math.Min(payload.Length, 4));
try {
- DeviceStream?.SetFeature(featureData);
+ SetFeatureAndTime();
} catch (IOException e) {
if (e.InnerException is Win32Exception { NativeErrorCode: 0 }) {
// retry once with no delay if we get a "The operation completed successfully" error
- DeviceStream?.SetFeature(featureData);
+ SetFeatureAndTime();
+ }
+ }
+
+ void SetFeatureAndTime() {
+ if (DeviceStream != null) {
+ DeviceStream.SetFeature(featureData);
+ _mostRecentFeatureSetTime = DateTime.Now;
}
}
}
diff --git a/PowerMateVolume/PowerMateVolume.cs b/PowerMateVolume/PowerMateVolume.cs
index 3ffc49c..95e3fed 100644
--- a/PowerMateVolume/PowerMateVolume.cs
+++ b/PowerMateVolume/PowerMateVolume.cs
@@ -12,12 +12,6 @@
powerMate.LightBrightness = 0;
-CancellationTokenSource cancellationTokenSource = new();
-Console.CancelKeyPress += (_, eventArgs) => {
- eventArgs.Cancel = true;
- cancellationTokenSource.Cancel();
-};
-
powerMate.InputReceived += (_, powerMateEvent) => {
switch (powerMateEvent) {
case { IsPressed: true, RotationDirection: RotationDirection.None }:
@@ -37,7 +31,24 @@
using IStandbyListener standbyListener = new EventLogStandbyListener();
standbyListener.FatalError += (_, exception) =>
MessageBox.Show("Event log subscription is broken, continuing without resume detection: " + exception, "PowerMateVolume", MessageBoxButtons.OK, MessageBoxIcon.Error);
-standbyListener.Resumed += (_, _) => powerMate.SetAllFeaturesIfStale();
+standbyListener.Resumed += (_, _) => {
+ try {
+ powerMate.SetAllFeaturesIfStale();
+ } catch (IOException) {
+ Thread.Sleep(2000);
+ try {
+ powerMate.SetAllFeaturesIfStale();
+ } catch (IOException) {
+ // device is probably in a bad state, but there's nothing we can do about it
+ }
+ }
+};
+
+CancellationTokenSource exitTokenSource = new();
+Console.CancelKeyPress += (_, eventArgs) => {
+ eventArgs.Cancel = true;
+ exitTokenSource.Cancel();
+};
Console.WriteLine("Listening for PowerMate events");
-cancellationTokenSource.Token.WaitHandle.WaitOne();
\ No newline at end of file
+exitTokenSource.Token.WaitHandle.WaitOne();
\ No newline at end of file
diff --git a/PowerMateVolume/PowerMateVolume.csproj b/PowerMateVolume/PowerMateVolume.csproj
index e1f29b6..ba9213b 100644
--- a/PowerMateVolume/PowerMateVolume.csproj
+++ b/PowerMateVolume/PowerMateVolume.csproj
@@ -12,7 +12,7 @@
Ben Hutchison
© 2023 $(Authors)
PowerMate Volume
- 1.0.2
+ 1.0.3
$(AssemblyTitle)
$(Version)
app.manifest
diff --git a/PowerMateVolume/StandbyEventEmitter.cs b/PowerMateVolume/StandbyEventEmitter.cs
index 735b7f9..1676fd9 100644
--- a/PowerMateVolume/StandbyEventEmitter.cs
+++ b/PowerMateVolume/StandbyEventEmitter.cs
@@ -12,6 +12,9 @@ public interface IStandbyListener: IDisposable {
public class EventLogStandbyListener: IStandbyListener {
+ private const int StandByEventId = 42;
+ private const int ResumeEventId = 107;
+
public event EventHandler? StandingBy;
public event EventHandler? Resumed;
public event EventHandler? FatalError;
@@ -21,7 +24,8 @@ public class EventLogStandbyListener: IStandbyListener {
/// if the given event log or file was not found
/// if the log did not already exist and this program is not running elevated
public EventLogStandbyListener() {
- _logWatcher = new EventLogWatcher(new EventLogQuery("System", PathType.LogName, "*[System[Provider/@Name=\"Microsoft-Windows-Kernel-Power\" and (EventID=42 or EventID=107)]]"));
+ _logWatcher = new EventLogWatcher(new EventLogQuery("System", PathType.LogName,
+ $"*[System[Provider/@Name=\"Microsoft-Windows-Kernel-Power\" and (EventID={StandByEventId} or EventID={ResumeEventId})]]"));
_logWatcher.EventRecordWritten += onEventRecord;
@@ -43,10 +47,10 @@ private void onEventRecord(object? sender, EventRecordWrittenEventArgs e) {
} else {
using EventRecord? record = e.EventRecord;
switch (record?.Id) {
- case 42:
+ case StandByEventId:
StandingBy?.Invoke(this, EventArgs.Empty);
break;
- case 107:
+ case ResumeEventId:
Resumed?.Invoke(this, EventArgs.Empty);
break;
}
diff --git a/PowerMateVolume/VolumeChanger.cs b/PowerMateVolume/VolumeChanger.cs
index 13be51a..3379e8c 100644
--- a/PowerMateVolume/VolumeChanger.cs
+++ b/PowerMateVolume/VolumeChanger.cs
@@ -21,11 +21,23 @@ public interface IVolumeChanger: IDisposable {
///
void IncreaseVolume(int increments = 1);
+ ///
+ /// Get or set the default audio output device's volume.
+ ///
+ /// The absolute volume level, in the range [0, 1]. Will be clipped if it's set to a value outside that range.
+ float Volume { get; set; }
+
///
/// If the default audio output device is not currently muted, then mute it. Otherwise, unmute it.
///
void ToggleMute();
+ ///
+ /// Mute or unmute the default audio output device, or get whether or not it's currently muted.
+ ///
+ /// if the device is or should be muted, or if it is or should be unmuted.
+ bool Muted { get; set; }
+
}
public class VolumeChanger: IVolumeChanger {
@@ -74,17 +86,39 @@ private void DetachFromCurrentDevice() {
_audioOutputEndpoint = null;
}
+ ///
public void IncreaseVolume(int increments = 1) {
if (_audioOutputVolume is not null && increments != 0) {
- float newVolume = Math.Max(0, Math.Min(1, _audioOutputVolume.MasterVolumeLevelScalar + VolumeIncrement * increments));
- _audioOutputVolume.MasterVolumeLevelScalar = newVolume;
+ Volume = _audioOutputVolume.MasterVolumeLevelScalar + VolumeIncrement * increments;
// Console.WriteLine($"Set volume to {newVolume:P2}");
}
}
+ ///
+ public float Volume {
+ get => _audioOutputVolume?.MasterVolumeLevelScalar ?? 0;
+ set {
+ float newVolume = Math.Max(0, Math.Min(1, value));
+ if (_audioOutputVolume != null) {
+ _audioOutputVolume.MasterVolumeLevelScalar = newVolume;
+ }
+ }
+ }
+
+ ///
public void ToggleMute() {
if (_audioOutputVolume is not null) {
- _audioOutputVolume.IsMuted ^= true;
+ Muted = !Muted;
+ }
+ }
+
+ ///
+ public bool Muted {
+ get => _audioOutputVolume?.IsMuted ?? true;
+ set {
+ if (_audioOutputVolume is not null) {
+ _audioOutputVolume.IsMuted = value;
+ }
}
}