diff --git a/.editorconfig b/.editorconfig index 5b6acadf..143210d2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -31,6 +31,10 @@ csharp_new_line_before_finally = true # The codebase uses block-scoped namespaces; keep new files consistent. csharp_style_namespace_declarations = block_scoped:suggestion dotnet_sort_system_directives_first = true +# Namespace must equal project root + relative folder path (issue #102). +# Deliberate exceptions disable IDE0130 in-file with a one-line reason. +dotnet_style_namespace_match_folder = true +dotnet_diagnostic.IDE0130.severity = error # ── this. / var ────────────────────────────────────────────────────── dotnet_style_qualification_for_field = false:suggestion diff --git a/Directory.Build.props b/Directory.Build.props index 76e59ecb..8c78a6bb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,6 +7,10 @@ IsExternalInit shim). --> latest + + true + 0.7.0 $(VersionPrefix) diff --git a/docs/README.md b/docs/README.md index e56b96e8..83e9ac23 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,7 @@ Documentation specific to the FanaBridge plugin: | Document | Description | |----------|-------------| +| [Architecture](architecture.md) | Layers, namespace rule, test-tree convention, and frozen names | | [Supported Devices](supported-devices.md) | Wheels and hub + module combos with built-in FanaBridge profiles — tested vs. unverified, and their LED/display capabilities | | [Device Settings Lifecycle](device-settings-lifecycle.md) | How a device decides what to store, why the LED editor is built up front, and the rules that keep a save from erasing settings | diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..0a0724ce --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,66 @@ +# Architecture + +Rules and frozen names for the FanaBridge solution. Folder inventories and history live elsewhere; this page is the contract. + +## Layers + +Three source projects, layered by dependency: + +| Project | Role | +|---------|------| +| **FanaBridge.Core** | SimHub-free device stack: HID transport, Fanatec protocol, profiles, LEDs, display encoding, tuning, diagnostics. References only non-SimHub libraries (HidSharp, Newtonsoft.Json). | +| **FanaBridge** (plugin) | SimHub / WPF shell: plugin entrypoint, device registry, Control Mapper bridge, settings, UI, display drivers. References Core and Updater. | +| **FanaBridge.Updater** | Isolated self-updater (release feed, download, file swap). References neither Core nor the plugin — audit boundary for code that rewrites files next to SimHub. | + +**Reference rules:** Core and Updater must not reference SimHub assemblies or each other. The plugin may reference both. Packaging uses **ILRepack** to merge Core + Updater into the shipped **`FanaBridge.dll`** (development builds keep separate assemblies for layering). + +## Namespace rule + +``` +namespace = project namespace root + relative folder path +``` + +| Project | Namespace root | +|---------|----------------| +| `src/FanaBridge.Core` | `FanaBridge.Core` | +| `src/FanaBridge` | `FanaBridge` | +| `src/FanaBridge.Updater` | `FanaBridge.Updater` | +| `tests/FanaBridge.Tests` | `FanaBridge.Tests` | + +Enforced at compile time via **IDE0130** (`dotnet_style_namespace_match_folder = true`, severity error, `EnforceCodeStyleInBuild`). Deliberate exceptions use an in-file `#pragma warning disable IDE0130` with a one-line reason. + +**Product exceptions:** + +1. **`Log` in namespace `FanaBridge`** (`FanaBridge.Core/Logging/Log.cs`) — unqualified `Log.*` call sites in both Core and plugin resolve only when the type lives on the shared root ancestor. +2. **`ModuleInitializerAttribute`** (`FanaBridge/Properties/ModuleInitializerAttribute.cs`) — net48 polyfill; must live in `System.Runtime.CompilerServices` by definition. + +XAML `x:Class` values follow the same path rule and are checked by a contract test (the C# analyzer does not cover markup). + +Domain folders and their matching namespaces are a **navigational taxonomy**, not directional boundaries. Cross-domain references within a project are expected (e.g. Devices ↔ Transport in Core). The only compiler-enforced directional boundaries are the assembly references: Core references nothing internal, Updater references nothing internal, and the plugin references both. + +## Test tree + +`tests/FanaBridge.Tests` mirrors the product projects: + +- `Core/` → `FanaBridge.Tests.Core…` +- `Plugin/` → `FanaBridge.Tests.Plugin…` +- `Updater/` → `FanaBridge.Tests.Updater…` +- `Contracts/` → repo-wide / external-contract tests (XAML layout, SimHub enum snapshot) + +Domain-local fakes live next to their domain; multi-domain doubles live under `TestDoubles/`. A third layout exception lives in the test tree: the Control Mapper reflection shim (see `tests/FanaBridge.Tests/README.md` for conventions and details). + +## Frozen names + +These strings are invisible to the C# namespace analyzer but **breaking to rename** (SimHub persistence, embedded resources, dashboards, or the updater whitelist): + +| Name | Why frozen | +|------|------------| +| **`FanaBridge.dll`** | Shipped assembly file name; updater package whitelist and install path. | +| **`FanaBridge.FanatecPlugin`** | Fully-qualified plugin type name persisted by SimHub. | +| **`Profiles` path segment** (under Core's embedded resources) | The built-in wheel-profile loader matches manifest names on a `.Profiles.` substring and `.json` suffix; keep profile JSON under a `Profiles/` folder. | +| **`FanatecPlugin.FanaBridgeSettings.json`** | On-disk settings file under SimHub `PluginsData/Common/`. | +| **`AttachDelegate` / `AddEvent` keys** | Property and event names registered with SimHub (`FanaBridge.*` properties, `DeviceConnected`, `DeviceDisconnected`, `WheelChanged`, …). Dashboards and automations bind to these strings. | +| **`Fanatec_[_]`** | `DeviceTypeID` format for SimHub device descriptors (and `Fanatec_Module_` parents for hub logos). | +| **`FS_WHEEL_SWTYPE_`** | Control Mapper variant ids (`FanaBridgeVariantProvider`); persisted in users' Control Mapper settings. | + +Not frozen, for the record: SimHub's `ResolveCache.json` stores plugin/registry FQNs, but the cache is hash-invalidated whenever `FanaBridge.dll` changes on disk, so those entries rebuild on every update — cache state, not durable state. diff --git a/src/FanaBridge.Core/Transport/ConnectionMonitor.cs b/src/FanaBridge.Core/Devices/ConnectionMonitor.cs similarity index 99% rename from src/FanaBridge.Core/Transport/ConnectionMonitor.cs rename to src/FanaBridge.Core/Devices/ConnectionMonitor.cs index d28843d0..1e03c472 100644 --- a/src/FanaBridge.Core/Transport/ConnectionMonitor.cs +++ b/src/FanaBridge.Core/Devices/ConnectionMonitor.cs @@ -1,6 +1,6 @@ using System; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Devices { /// /// Encapsulates the Fanatec device connection state machine: diff --git a/src/FanaBridge.Core/Transport/FanatecWheelbase.cs b/src/FanaBridge.Core/Devices/FanatecWheelbase.cs similarity index 99% rename from src/FanaBridge.Core/Transport/FanatecWheelbase.cs rename to src/FanaBridge.Core/Devices/FanatecWheelbase.cs index 5c2a4b4e..1b45ab93 100644 --- a/src/FanaBridge.Core/Transport/FanatecWheelbase.cs +++ b/src/FanaBridge.Core/Devices/FanatecWheelbase.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using FanaBridge.Profiles; -using FanaBridge.Protocol; +using FanaBridge.Core.Devices.Identity; +using FanaBridge.Core.Devices.Profiles; +using FanaBridge.Core.Transport; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Devices { /// /// Represents a connected Fanatec wheelbase — the root of all communication. diff --git a/src/FanaBridge.Core/Transport/IWheelbaseConnection.cs b/src/FanaBridge.Core/Devices/IWheelbaseConnection.cs similarity index 96% rename from src/FanaBridge.Core/Transport/IWheelbaseConnection.cs rename to src/FanaBridge.Core/Devices/IWheelbaseConnection.cs index b231c27a..03c5051d 100644 --- a/src/FanaBridge.Core/Transport/IWheelbaseConnection.cs +++ b/src/FanaBridge.Core/Devices/IWheelbaseConnection.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Transport +namespace FanaBridge.Core.Devices { /// /// Connection-check surface of a , used by diff --git a/src/FanaBridge.Core/Protocol/FanatecDeviceTables.cs b/src/FanaBridge.Core/Devices/Identity/FanatecDeviceTables.cs similarity index 99% rename from src/FanaBridge.Core/Protocol/FanatecDeviceTables.cs rename to src/FanaBridge.Core/Devices/Identity/FanatecDeviceTables.cs index eba62efe..cbd0aa88 100644 --- a/src/FanaBridge.Core/Protocol/FanatecDeviceTables.cs +++ b/src/FanaBridge.Core/Devices/Identity/FanatecDeviceTables.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Devices.Identity { /// /// Reference tables mapping the raw FF 08 system-report bytes to FanaBridge diff --git a/src/FanaBridge.Core/Protocol/FanatecIdentity.cs b/src/FanaBridge.Core/Devices/Identity/FanatecIdentity.cs similarity index 98% rename from src/FanaBridge.Core/Protocol/FanatecIdentity.cs rename to src/FanaBridge.Core/Devices/Identity/FanatecIdentity.cs index 5d035e2b..6f0c797b 100644 --- a/src/FanaBridge.Core/Protocol/FanatecIdentity.cs +++ b/src/FanaBridge.Core/Devices/Identity/FanatecIdentity.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Devices.Identity { /// /// Decodes wheelbase + attachment + module identity from the col03 FF 08 diff --git a/src/FanaBridge.Core/Transport/IdentitySettler.cs b/src/FanaBridge.Core/Devices/Identity/IdentitySettler.cs similarity index 98% rename from src/FanaBridge.Core/Transport/IdentitySettler.cs rename to src/FanaBridge.Core/Devices/Identity/IdentitySettler.cs index 509f29b9..5ee283a4 100644 --- a/src/FanaBridge.Core/Transport/IdentitySettler.cs +++ b/src/FanaBridge.Core/Devices/Identity/IdentitySettler.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Transport +namespace FanaBridge.Core.Devices.Identity { /// /// Settles raw wheel/hub + module identity readings before they are committed. diff --git a/src/FanaBridge.Core/Protocol/SrmConverterIdentity.cs b/src/FanaBridge.Core/Devices/Identity/SrmConverterIdentity.cs similarity index 98% rename from src/FanaBridge.Core/Protocol/SrmConverterIdentity.cs rename to src/FanaBridge.Core/Devices/Identity/SrmConverterIdentity.cs index 5faacd6f..c0103a82 100644 --- a/src/FanaBridge.Core/Protocol/SrmConverterIdentity.cs +++ b/src/FanaBridge.Core/Devices/Identity/SrmConverterIdentity.cs @@ -1,7 +1,7 @@ using System; -using FanaBridge.Transport; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Devices.Identity { /// /// SRM Conversion Kit identity, recovered from the kit's private DE FA AD0xDD diff --git a/src/FanaBridge.Core/Protocol/SystemReportReader.cs b/src/FanaBridge.Core/Devices/Identity/SystemReportReader.cs similarity index 98% rename from src/FanaBridge.Core/Protocol/SystemReportReader.cs rename to src/FanaBridge.Core/Devices/Identity/SystemReportReader.cs index fe52d08f..cb230916 100644 --- a/src/FanaBridge.Core/Protocol/SystemReportReader.cs +++ b/src/FanaBridge.Core/Devices/Identity/SystemReportReader.cs @@ -1,7 +1,7 @@ using System; -using FanaBridge.Transport; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Devices.Identity { /// /// The col03 FF 08 system-report codec — the wire-level side of identity. diff --git a/src/FanaBridge.Core/Protocol/WheelEngage.cs b/src/FanaBridge.Core/Devices/Identity/WheelEngage.cs similarity index 97% rename from src/FanaBridge.Core/Protocol/WheelEngage.cs rename to src/FanaBridge.Core/Devices/Identity/WheelEngage.cs index 6c4486a6..59788f49 100644 --- a/src/FanaBridge.Core/Protocol/WheelEngage.cs +++ b/src/FanaBridge.Core/Devices/Identity/WheelEngage.cs @@ -1,6 +1,6 @@ -using FanaBridge.Transport; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Devices.Identity { /// /// Replicates the kernel filter's engage sequence (FWFUProtocolUsbBulkOrInterruptTransfer) diff --git a/src/FanaBridge.Core/Profiles/ColorFormat.cs b/src/FanaBridge.Core/Devices/Profiles/ColorFormat.cs similarity index 92% rename from src/FanaBridge.Core/Profiles/ColorFormat.cs rename to src/FanaBridge.Core/Devices/Profiles/ColorFormat.cs index c36d4d89..86e5aa51 100644 --- a/src/FanaBridge.Core/Profiles/ColorFormat.cs +++ b/src/FanaBridge.Core/Devices/Profiles/ColorFormat.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Pixel encoding for the Color LED channel (subcmd 0x02). diff --git a/src/FanaBridge.Core/Profiles/DeviceConfig.cs b/src/FanaBridge.Core/Devices/Profiles/DeviceConfig.cs similarity index 98% rename from src/FanaBridge.Core/Profiles/DeviceConfig.cs rename to src/FanaBridge.Core/Devices/Profiles/DeviceConfig.cs index eb35ce18..9523b408 100644 --- a/src/FanaBridge.Core/Profiles/DeviceConfig.cs +++ b/src/FanaBridge.Core/Devices/Profiles/DeviceConfig.cs @@ -1,6 +1,6 @@ using System; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Identifies a specific selectable device configuration — a standalone diff --git a/src/FanaBridge.Core/Profiles/DisplayType.cs b/src/FanaBridge.Core/Devices/Profiles/DisplayType.cs similarity index 92% rename from src/FanaBridge.Core/Profiles/DisplayType.cs rename to src/FanaBridge.Core/Devices/Profiles/DisplayType.cs index b446adbc..385d909c 100644 --- a/src/FanaBridge.Core/Profiles/DisplayType.cs +++ b/src/FanaBridge.Core/Devices/Profiles/DisplayType.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// The type of display available on a wheel or button module. diff --git a/src/FanaBridge.Core/Profiles/InputMapping.cs b/src/FanaBridge.Core/Devices/Profiles/InputMapping.cs similarity index 97% rename from src/FanaBridge.Core/Profiles/InputMapping.cs rename to src/FanaBridge.Core/Devices/Profiles/InputMapping.cs index 783f8a0f..0fab0aa3 100644 --- a/src/FanaBridge.Core/Profiles/InputMapping.cs +++ b/src/FanaBridge.Core/Devices/Profiles/InputMapping.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Structured input association for a single LED. diff --git a/src/FanaBridge.Core/Profiles/LedChannelConverter.cs b/src/FanaBridge.Core/Devices/Profiles/LedChannelConverter.cs similarity index 97% rename from src/FanaBridge.Core/Profiles/LedChannelConverter.cs rename to src/FanaBridge.Core/Devices/Profiles/LedChannelConverter.cs index fc216327..c325e696 100644 --- a/src/FanaBridge.Core/Profiles/LedChannelConverter.cs +++ b/src/FanaBridge.Core/Devices/Profiles/LedChannelConverter.cs @@ -1,7 +1,7 @@ using System; using Newtonsoft.Json; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// JSON converter for that accepts both v1 and v2 diff --git a/src/FanaBridge.Core/Profiles/LedColorLimitation.cs b/src/FanaBridge.Core/Devices/Profiles/LedColorLimitation.cs similarity index 99% rename from src/FanaBridge.Core/Profiles/LedColorLimitation.cs rename to src/FanaBridge.Core/Devices/Profiles/LedColorLimitation.cs index 2ca84379..076abcbf 100644 --- a/src/FanaBridge.Core/Profiles/LedColorLimitation.cs +++ b/src/FanaBridge.Core/Devices/Profiles/LedColorLimitation.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// A way in which a wheel's LEDs cannot show what SimHub's color picker offers. diff --git a/src/FanaBridge.Core/Profiles/LedDefinition.cs b/src/FanaBridge.Core/Devices/Profiles/LedDefinition.cs similarity index 97% rename from src/FanaBridge.Core/Profiles/LedDefinition.cs rename to src/FanaBridge.Core/Devices/Profiles/LedDefinition.cs index 19aede41..1a1f88bd 100644 --- a/src/FanaBridge.Core/Profiles/LedDefinition.cs +++ b/src/FanaBridge.Core/Devices/Profiles/LedDefinition.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Describes a single physical LED on the device. diff --git a/src/FanaBridge.Core/Profiles/LedEnums.cs b/src/FanaBridge.Core/Devices/Profiles/LedEnums.cs similarity index 97% rename from src/FanaBridge.Core/Profiles/LedEnums.cs rename to src/FanaBridge.Core/Devices/Profiles/LedEnums.cs index eacf9906..2b6aea68 100644 --- a/src/FanaBridge.Core/Profiles/LedEnums.cs +++ b/src/FanaBridge.Core/Devices/Profiles/LedEnums.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Hardware communication channel for a single LED. diff --git a/src/FanaBridge.Core/Profiles/ProfileMatch.cs b/src/FanaBridge.Core/Devices/Profiles/ProfileMatch.cs similarity index 95% rename from src/FanaBridge.Core/Profiles/ProfileMatch.cs rename to src/FanaBridge.Core/Devices/Profiles/ProfileMatch.cs index 0e59b200..85cd33f9 100644 --- a/src/FanaBridge.Core/Profiles/ProfileMatch.cs +++ b/src/FanaBridge.Core/Devices/Profiles/ProfileMatch.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Matching criteria to associate a profile with the connected hardware. diff --git a/src/FanaBridge.Core/Profiles/ProfileSource.cs b/src/FanaBridge.Core/Devices/Profiles/ProfileSource.cs similarity index 89% rename from src/FanaBridge.Core/Profiles/ProfileSource.cs rename to src/FanaBridge.Core/Devices/Profiles/ProfileSource.cs index 65083c4d..034f7985 100644 --- a/src/FanaBridge.Core/Profiles/ProfileSource.cs +++ b/src/FanaBridge.Core/Devices/Profiles/ProfileSource.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Indicates whether a profile was shipped with the plugin or created by the user. diff --git a/src/FanaBridge.Core/Profiles/WheelCapabilities.cs b/src/FanaBridge.Core/Devices/Profiles/WheelCapabilities.cs similarity index 99% rename from src/FanaBridge.Core/Profiles/WheelCapabilities.cs rename to src/FanaBridge.Core/Devices/Profiles/WheelCapabilities.cs index b10de2eb..829dbe00 100644 --- a/src/FanaBridge.Core/Profiles/WheelCapabilities.cs +++ b/src/FanaBridge.Core/Devices/Profiles/WheelCapabilities.cs @@ -1,6 +1,6 @@ using System.Linq; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Runtime view of a wheel's hardware capabilities, computed from a diff --git a/src/FanaBridge.Core/Profiles/WheelProfile.cs b/src/FanaBridge.Core/Devices/Profiles/WheelProfile.cs similarity index 96% rename from src/FanaBridge.Core/Profiles/WheelProfile.cs rename to src/FanaBridge.Core/Devices/Profiles/WheelProfile.cs index 507f9e39..762d03a6 100644 --- a/src/FanaBridge.Core/Profiles/WheelProfile.cs +++ b/src/FanaBridge.Core/Devices/Profiles/WheelProfile.cs @@ -3,7 +3,7 @@ using System.Linq; using Newtonsoft.Json; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// A complete wheel profile — the single source of truth for a device's @@ -104,17 +104,17 @@ public DisplayType DisplayType { if (Enum.TryParse(Display, true, out DisplayType dt)) return dt; - return FanaBridge.Profiles.DisplayType.None; + return FanaBridge.Core.Devices.Profiles.DisplayType.None; } } /// /// ITM display wire id (which device the wheel's ITM screen is: 1 base, 3 wheel OLED, - /// 4 Bentley). Defaults to + /// 4 Bentley). Defaults to /// (3) when omitted — correct for PBME and GTSWX. /// [JsonIgnore] - public byte ItmDeviceId => ItmDeviceIdRaw ?? FanaBridge.Protocol.ItmEncoder.DefaultDeviceId; + public byte ItmDeviceId => ItmDeviceIdRaw ?? FanaBridge.Core.Display.Protocol.ItmEncoder.DefaultDeviceId; /// Total LED count across all channels. [JsonIgnore] diff --git a/src/FanaBridge.Core/Profiles/WheelProfileStore.cs b/src/FanaBridge.Core/Devices/Profiles/WheelProfileStore.cs similarity index 99% rename from src/FanaBridge.Core/Profiles/WheelProfileStore.cs rename to src/FanaBridge.Core/Devices/Profiles/WheelProfileStore.cs index c520486f..8741f145 100644 --- a/src/FanaBridge.Core/Profiles/WheelProfileStore.cs +++ b/src/FanaBridge.Core/Devices/Profiles/WheelProfileStore.cs @@ -5,7 +5,7 @@ using System.Threading; using Newtonsoft.Json; -namespace FanaBridge.Profiles +namespace FanaBridge.Core.Devices.Profiles { /// /// Loads definitions and provides lookup by @@ -126,7 +126,7 @@ private static void LoadFromEmbeddedResources( foreach (string resourceName in assembly.GetManifestResourceNames()) { // Embedded resource names follow: {RootNamespace}.{RelativePath}.{filename} - // e.g. "FanaBridge.Resources.Profiles.PSWBMW.json" + // e.g. "FanaBridge.Core.Resources.Profiles.PSWBMW.json" if (!resourceName.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) continue; if (resourceName.IndexOf(".Profiles.", StringComparison.OrdinalIgnoreCase) < 0) diff --git a/src/FanaBridge/Diagnostics/ConverterCaptureProbe.cs b/src/FanaBridge.Core/Diagnostics/ConverterCaptureProbe.cs similarity index 99% rename from src/FanaBridge/Diagnostics/ConverterCaptureProbe.cs rename to src/FanaBridge.Core/Diagnostics/ConverterCaptureProbe.cs index 081f9f52..17301db7 100644 --- a/src/FanaBridge/Diagnostics/ConverterCaptureProbe.cs +++ b/src/FanaBridge.Core/Diagnostics/ConverterCaptureProbe.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using FanaBridge.Transport; +using FanaBridge.Core.Devices.Identity; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Diagnostics { /// /// One-shot, read-only identity capture run at diagnostics time so a SINGLE detection report from diff --git a/src/FanaBridge.Core/Diagnostics/FanatecSoftwareMonitor.cs b/src/FanaBridge.Core/Diagnostics/FanatecSoftwareMonitor.cs index 8776c15d..b3e4a420 100644 --- a/src/FanaBridge.Core/Diagnostics/FanatecSoftwareMonitor.cs +++ b/src/FanaBridge.Core/Diagnostics/FanatecSoftwareMonitor.cs @@ -1,6 +1,6 @@ using System; -namespace FanaBridge.Diagnostics +namespace FanaBridge.Core.Diagnostics { /// /// Detects whether the Fanatec app/service is running alongside FanaBridge. Both drive diff --git a/src/FanaBridge.Core/Protocol/ItmDeviceCatalog.cs b/src/FanaBridge.Core/Display/Catalog/ItmDeviceCatalog.cs similarity index 97% rename from src/FanaBridge.Core/Protocol/ItmDeviceCatalog.cs rename to src/FanaBridge.Core/Display/Catalog/ItmDeviceCatalog.cs index b0fa7ff8..5a516bbc 100644 --- a/src/FanaBridge.Core/Protocol/ItmDeviceCatalog.cs +++ b/src/FanaBridge.Core/Display/Catalog/ItmDeviceCatalog.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; +using FanaBridge.Core.Display.Protocol; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Display.Catalog { /// /// One slot in an ITM device's page set: the on-wire page number, which page content diff --git a/src/FanaBridge.Core/Protocol/DisplayEncoder.cs b/src/FanaBridge.Core/Display/Protocol/DisplayEncoder.cs similarity index 98% rename from src/FanaBridge.Core/Protocol/DisplayEncoder.cs rename to src/FanaBridge.Core/Display/Protocol/DisplayEncoder.cs index f51f8db2..f947977a 100644 --- a/src/FanaBridge.Core/Protocol/DisplayEncoder.cs +++ b/src/FanaBridge.Core/Display/Protocol/DisplayEncoder.cs @@ -1,7 +1,7 @@ using System; -using FanaBridge.Transport; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Display.Protocol { /// /// Encodes and sends display control reports for the Fanatec diff --git a/src/FanaBridge.Core/Protocol/ItmEncoder.cs b/src/FanaBridge.Core/Display/Protocol/ItmEncoder.cs similarity index 99% rename from src/FanaBridge.Core/Protocol/ItmEncoder.cs rename to src/FanaBridge.Core/Display/Protocol/ItmEncoder.cs index f43befe4..6ea7819d 100644 --- a/src/FanaBridge.Core/Protocol/ItmEncoder.cs +++ b/src/FanaBridge.Core/Display/Protocol/ItmEncoder.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; -using FanaBridge.Transport; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Display.Protocol { /// /// A single telemetry value for an ITM ValueUpdate entry (col03 FF 05 01). @@ -129,7 +129,7 @@ public static ItmParamDef WithSuffix(byte slotId, string suffix, ushort position /// reference: Enable (FF 02 02), ParamDefs (FF 05 03), ValueUpdate /// (FF 05 01), and the PageSet frame (FF 05 04). /// - /// This is a pure framing layer — like and + /// This is a pure framing layer — like and /// , it builds and writes reports but holds no display /// state. Page selection, telemetry-to-parameter mapping, and the firmware-safety rate /// limits (e.g. value-update pacing) are the caller's diff --git a/src/FanaBridge.Core/Protocol/ItmTelemetry.cs b/src/FanaBridge.Core/Display/Protocol/ItmTelemetry.cs similarity index 98% rename from src/FanaBridge.Core/Protocol/ItmTelemetry.cs rename to src/FanaBridge.Core/Display/Protocol/ItmTelemetry.cs index d2d2f743..6c48d006 100644 --- a/src/FanaBridge.Core/Protocol/ItmTelemetry.cs +++ b/src/FanaBridge.Core/Display/Protocol/ItmTelemetry.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using FanaBridge.Core.Display.Catalog; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Display.Protocol { /// /// A page's content identity — the fixed parameter layout the firmware renders (SPEED @@ -100,7 +102,7 @@ public ItmSubscription(byte firmwareHandle, ushort paramId, byte dataType = 0) /// Wire-side ITM protocol vocabulary: the per-page parameter catalog (which /// parameter IDs a page carries, in order) and firmware subscription-report parsing. /// This is pure wire — no SimHub GameData. The SimHub telemetry → value/suffix - /// mapping lives in ItmTelemetryMapper (Adapters), which knows both sides. + /// mapping lives in ItmTelemetryMapper (Display.Drivers), which knows both sides. /// /// This declares each page's parameter list () and display name /// (), keyed by the content identity. Which pages a diff --git a/src/FanaBridge.Core/Protocol/SevenSegment.cs b/src/FanaBridge.Core/Display/Protocol/SevenSegment.cs similarity index 99% rename from src/FanaBridge.Core/Protocol/SevenSegment.cs rename to src/FanaBridge.Core/Display/Protocol/SevenSegment.cs index fbaa8f49..e580e3d2 100644 --- a/src/FanaBridge.Core/Protocol/SevenSegment.cs +++ b/src/FanaBridge.Core/Display/Protocol/SevenSegment.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Display.Protocol { /// /// 7-segment display encoding table. diff --git a/src/FanaBridge.Core/Protocol/ItmLifecycleController.cs b/src/FanaBridge.Core/Display/Session/ItmLifecycleController.cs similarity index 99% rename from src/FanaBridge.Core/Protocol/ItmLifecycleController.cs rename to src/FanaBridge.Core/Display/Session/ItmLifecycleController.cs index 2dc221bd..1d405f24 100644 --- a/src/FanaBridge.Core/Protocol/ItmLifecycleController.cs +++ b/src/FanaBridge.Core/Display/Session/ItmLifecycleController.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using FanaBridge.Core.Display.Catalog; +using FanaBridge.Core.Display.Protocol; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Display.Session { /// The ITM lifecycle states. See . public enum ItmLifecycleState diff --git a/src/FanaBridge.Core/FanaBridge.Core.csproj b/src/FanaBridge.Core/FanaBridge.Core.csproj index 256e80d9..6a7809fd 100644 --- a/src/FanaBridge.Core/FanaBridge.Core.csproj +++ b/src/FanaBridge.Core/FanaBridge.Core.csproj @@ -3,11 +3,12 @@ net48 Library - - FanaBridge + + FanaBridge.Core FanaBridge.Core FanaBridge device stack (SimHub-free): HID transport, Fanatec protocol, wheel profiles FanaBridge @@ -45,11 +46,8 @@ - - + diff --git a/src/FanaBridge.Core/Protocol/ColorHelper.cs b/src/FanaBridge.Core/Leds/ColorHelper.cs similarity index 99% rename from src/FanaBridge.Core/Protocol/ColorHelper.cs rename to src/FanaBridge.Core/Leds/ColorHelper.cs index 8f88bc3b..1e1039d9 100644 --- a/src/FanaBridge.Core/Protocol/ColorHelper.cs +++ b/src/FanaBridge.Core/Leds/ColorHelper.cs @@ -1,6 +1,6 @@ using System; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Leds { /// /// RGB color helper with RGB565 conversion and predefined colors. diff --git a/src/FanaBridge.Core/Protocol/LedEncoder.cs b/src/FanaBridge.Core/Leds/LedEncoder.cs similarity index 99% rename from src/FanaBridge.Core/Protocol/LedEncoder.cs rename to src/FanaBridge.Core/Leds/LedEncoder.cs index 1ab8b10a..7b897479 100644 --- a/src/FanaBridge.Core/Protocol/LedEncoder.cs +++ b/src/FanaBridge.Core/Leds/LedEncoder.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using FanaBridge.Transport; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Leds { /// /// Encodes and sends LED control reports for Fanatec wheels. diff --git a/src/FanaBridge.Core/Protocol/LegacyLedEncoder.cs b/src/FanaBridge.Core/Leds/LegacyLedEncoder.cs similarity index 99% rename from src/FanaBridge.Core/Protocol/LegacyLedEncoder.cs rename to src/FanaBridge.Core/Leds/LegacyLedEncoder.cs index 626a66ef..007d69e2 100644 --- a/src/FanaBridge.Core/Protocol/LegacyLedEncoder.cs +++ b/src/FanaBridge.Core/Leds/LegacyLedEncoder.cs @@ -1,7 +1,7 @@ using System; -using FanaBridge.Transport; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Leds { /// /// Encodes and sends LED control reports for legacy Fanatec wheels diff --git a/src/FanaBridge.Core/Logging/ILogSink.cs b/src/FanaBridge.Core/Logging/ILogSink.cs new file mode 100644 index 00000000..fed0d66b --- /dev/null +++ b/src/FanaBridge.Core/Logging/ILogSink.cs @@ -0,0 +1,15 @@ +namespace FanaBridge.Core.Logging +{ + /// + /// Log output contract for the SimHub-free core. The plugin shell installs a + /// sink that forwards to SimHub's logger; standalone hosts (unit tests) leave + /// it unset and core logging is a silent no-op. + /// + public interface ILogSink + { + void Info(string message); + void Warn(string message); + void Error(string message); + void Debug(string message); + } +} diff --git a/src/FanaBridge.Core/Logging/Log.cs b/src/FanaBridge.Core/Logging/Log.cs index 270c20b6..43b25050 100644 --- a/src/FanaBridge.Core/Logging/Log.cs +++ b/src/FanaBridge.Core/Logging/Log.cs @@ -1,18 +1,9 @@ +// Exception: namespace FanaBridge (not FanaBridge.Core.Logging) so unqualified Log.* from both assemblies resolves via the shared root ancestor. +#pragma warning disable IDE0130 // Log must live in root FanaBridge so unqualified Log.* resolves from both Core and plugin +using FanaBridge.Core.Logging; + namespace FanaBridge { - /// - /// Log output contract for the SimHub-free core. The plugin shell installs a - /// sink that forwards to SimHub's logger; standalone hosts (unit tests) leave - /// it unset and core logging is a silent no-op. - /// - public interface ILogSink - { - void Info(string message); - void Warn(string message); - void Error(string message); - void Debug(string message); - } - /// /// Process-wide log seam for the core device stack. Kept static (not /// injected) because logging is the one cross-cutting concern threaded diff --git a/src/FanaBridge.Core/Transport/Col03Family.cs b/src/FanaBridge.Core/Transport/Col03Family.cs index 6d7b42a0..87aadaa5 100644 --- a/src/FanaBridge.Core/Transport/Col03Family.cs +++ b/src/FanaBridge.Core/Transport/Col03Family.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Transport +namespace FanaBridge.Core.Transport { /// /// The frame families multiplexed on the col03 input endpoint, as routed by diff --git a/src/FanaBridge.Core/Transport/Col03FrameClassifier.cs b/src/FanaBridge.Core/Transport/Col03FrameClassifier.cs index d9258c7d..814108a5 100644 --- a/src/FanaBridge.Core/Transport/Col03FrameClassifier.cs +++ b/src/FanaBridge.Core/Transport/Col03FrameClassifier.cs @@ -1,6 +1,6 @@ -using FanaBridge.Transport; +using FanaBridge.Core.Devices.Identity; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Transport { /// /// The single home for col03 frame-family wire signatures. The transport's diff --git a/src/FanaBridge.Core/Transport/Col03QueueSet.cs b/src/FanaBridge.Core/Transport/Col03QueueSet.cs index f97d0de0..a6f8b61b 100644 --- a/src/FanaBridge.Core/Transport/Col03QueueSet.cs +++ b/src/FanaBridge.Core/Transport/Col03QueueSet.cs @@ -1,6 +1,5 @@ -using FanaBridge.Protocol; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Transport { /// /// The per-family input queues behind one col03 connection. The transport's diff --git a/src/FanaBridge.Core/Transport/FanatecTransport.cs b/src/FanaBridge.Core/Transport/FanatecTransport.cs index a6d85857..bc983b62 100644 --- a/src/FanaBridge.Core/Transport/FanatecTransport.cs +++ b/src/FanaBridge.Core/Transport/FanatecTransport.cs @@ -2,10 +2,11 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading; +using FanaBridge.Core.Devices; using HidSharp; using Microsoft.Win32.SafeHandles; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Transport { /// /// HID transport layer for Fanatec wheel hardware. diff --git a/src/FanaBridge.Core/Transport/HidReportQueue.cs b/src/FanaBridge.Core/Transport/HidReportQueue.cs index d9a5b98f..46963afe 100644 --- a/src/FanaBridge.Core/Transport/HidReportQueue.cs +++ b/src/FanaBridge.Core/Transport/HidReportQueue.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Threading; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Transport { /// /// Bounded FIFO of HID input reports, bridging the blocking reader thread to diff --git a/src/FanaBridge.Core/Transport/IConnectableTransport.cs b/src/FanaBridge.Core/Transport/IConnectableTransport.cs index 29bfd877..27875547 100644 --- a/src/FanaBridge.Core/Transport/IConnectableTransport.cs +++ b/src/FanaBridge.Core/Transport/IConnectableTransport.cs @@ -1,11 +1,11 @@ using System; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Transport { /// /// The connect-lifecycle surface of a device transport, layered on top of /// (the send/read surface the encoders and - /// protocol readers use). orchestrates against + /// protocol readers use). orchestrates against /// this interface rather than the concrete , /// so its identity state machine (drain → settle → commit, SRM precedence, /// disconnect reset) is unit-testable with a fake transport — the layer where diff --git a/src/FanaBridge.Core/Transport/IDeviceTransport.cs b/src/FanaBridge.Core/Transport/IDeviceTransport.cs index 82578ae3..8eded348 100644 --- a/src/FanaBridge.Core/Transport/IDeviceTransport.cs +++ b/src/FanaBridge.Core/Transport/IDeviceTransport.cs @@ -1,6 +1,6 @@ using System; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Transport { /// /// Low-level HID transport abstraction for all protocol encoders diff --git a/src/FanaBridge.Core/Transport/IHidBusEnumerator.cs b/src/FanaBridge.Core/Transport/IHidBusEnumerator.cs index fb5dc6b3..9310ce7e 100644 --- a/src/FanaBridge.Core/Transport/IHidBusEnumerator.cs +++ b/src/FanaBridge.Core/Transport/IHidBusEnumerator.cs @@ -2,7 +2,7 @@ using System.Linq; using HidSharp; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Transport { /// /// A lightweight snapshot of one HID device on the bus, with the descriptor @@ -34,7 +34,7 @@ public HidDeviceInfo(int productId, int maxOutputReportLength, int maxInputRepor /// /// Seam over HID bus enumeration (DeviceList.Local is a process-global - /// static that cannot be faked). consumes this + /// static that cannot be faked). consumes this /// for discovery so base-PID selection — which has historically bitten on /// interface-shape edge cases — is table-testable. /// diff --git a/src/FanaBridge.Core/Transport/IReportStream.cs b/src/FanaBridge.Core/Transport/IReportStream.cs index b3debd69..d4e9ddee 100644 --- a/src/FanaBridge.Core/Transport/IReportStream.cs +++ b/src/FanaBridge.Core/Transport/IReportStream.cs @@ -1,6 +1,6 @@ using System; -namespace FanaBridge.Transport +namespace FanaBridge.Core.Transport { /// /// One family of inbound HID reports (e.g. col03 FF 08 identity), fed by the diff --git a/src/FanaBridge.Core/Protocol/ReportElicit.cs b/src/FanaBridge.Core/Transport/ReportElicit.cs similarity index 98% rename from src/FanaBridge.Core/Protocol/ReportElicit.cs rename to src/FanaBridge.Core/Transport/ReportElicit.cs index d805dcf1..bb1c3ffe 100644 --- a/src/FanaBridge.Core/Protocol/ReportElicit.cs +++ b/src/FanaBridge.Core/Transport/ReportElicit.cs @@ -1,7 +1,6 @@ using System; -using FanaBridge.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Transport { /// /// The one request→response primitive over a per-family report stream: diff --git a/src/FanaBridge.Core/Protocol/EncoderMode.cs b/src/FanaBridge.Core/Tuning/EncoderMode.cs similarity index 95% rename from src/FanaBridge.Core/Protocol/EncoderMode.cs rename to src/FanaBridge.Core/Tuning/EncoderMode.cs index a8a71d83..70d635d5 100644 --- a/src/FanaBridge.Core/Protocol/EncoderMode.cs +++ b/src/FanaBridge.Core/Tuning/EncoderMode.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Tuning { /// /// Encoder operating mode for Fanatec button modules. diff --git a/src/FanaBridge.Core/Protocol/FanatecTuningController.cs b/src/FanaBridge.Core/Tuning/FanatecTuningController.cs similarity index 99% rename from src/FanaBridge.Core/Protocol/FanatecTuningController.cs rename to src/FanaBridge.Core/Tuning/FanatecTuningController.cs index f4f9001f..17093dd6 100644 --- a/src/FanaBridge.Core/Protocol/FanatecTuningController.cs +++ b/src/FanaBridge.Core/Tuning/FanatecTuningController.cs @@ -1,8 +1,8 @@ using System; using System.Threading; -using FanaBridge.Transport; +using FanaBridge.Core.Transport; -namespace FanaBridge.Protocol +namespace FanaBridge.Core.Tuning { /// /// Manages the Fanatec tuning configuration protocol (col03, cmd class 0x03). diff --git a/src/FanaBridge/Adapters/ControlMapperBridge.cs b/src/FanaBridge/ControlMapper/ControlMapperBridge.cs similarity index 99% rename from src/FanaBridge/Adapters/ControlMapperBridge.cs rename to src/FanaBridge/ControlMapper/ControlMapperBridge.cs index 8326cbad..e36a2c1e 100644 --- a/src/FanaBridge/Adapters/ControlMapperBridge.cs +++ b/src/FanaBridge/ControlMapper/ControlMapperBridge.cs @@ -6,7 +6,7 @@ using System.Text; using SimHub.Plugins.OutputPlugins.ControlRemapper.Variants; -namespace FanaBridge.Adapters +namespace FanaBridge.ControlMapper { /// /// Reflection plumbing that registers a diff --git a/src/FanaBridge/Adapters/FanaBridgeVariantProvider.cs b/src/FanaBridge/ControlMapper/FanaBridgeVariantProvider.cs similarity index 97% rename from src/FanaBridge/Adapters/FanaBridgeVariantProvider.cs rename to src/FanaBridge/ControlMapper/FanaBridgeVariantProvider.cs index dd850c82..9314c406 100644 --- a/src/FanaBridge/Adapters/FanaBridgeVariantProvider.cs +++ b/src/FanaBridge/ControlMapper/FanaBridgeVariantProvider.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using FanaBridge.Protocol; +using FanaBridge.Core.Devices.Identity; using SimHub.Plugins.OutputPlugins.ControlRemapper.Variants; -namespace FanaBridge.Adapters +namespace FanaBridge.ControlMapper { /// /// Reports the currently-attached Fanatec rim to SimHub's Control Mapper as an @@ -40,7 +40,7 @@ public class FanaBridgeVariantProvider : IVariantProvider /// /// Fanatec USB vendor id (0x0EB7 = 3767). Matches the value the stock /// FanatecVariantProvider gates on, and - /// . + /// . /// public const int FanatecVendorId = 0x0EB7; @@ -77,7 +77,7 @@ public class FanaBridgeVariantProvider : IVariantProvider /// provider, or no variant at all, decide). /// /// Gated on the Fanatec vendor id AND the connected wheelbase's product id - /// (): we only speak for + /// (): we only speak for /// the device we actually have open over HID. A standalone Fanatec peripheral on /// its own PID (pedals, handbrake) — or another Fanatec base we aren't driving — /// is therefore left alone and falls through to SimHub instead of being tagged with diff --git a/src/FanaBridge/Adapters/FanatecDevicesRegistry.cs b/src/FanaBridge/Devices/FanatecDevicesRegistry.cs similarity index 98% rename from src/FanaBridge/Adapters/FanatecDevicesRegistry.cs rename to src/FanaBridge/Devices/FanatecDevicesRegistry.cs index 723b235c..3a1532a1 100644 --- a/src/FanaBridge/Adapters/FanatecDevicesRegistry.cs +++ b/src/FanaBridge/Devices/FanatecDevicesRegistry.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using FanaBridge.Profiles; +using FanaBridge.Core.Devices.Profiles; +using FanaBridge.Settings; +using FanaBridge.UI.Devices; using SimHub.Plugins.Devices; -namespace FanaBridge.Adapters +namespace FanaBridge.Devices { /// /// Registers one DeviceDescriptor per loaded so each @@ -25,7 +27,7 @@ public class FanatecDevicesRegistry : IDeviceDescriptorsRegistry /// plugin: SimHub shows a device's settings whether or not FanaBridge is /// running. Overridable so tests can build instances without WPF. /// - internal static readonly IDevicePanelFactory PanelFactory = new UI.DevicePanelFactory(); + internal static readonly IDevicePanelFactory PanelFactory = new DevicePanelFactory(); public IEnumerable GetDevices() { diff --git a/src/FanaBridge/Adapters/FanatecWheelDeviceInstance.cs b/src/FanaBridge/Devices/FanatecWheelDeviceInstance.cs similarity index 99% rename from src/FanaBridge/Adapters/FanatecWheelDeviceInstance.cs rename to src/FanaBridge/Devices/FanatecWheelDeviceInstance.cs index d067b0db..a1008e17 100644 --- a/src/FanaBridge/Adapters/FanatecWheelDeviceInstance.cs +++ b/src/FanaBridge/Devices/FanatecWheelDeviceInstance.cs @@ -3,16 +3,20 @@ using System.ComponentModel; using System.Linq; using FanaBridge; -using FanaBridge.Profiles; -using FanaBridge.Protocol; -using FanaBridge.Transport; +using FanaBridge.Core.Devices.Profiles; +using FanaBridge.Core.Display.Session; +using FanaBridge.Display; +using FanaBridge.Display.Drivers; +using FanaBridge.Leds; +using FanaBridge.Settings; +using FanaBridge.UI.Devices; using GameReaderCommon; using Newtonsoft.Json.Linq; using SimHub.Plugins; using SimHub.Plugins.Devices; using SimHub.Plugins.OutputPlugins.GraphicalDash.LedModules; -namespace FanaBridge.Adapters +namespace FanaBridge.Devices { /// /// DeviceInstance for a specific Fanatec wheel type. diff --git a/src/FanaBridge/Adapters/SimHubDevicesGateway.cs b/src/FanaBridge/Devices/SimHubDevicesGateway.cs similarity index 98% rename from src/FanaBridge/Adapters/SimHubDevicesGateway.cs rename to src/FanaBridge/Devices/SimHubDevicesGateway.cs index 97c51cca..ed182b1f 100644 --- a/src/FanaBridge/Adapters/SimHubDevicesGateway.cs +++ b/src/FanaBridge/Devices/SimHubDevicesGateway.cs @@ -3,7 +3,7 @@ using SimHub.Plugins; using SimHub.Plugins.Devices; -namespace FanaBridge.Adapters +namespace FanaBridge.Devices { /// /// Read-and-check access to SimHub's Devices plugin for the settings diff --git a/src/FanaBridge/Diagnostics/DiagnosticsReport.cs b/src/FanaBridge/Diagnostics/DiagnosticsReport.cs index 4380e383..4a9f3fed 100644 --- a/src/FanaBridge/Diagnostics/DiagnosticsReport.cs +++ b/src/FanaBridge/Diagnostics/DiagnosticsReport.cs @@ -1,10 +1,11 @@ using System; using System.Linq; using System.Text; -using FanaBridge.Transport; +using FanaBridge.Core.Devices; +using FanaBridge.Core.Diagnostics; using HidSharp; -namespace FanaBridge.Protocol +namespace FanaBridge.Diagnostics { /// /// Builds a human-readable, GitHub-ready snapshot of device detection — the diff --git a/src/FanaBridge/Adapters/DisplaySettings.cs b/src/FanaBridge/Display/DisplaySettings.cs similarity index 98% rename from src/FanaBridge/Adapters/DisplaySettings.cs rename to src/FanaBridge/Display/DisplaySettings.cs index 457e8534..7c1627d6 100644 --- a/src/FanaBridge/Adapters/DisplaySettings.cs +++ b/src/FanaBridge/Display/DisplaySettings.cs @@ -1,4 +1,4 @@ -namespace FanaBridge.Adapters +namespace FanaBridge.Display { /// /// Type-safe display configuration. diff --git a/src/FanaBridge/Adapters/FanatecDisplayDriver.cs b/src/FanaBridge/Display/Drivers/FanatecDisplayDriver.cs similarity index 98% rename from src/FanaBridge/Adapters/FanatecDisplayDriver.cs rename to src/FanaBridge/Display/Drivers/FanatecDisplayDriver.cs index 55dac741..3df69521 100644 --- a/src/FanaBridge/Adapters/FanatecDisplayDriver.cs +++ b/src/FanaBridge/Display/Drivers/FanatecDisplayDriver.cs @@ -1,8 +1,9 @@ -using FanaBridge.Protocol; -using GameReaderCommon; using System; +using FanaBridge.Core.Display.Protocol; +using FanaBridge.Display; +using GameReaderCommon; -namespace FanaBridge.Adapters +namespace FanaBridge.Display.Drivers { /// /// Maps telemetry data to the Fanatec 3-digit 7-segment display. diff --git a/src/FanaBridge/Adapters/ItmDisplayDriver.cs b/src/FanaBridge/Display/Drivers/ItmDisplayDriver.cs similarity index 99% rename from src/FanaBridge/Adapters/ItmDisplayDriver.cs rename to src/FanaBridge/Display/Drivers/ItmDisplayDriver.cs index f1c96fb8..5ae602b9 100644 --- a/src/FanaBridge/Adapters/ItmDisplayDriver.cs +++ b/src/FanaBridge/Display/Drivers/ItmDisplayDriver.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; -using FanaBridge.Protocol; +using FanaBridge.Core.Display.Protocol; +using FanaBridge.Core.Display.Session; using GameReaderCommon; -namespace FanaBridge.Adapters +namespace FanaBridge.Display.Drivers { /// /// Drives a Fanatec ITM telemetry display: maps SimHub telemetry to the parameters the diff --git a/src/FanaBridge/Adapters/ItmTelemetryMapper.cs b/src/FanaBridge/Display/Drivers/ItmTelemetryMapper.cs similarity index 98% rename from src/FanaBridge/Adapters/ItmTelemetryMapper.cs rename to src/FanaBridge/Display/Drivers/ItmTelemetryMapper.cs index 258d9e3b..e34f733c 100644 --- a/src/FanaBridge/Adapters/ItmTelemetryMapper.cs +++ b/src/FanaBridge/Display/Drivers/ItmTelemetryMapper.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using FanaBridge.Core.Display.Protocol; using GameReaderCommon; -using FanaBridge.Protocol; -namespace FanaBridge.Adapters +namespace FanaBridge.Display.Drivers { /// /// Maps SimHub telemetry to encoded entries @@ -14,8 +14,8 @@ namespace FanaBridge.Adapters /// The wire-side vocabulary — the page catalog (which params a page carries) and /// subscription-report parsing — lives in (Protocol, no SimHub). /// This class knows both sides (wire paramId + SimHub GameData), which is why - /// it belongs in Adapters, not Protocol. It holds no wire framing and no state — the pure, - /// per-frame translation step, the ITM analogue of FanatecDisplayDriver's reads. + /// it belongs in Display.Drivers (plugin), not Protocol (Core). It holds no wire framing and + /// no state — the pure, per-frame translation step, the ITM analogue of FanatecDisplayDriver's reads. /// public static class ItmTelemetryMapper { diff --git a/src/FanaBridge/FanatecPlugin.cs b/src/FanaBridge/FanatecPlugin.cs index 23e29494..3835c050 100644 --- a/src/FanaBridge/FanatecPlugin.cs +++ b/src/FanaBridge/FanatecPlugin.cs @@ -1,18 +1,24 @@ -using FanaBridge.Adapters; -using FanaBridge.Profiles; -using FanaBridge.Protocol; -using FanaBridge.Transport; -using FanaBridge.UI; -using FanaBridge.Updater; -using FanaBridge.Updates; -using GameReaderCommon; -using SimHub.Plugins; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows.Media; using System.Windows.Media.Imaging; +using FanaBridge.ControlMapper; +using FanaBridge.Core.Devices; +using FanaBridge.Core.Devices.Profiles; +using FanaBridge.Core.Display.Protocol; +using FanaBridge.Core.Leds; +using FanaBridge.Core.Transport; +using FanaBridge.Core.Tuning; +using FanaBridge.Devices; +using FanaBridge.Diagnostics; +using FanaBridge.Settings; +using FanaBridge.UI.Settings; +using FanaBridge.Updater; +using FanaBridge.Updates; +using GameReaderCommon; +using SimHub.Plugins; namespace FanaBridge { @@ -61,7 +67,7 @@ public class FanatecPlugin : IPlugin, IDataPlugin, IWPFSettingsV2, IReusable // volatile: written on the DataUpdate thread (UpdateControlMapperIntegration), // read in the WheelChanged handler, which may fire from any thread. Ensures the // handler observes the constructed bridge; a missed read self-heals next tick. - private volatile FanaBridge.Adapters.ControlMapperBridge _controlMapperBridge; + private volatile ControlMapperBridge _controlMapperBridge; /// Frame counter that throttles the Control Mapper reconcile (see ). private int _cmReconcileTick; @@ -71,8 +77,8 @@ public class FanatecPlugin : IPlugin, IDataPlugin, IWPFSettingsV2, IReusable // connected wheel's SimHub device name — the user's rename if set, else the // short name — and label the mapped controller consistently with the Devices view. private readonly object _deviceInstancesLock = new object(); - private readonly List _deviceInstances = - new List(); + private readonly List _deviceInstances = + new List(); /// Fired when connection status or wheel identity changes. May fire from any thread. public event Action StateChanged; @@ -214,10 +220,10 @@ internal void InstallWheelbaseForTest(FanatecWheelbase wheelbase, bool withDispl _display = new DisplayEncoder(wheelbase.Transport); } - /// Called by each so the + /// Called by each so the /// plugin can read the connected wheel's SimHub device name for the Control Mapper /// integration. Idempotent. - internal void RegisterDeviceInstance(Adapters.FanatecWheelDeviceInstance instance) + internal void RegisterDeviceInstance(FanatecWheelDeviceInstance instance) { if (instance == null) return; lock (_deviceInstancesLock) @@ -226,7 +232,7 @@ internal void RegisterDeviceInstance(Adapters.FanatecWheelDeviceInstance instanc } /// Removes a DeviceInstance from the registry (on its End). - internal void UnregisterDeviceInstance(Adapters.FanatecWheelDeviceInstance instance) + internal void UnregisterDeviceInstance(FanatecWheelDeviceInstance instance) { if (instance == null) return; lock (_deviceInstancesLock) @@ -264,8 +270,8 @@ public string GetConnectedWheelDisplayName() // Detects the vendor's app/service running alongside FanaBridge — the same // display, no arbitration, so it's a warning the user needs to see instead of // chasing phantom ITM flicker. TTL-cached; edges are logged once. - private readonly Diagnostics.FanatecSoftwareMonitor _fanatecSoftware = - new Diagnostics.FanatecSoftwareMonitor( + private readonly Core.Diagnostics.FanatecSoftwareMonitor _fanatecSoftware = + new Core.Diagnostics.FanatecSoftwareMonitor( log: msg => SimHub.Logging.Current.Warn("FanaBridge: " + msg)); /// @@ -320,7 +326,7 @@ internal void ProbeItmCoDriver() { try { - var bridge = _controlMapperBridge ?? new FanaBridge.Adapters.ControlMapperBridge(); + var bridge = _controlMapperBridge ?? new ControlMapperBridge(); return bridge.IsRecognizeIndividualWheelsOn(PluginManager); } catch { return null; } @@ -408,7 +414,7 @@ public void Init(PluginManager pluginManager) // dashboard while testing the experimental Control Mapper integration. // Cheap (no reflection) — it only reads live wheel state. this.AttachDelegate("FanaBridge.ControlMapperVariant", - () => Adapters.FanaBridgeVariantProvider.ComputeFriendlyName() ?? ""); + () => FanaBridgeVariantProvider.ComputeFriendlyName() ?? ""); // --- Events --- this.AddEvent("DeviceConnected"); @@ -628,7 +634,7 @@ private void UpdateControlMapperIntegration() if (Settings.EnableControlMapperIntegration) { if (_controlMapperBridge == null) - _controlMapperBridge = new FanaBridge.Adapters.ControlMapperBridge(); + _controlMapperBridge = new ControlMapperBridge(); _controlMapperBridge.EnsureRegistered(PluginManager); // Stamp a friendly CustomName on the connected wheel's source(s) so // the UI shows a readable name while the match key stays the stable id. @@ -714,9 +720,9 @@ public void FinalizePlugin() // serializes this against any frame still in flight; see // docs/device-settings-lifecycle.md. Copied out of the lock so // no device work runs while holding the instance list. - List instances; + List instances; lock (_deviceInstancesLock) - instances = new List(_deviceInstances); + instances = new List(_deviceInstances); foreach (var inst in instances) { @@ -773,7 +779,7 @@ public string BuildDiagnosticsReport() string controlMapperSection; try { - var bridge = _controlMapperBridge ?? new FanaBridge.Adapters.ControlMapperBridge(); + var bridge = _controlMapperBridge ?? new ControlMapperBridge(); controlMapperSection = bridge.DescribeResolution(PluginManager); } catch (Exception ex) diff --git a/src/FanaBridge/Adapters/FanatecLedDriver.cs b/src/FanaBridge/Leds/FanatecLedDriver.cs similarity index 99% rename from src/FanaBridge/Adapters/FanatecLedDriver.cs rename to src/FanaBridge/Leds/FanatecLedDriver.cs index 7f8c1805..8fd5b9eb 100644 --- a/src/FanaBridge/Adapters/FanatecLedDriver.cs +++ b/src/FanaBridge/Leds/FanatecLedDriver.cs @@ -5,10 +5,10 @@ using BA63Driver.Base; using BA63Driver.Interfaces; using BA63Driver.Mapper; -using FanaBridge.Profiles; -using FanaBridge.Protocol; +using FanaBridge.Core.Devices.Profiles; +using FanaBridge.Core.Leds; -namespace FanaBridge.Adapters +namespace FanaBridge.Leds { /// /// Unified BA63-compatible driver for all Fanatec LED types. diff --git a/src/FanaBridge/Adapters/FanatecLedManager.cs b/src/FanaBridge/Leds/FanatecLedManager.cs similarity index 98% rename from src/FanaBridge/Adapters/FanatecLedManager.cs rename to src/FanaBridge/Leds/FanatecLedManager.cs index dc1d5ba5..cf20b408 100644 --- a/src/FanaBridge/Adapters/FanatecLedManager.cs +++ b/src/FanaBridge/Leds/FanatecLedManager.cs @@ -1,15 +1,14 @@ using System; -using FanaBridge.Profiles; -using FanaBridge.Protocol; +using FanaBridge.Core.Devices.Profiles; using SimHub.Plugins.OutputPlugins.GraphicalDash.PSE; -namespace FanaBridge.Adapters +namespace FanaBridge.Leds { /// /// Bridges into SimHub's /// ILedDeviceManager pipeline via LedsGenericManager<T>. /// - /// Each creates one of these with + /// Each creates one of these with /// the appropriate and passes it to /// LedModuleSettings<FanatecLedManager> as a pre-created driver /// (via LedModuleOptions.LedDriver or the constructor overload). diff --git a/src/FanaBridge/Adapters/FanatecLedModuleHost.cs b/src/FanaBridge/Leds/FanatecLedModuleHost.cs similarity index 98% rename from src/FanaBridge/Adapters/FanatecLedModuleHost.cs rename to src/FanaBridge/Leds/FanatecLedModuleHost.cs index 8dc7e887..5b35ccac 100644 --- a/src/FanaBridge/Adapters/FanatecLedModuleHost.cs +++ b/src/FanaBridge/Leds/FanatecLedModuleHost.cs @@ -2,14 +2,14 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Controls; -using FanaBridge.Profiles; -using FanaBridge.Transport; +using FanaBridge.Core.Devices; +using FanaBridge.Core.Devices.Profiles; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SimHub.Plugins; using SimHub.Plugins.OutputPlugins.GraphicalDash.LedModules; -namespace FanaBridge.Adapters +namespace FanaBridge.Leds { /// /// The real LED settings module, built from registered (override-resolved) @@ -51,7 +51,7 @@ public FanatecLedModuleHost( // resolves capabilities when the tab is opened, so it reflects // the wheel that is actually connected. ExtraSettingsControlFactory = - _ => new UI.LedColorLimitationNotice(currentCapabilities), + _ => new UI.Devices.LedColorLimitationNotice(currentCapabilities), }; _module = new LedModuleSettings(options) diff --git a/src/FanaBridge/Adapters/IFanatecLedModuleHost.cs b/src/FanaBridge/Leds/IFanatecLedModuleHost.cs similarity index 94% rename from src/FanaBridge/Adapters/IFanatecLedModuleHost.cs rename to src/FanaBridge/Leds/IFanatecLedModuleHost.cs index b87de454..4ce298f1 100644 --- a/src/FanaBridge/Adapters/IFanatecLedModuleHost.cs +++ b/src/FanaBridge/Leds/IFanatecLedModuleHost.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Windows.Controls; +using FanaBridge.Core.Devices.Profiles; using Newtonsoft.Json.Linq; using SimHub.Plugins; -namespace FanaBridge.Adapters +namespace FanaBridge.Leds { /// /// The LED settings module, behind an interface so persistence is testable @@ -52,7 +53,7 @@ internal interface IFanatecLedModuleHost : IDisposable void StopDriving(); /// Tells the module the active profile may have changed. - void HotSwapIfNeeded(Profiles.WheelCapabilities currentCaps); + void HotSwapIfNeeded(WheelCapabilities currentCaps); /// The module's dynamic button actions (brightness, etc.). IEnumerable GetDynamicActions(); diff --git a/src/FanaBridge/Adapters/NoLedModuleHost.cs b/src/FanaBridge/Leds/NoLedModuleHost.cs similarity index 93% rename from src/FanaBridge/Adapters/NoLedModuleHost.cs rename to src/FanaBridge/Leds/NoLedModuleHost.cs index 1199feb4..17c8f53f 100644 --- a/src/FanaBridge/Adapters/NoLedModuleHost.cs +++ b/src/FanaBridge/Leds/NoLedModuleHost.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Controls; -using FanaBridge.Profiles; +using FanaBridge.Core.Devices.Profiles; using Newtonsoft.Json.Linq; using SimHub.Plugins; -namespace FanaBridge.Adapters +namespace FanaBridge.Leds { /// /// Stands in for the LED module on devices whose registered capabilities diff --git a/src/FanaBridge/Logging/SimHubLogSink.cs b/src/FanaBridge/Logging/SimHubLogSink.cs index 4273b77a..b9a215b6 100644 --- a/src/FanaBridge/Logging/SimHubLogSink.cs +++ b/src/FanaBridge/Logging/SimHubLogSink.cs @@ -1,6 +1,7 @@ using System.Runtime.CompilerServices; +using FanaBridge.Core.Logging; -namespace FanaBridge +namespace FanaBridge.Logging { /// Forwards the core's seam to SimHub's logger. internal sealed class SimHubLogSink : ILogSink @@ -23,11 +24,3 @@ internal static class CoreLogBridge internal static void Install() => Log.Sink = new SimHubLogSink(); } } - -// The C# 9 module-initializer feature is compiler-only; net48 just lacks the -// attribute type. Declaring it internally is the standard polyfill. -namespace System.Runtime.CompilerServices -{ - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class ModuleInitializerAttribute : Attribute { } -} diff --git a/src/FanaBridge/Properties/ModuleInitializerAttribute.cs b/src/FanaBridge/Properties/ModuleInitializerAttribute.cs new file mode 100644 index 00000000..55fa1e36 --- /dev/null +++ b/src/FanaBridge/Properties/ModuleInitializerAttribute.cs @@ -0,0 +1,10 @@ +// net48 polyfill for the C# 9 module-initializer attribute (compiler-only; type missing on TFM). +// Exception to directory/namespace literalness: System.Runtime.CompilerServices by definition. +#pragma warning disable IDE0130 // Polyfill attribute must live in System.Runtime.CompilerServices by definition +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class ModuleInitializerAttribute : Attribute { } +} diff --git a/src/FanaBridge/Adapters/FanatecDeviceSettings.cs b/src/FanaBridge/Settings/FanatecDeviceSettings.cs similarity index 98% rename from src/FanaBridge/Adapters/FanatecDeviceSettings.cs rename to src/FanaBridge/Settings/FanatecDeviceSettings.cs index 89a1f477..c2d315c5 100644 --- a/src/FanaBridge/Adapters/FanatecDeviceSettings.cs +++ b/src/FanaBridge/Settings/FanatecDeviceSettings.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using FanaBridge.Profiles; +using FanaBridge.Core.Devices.Profiles; +using FanaBridge.Display; +using FanaBridge.Leds; using Newtonsoft.Json.Linq; -namespace FanaBridge.Adapters +namespace FanaBridge.Settings { /// /// Owns everything a device persists: FanaBridge's own settings, the LED diff --git a/src/FanaBridge/FanatecPluginSettings.cs b/src/FanaBridge/Settings/FanatecPluginSettings.cs similarity index 96% rename from src/FanaBridge/FanatecPluginSettings.cs rename to src/FanaBridge/Settings/FanatecPluginSettings.cs index 8c64ae6f..f6d4f339 100644 --- a/src/FanaBridge/FanatecPluginSettings.cs +++ b/src/FanaBridge/Settings/FanatecPluginSettings.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace FanaBridge +namespace FanaBridge.Settings { /// /// Plugin settings. Must be JSON-serializable (public fields/properties, no complex types). @@ -49,7 +49,7 @@ public class FanatecPluginSettings /// (surfaced in the settings UI when it isn't). Gap-filler: SimHub wins for any /// wheel it already recognizes, so existing mappings are never disturbed, and it's /// a no-op unless the user has opted into per-rim mapping at the SimHub level. See - /// . + /// . /// public bool EnableControlMapperIntegration { get; set; } = true; diff --git a/src/FanaBridge/Adapters/FanatecSettingsSnapshot.cs b/src/FanaBridge/Settings/FanatecSettingsSnapshot.cs similarity index 98% rename from src/FanaBridge/Adapters/FanatecSettingsSnapshot.cs rename to src/FanaBridge/Settings/FanatecSettingsSnapshot.cs index ca537f64..54f919ba 100644 --- a/src/FanaBridge/Adapters/FanatecSettingsSnapshot.cs +++ b/src/FanaBridge/Settings/FanatecSettingsSnapshot.cs @@ -1,6 +1,7 @@ -using Newtonsoft.Json.Linq; +using FanaBridge.Display; +using Newtonsoft.Json.Linq; -namespace FanaBridge.Adapters +namespace FanaBridge.Settings { /// /// The FanaBridge-owned settings of one device, as an immutable value. diff --git a/src/FanaBridge/Adapters/PersistedPluginSettings.cs b/src/FanaBridge/Settings/PersistedPluginSettings.cs similarity index 99% rename from src/FanaBridge/Adapters/PersistedPluginSettings.cs rename to src/FanaBridge/Settings/PersistedPluginSettings.cs index e272ff3a..3e250e7b 100644 --- a/src/FanaBridge/Adapters/PersistedPluginSettings.cs +++ b/src/FanaBridge/Settings/PersistedPluginSettings.cs @@ -3,7 +3,7 @@ using System.IO; using Newtonsoft.Json.Linq; -namespace FanaBridge.Adapters +namespace FanaBridge.Settings { /// /// Reads the plugin's persisted settings without a running plugin. diff --git a/src/FanaBridge/UI/DevicePanelFactory.cs b/src/FanaBridge/UI/Devices/DevicePanelFactory.cs similarity index 83% rename from src/FanaBridge/UI/DevicePanelFactory.cs rename to src/FanaBridge/UI/Devices/DevicePanelFactory.cs index 64e906b6..4a3da7d7 100644 --- a/src/FanaBridge/UI/DevicePanelFactory.cs +++ b/src/FanaBridge/UI/Devices/DevicePanelFactory.cs @@ -1,9 +1,11 @@ using System; using System.Windows.Controls; -using FanaBridge.Adapters; -using FanaBridge.Profiles; +using FanaBridge.Core.Devices.Profiles; +using FanaBridge.Display; +using FanaBridge.Settings; +using FanaBridge.UI.Settings; -namespace FanaBridge.UI +namespace FanaBridge.UI.Devices { /// UI-side implementation of the device settings-panel factory. internal sealed class DevicePanelFactory : IDevicePanelFactory diff --git a/src/FanaBridge/Adapters/IDevicePanelFactory.cs b/src/FanaBridge/UI/Devices/IDevicePanelFactory.cs similarity index 88% rename from src/FanaBridge/Adapters/IDevicePanelFactory.cs rename to src/FanaBridge/UI/Devices/IDevicePanelFactory.cs index 14285667..d2a58a3b 100644 --- a/src/FanaBridge/Adapters/IDevicePanelFactory.cs +++ b/src/FanaBridge/UI/Devices/IDevicePanelFactory.cs @@ -1,8 +1,10 @@ using System; using System.Windows.Controls; -using FanaBridge.Profiles; +using FanaBridge.Core.Devices.Profiles; +using FanaBridge.Display; +using FanaBridge.Settings; -namespace FanaBridge.Adapters +namespace FanaBridge.UI.Devices { /// /// Constructs the per-device WPF settings panels. diff --git a/src/FanaBridge/UI/LedColorLimitationNotice.xaml b/src/FanaBridge/UI/Devices/LedColorLimitationNotice.xaml similarity index 95% rename from src/FanaBridge/UI/LedColorLimitationNotice.xaml rename to src/FanaBridge/UI/Devices/LedColorLimitationNotice.xaml index ebbdcdec..150df6d3 100644 --- a/src/FanaBridge/UI/LedColorLimitationNotice.xaml +++ b/src/FanaBridge/UI/Devices/LedColorLimitationNotice.xaml @@ -1,4 +1,4 @@ -