-
Notifications
You must be signed in to change notification settings - Fork 10
Enhances OpenRGB integration with zone support #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| { | ||
| "version": "0.2.0", | ||
| "configurations": [ | ||
| { | ||
| "name": ".NET Core Launch (YALCY)", | ||
| "type": "coreclr", | ||
| "request": "launch", | ||
| "preLaunchTask": "build", | ||
| "program": "${workspaceFolder}/YALCY/bin/Debug/net9.0/YALCY.dll", | ||
| "args": [], | ||
| "cwd": "${workspaceFolder}/YALCY", | ||
| "console": "internalConsole", | ||
| "stopAtEntry": false | ||
| }, | ||
| { | ||
| "name": ".NET Core Launch (YALCY.CLI)", | ||
| "type": "coreclr", | ||
| "request": "launch", | ||
| "preLaunchTask": "build-cli", | ||
| "program": "${workspaceFolder}/YALCY.CLI/bin/Debug/net9.0/YALCY.CLI.dll", | ||
| "args": [], | ||
| "cwd": "${workspaceFolder}/YALCY.CLI", | ||
| "console": "internalConsole", | ||
| "stopAtEntry": false | ||
| }, | ||
| { | ||
| "name": ".NET Core Attach", | ||
| "type": "coreclr", | ||
| "request": "attach" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| { | ||
| "version": "2.0.0", | ||
| "tasks": [ | ||
| { | ||
| "label": "build", | ||
| "command": "dotnet", | ||
| "type": "process", | ||
| "args": [ | ||
| "build", | ||
| "${workspaceFolder}/YALCY/YALCY.csproj", | ||
| "/property:GenerateFullPaths=true", | ||
| "/consoleloggerparameters:NoSummary;ForceNoAlign" | ||
| ], | ||
| "problemMatcher": "$msCompile", | ||
| "group": { | ||
| "kind": "build", | ||
| "isDefault": true | ||
| } | ||
| }, | ||
| { | ||
| "label": "build-cli", | ||
| "command": "dotnet", | ||
| "type": "process", | ||
| "args": [ | ||
| "build", | ||
| "${workspaceFolder}/YALCY.CLI/YALCY.CLI.csproj", | ||
| "/property:GenerateFullPaths=true", | ||
| "/consoleloggerparameters:NoSummary;ForceNoAlign" | ||
| ], | ||
| "problemMatcher": "$msCompile" | ||
| }, | ||
| { | ||
| "label": "publish", | ||
| "command": "dotnet", | ||
| "type": "process", | ||
| "args": [ | ||
| "publish", | ||
| "${workspaceFolder}/YALCY/YALCY.csproj", | ||
| "/property:GenerateFullPaths=true", | ||
| "/consoleloggerparameters:NoSummary;ForceNoAlign" | ||
| ], | ||
| "problemMatcher": "$msCompile" | ||
| }, | ||
| { | ||
| "label": "watch", | ||
| "command": "dotnet", | ||
| "type": "process", | ||
| "args": [ | ||
| "watch", | ||
| "run", | ||
| "--project", | ||
| "${workspaceFolder}/YALCY/YALCY.csproj" | ||
| ], | ||
| "problemMatcher": "$msCompile" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,16 +14,32 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace YALCY.Integrations.OpenRGB; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class ZoneInfo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Device Device { get; set; } = null!; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public int ZoneIndex { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Zone Zone { get; set; } = null!; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class OpenRgbTalker | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private CancellationTokenSource cts = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private Task updateTask = Task.CompletedTask; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Legacy device-based lists (kept for backward compatibility) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public List<Device> OffList = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public List<Device> LightPodList = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public List<Device> StrobeList = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public List<Device> FoggerList = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Dictionary<int, Color[]> LightPodStates = new Dictionary<int, Color[]>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // New zone-based dictionaries | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Dictionary<string, ZoneInfo> OffZones = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Dictionary<string, ZoneInfo> LightPodZones = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Dictionary<string, ZoneInfo> StrobeZones = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Dictionary<string, ZoneInfo> FoggerZones = new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Dictionary<string, Color[]> LightPodZoneStates = new Dictionary<string, Color[]>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static string name = "YALCY"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static bool autoConnect = false; // can't catch exceptions in constructor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static int timeoutMs = 1000; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -62,6 +78,26 @@ public async Task ConnectToOpenRgbServerAsync(string serverIp, ushort serverPort | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Clear all device lists to prevent duplicates on reconnection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OffList.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LightPodList.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| StrobeList.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FoggerList.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LightPodStates.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OffZones.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LightPodZones.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| StrobeZones.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FoggerZones.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LightPodZoneStates.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Clear visual lists in the UI synchronously | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await Dispatcher.UIThread.InvokeAsync(() => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MainWindowViewModel.ClearOpenRgbVisualList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainViewModel.DeviceCategories.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainViewModel.DevicesWithZones.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client = new OpenRgbClient(serverIp, serverPort, name, autoConnect, timeoutMs, protocolVersionNumber); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This really should be awaited since it waits for timeoutMs, however it isn't written that way. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -88,6 +124,7 @@ public async Task ConnectToOpenRgbServerAsync(string serverIp, ushort serverPort | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OffList.Add(device); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OpenRgbDeviceInserted?.Invoke(device); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainViewModel.DeviceCategories.Add(new DeviceCategory(device, 0, mainViewModel)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainViewModel.DevicesWithZones.Add(new DeviceWithZones(device, mainViewModel)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UsbDeviceMonitor.OnStageKitCommand += OnStageKitEvent; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -144,10 +181,22 @@ private void OnDeviceLisUpdate(object o, EventArgs e) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var devices = client.GetAllControllerData(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OffList.Clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Dispatcher.UIThread.InvokeAsync(MainWindowViewModel.ClearOpenRgbVisualList); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (_mainViewModel != null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Dispatcher.UIThread.InvokeAsync(() => _mainViewModel.ClearDevicesWithZones()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var dev in devices) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OffList.Add(dev); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OpenRgbDeviceInserted?.Invoke(dev); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (_mainViewModel != null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Dispatcher.UIThread.InvokeAsync(() => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _mainViewModel.DevicesWithZones.Add(new DeviceWithZones(dev, _mainViewModel))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+185
to
+199
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (_mainViewModel != null) | |
| { | |
| Dispatcher.UIThread.InvokeAsync(() => _mainViewModel.ClearDevicesWithZones()); | |
| } | |
| foreach (var dev in devices) | |
| { | |
| OffList.Add(dev); | |
| OpenRgbDeviceInserted?.Invoke(dev); | |
| if (_mainViewModel != null) | |
| { | |
| Dispatcher.UIThread.InvokeAsync(() => | |
| _mainViewModel.DevicesWithZones.Add(new DeviceWithZones(dev, _mainViewModel))); | |
| } | |
| Dispatcher.UIThread.InvokeAsync(() => | |
| { | |
| if (_mainViewModel != null) | |
| { | |
| _mainViewModel.ClearDevicesWithZones(); | |
| } | |
| }); | |
| foreach (var dev in devices) | |
| { | |
| OffList.Add(dev); | |
| OpenRgbDeviceInserted?.Invoke(dev); | |
| Dispatcher.UIThread.InvokeAsync(() => | |
| { | |
| var vm = _mainViewModel; | |
| if (vm != null) | |
| { | |
| vm.DevicesWithZones.Add(new DeviceWithZones(dev, vm)); | |
| } | |
| }); |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When clearing OffList and updating the device list, only DevicesWithZones is being repopulated (line 197-198) but DeviceCategories is not. This creates an inconsistency - on initial connection (lines 122-127), both collections are populated, but on device list update, only DevicesWithZones is repopulated. This means the old DataGrid-based UI using DeviceCategories won't update when devices are hot-plugged. Either remove the line that adds to DeviceCategories during initial connection (since it appears deprecated) or also repopulate it here for consistency.
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At line 431, LightPodZoneStates[zoneKey] is accessed without checking if the key exists. If a zone is added to LightPodZones without properly initializing its state in LightPodZoneStates, this will throw a KeyNotFoundException. This is related to the initialization issue in DeviceZoneCategory constructor where zones are not added to any category list initially. Consider adding a check: if (!LightPodZoneStates.ContainsKey(zoneKey)) continue; or ensure proper initialization in the DeviceZoneCategory constructor.
| var keysPerArea = Math.Max(1, zoneInfo.Zone.LedCount / numAreas); | |
| var colors = LightPodZoneStates[zoneKey]; | |
| var keysPerArea = Math.Max(1, zoneInfo.Zone.LedCount / numAreas); | |
| if (!LightPodZoneStates.TryGetValue(zoneKey, out var colors)) | |
| { | |
| // Zone state not initialized; skip to avoid KeyNotFoundException | |
| continue; | |
| } |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,6 @@ public static AppBuilder BuildAvaloniaApp() | |||||
| => AppBuilder.Configure<App>() | ||||||
| .UsePlatformDetect() | ||||||
| .WithInterFont() | ||||||
| .LogToTrace() | ||||||
| .LogToTrace() | ||||||
|
||||||
| .LogToTrace() | |
| .LogToTrace() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The clearing of zone-based dictionaries (lines 87-91) is not thread-safe, while the legacy
LightPodStatesdictionary is protected with a lock in other parts of the code (e.g., lines 349, 380, 458). If multiple threads access these dictionaries simultaneously (e.g., during reconnection while effects are running), this could lead to race conditions. Consider adding lock statements around dictionary operations forLightPodZoneStatessimilar to howLightPodStatesis protected, or ensure that reconnection properly cancels all ongoing tasks before clearing.