Phase 8C: RioJoy.Tray multi-targets net48 (x64) + net40 (x86, Windows XP)
The XP flavor keeps the full runtime AND the mapping editor (decided: editor everywhere). Gated per flavor: - Wallpaper maker + WallpaperCanvas + generation (SkiaSharp) are net48-only; the XP coordinator applies the profile's pre-rendered wallpaper instead, and WallpaperApplier converts PNG->BMP on net40 (XP SPI_SETDESKWALLPAPER accepts only BMP). - Joystick chain: ViGEm branch compiled out on net40 (HID feeder stays, ready for RioGamepadXP.sys); the editor button picker offers the full 96 HID buttons on net40 vs the 11 named Xbox buttons on net48. Verified: net40/x86 exe boots on Win10; the editor renders offline in a 32-bit host with a Button-50 joystick binding resolving correctly and live gauges working (screenshot harness). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -261,9 +261,15 @@ public sealed class ProfileEditorForm : Form
|
||||
_valueCombo.Items.Add(new ValueItem(k.Name, k.Value));
|
||||
break;
|
||||
case RioRouteKind.Joystick:
|
||||
#if NET40
|
||||
// XP: RioGamepadXP exposes the full 96-button HID layout.
|
||||
for (int btn = 1; btn <= RioJoy.Core.Hid.RioHidReport.ButtonCount; btn++)
|
||||
_valueCombo.Items.Add(new ValueItem($"Button {btn}", (byte)btn));
|
||||
#else
|
||||
// The signed ViGEmBus Xbox 360 pad exposes 11 buttons; show their names.
|
||||
for (int btn = 1; btn <= ViGEmJoystickSink.MappableButtonCount; btn++)
|
||||
_valueCombo.Items.Add(new ValueItem($"Button {btn} ({ViGEmJoystickSink.ButtonNames[btn - 1]})", (byte)btn));
|
||||
#endif
|
||||
break;
|
||||
case RioRouteKind.Hat:
|
||||
foreach (RioHat h in new[] { RioHat.Up, RioHat.Right, RioHat.Down, RioHat.Left })
|
||||
|
||||
@@ -5,7 +5,9 @@ using RioJoy.Core.Output;
|
||||
using RioJoy.Core.Overlay;
|
||||
using RioJoy.Core.Profiles;
|
||||
using RioJoy.Core.Serial;
|
||||
using RioJoy.Overlay;
|
||||
#if !NET40
|
||||
using RioJoy.Overlay; // SkiaSharp wallpaper generation — modern flavor only
|
||||
#endif
|
||||
|
||||
namespace RioJoy.Tray;
|
||||
|
||||
@@ -149,12 +151,14 @@ public sealed class RioCoordinator : IDisposable
|
||||
// feeder, then a no-op when neither driver is present.
|
||||
private IJoystickSink CreateJoystickSink(out string note)
|
||||
{
|
||||
#if !NET40 // ViGEmBus is Win10+; on XP the HID feeder drives RioGamepadXP.sys
|
||||
if (ViGEmJoystickSink.TryCreate(out ViGEmJoystickSink? vigem))
|
||||
{
|
||||
_joystick = vigem;
|
||||
note = string.Empty;
|
||||
return vigem!;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (HidFeederJoystickSink.TryCreate(out HidFeederJoystickSink? feeder))
|
||||
{
|
||||
@@ -241,6 +245,20 @@ public sealed class RioCoordinator : IDisposable
|
||||
/// </summary>
|
||||
private void ApplyWallpaper(RioProfile profile)
|
||||
{
|
||||
#if NET40
|
||||
// XP flavor: no SkiaSharp, so no generation — apply the profile's
|
||||
// pre-rendered wallpaper (authored on a modern machine) if it exists.
|
||||
if (string.IsNullOrWhiteSpace(profile.WallpaperPath) || !File.Exists(profile.WallpaperPath))
|
||||
return;
|
||||
try
|
||||
{
|
||||
WallpaperApplier.Apply(profile.WallpaperPath!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetStatus($"{Status} [wallpaper: {ex.Message}]");
|
||||
}
|
||||
#else
|
||||
string? templatePath = _config().OverlayTemplatePath;
|
||||
if (string.IsNullOrWhiteSpace(templatePath) || !File.Exists(templatePath))
|
||||
return;
|
||||
@@ -263,6 +281,7 @@ public sealed class RioCoordinator : IDisposable
|
||||
{
|
||||
SetStatus($"{Status} [wallpaper: {ex.Message}]");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string SafeFileName(string name)
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<!-- net48 = Windows 10/11 (x64, full app incl. wallpaper maker/SkiaSharp).
|
||||
net40 = Windows XP SP3 (x86): full runtime + mapping editor; wallpaper
|
||||
GENERATION is excluded (SkiaSharp has no XP support) — XP applies
|
||||
pre-rendered wallpapers only. See docs/PLAN.md §Phase 8. -->
|
||||
<TargetFrameworks>net48;net40</TargetFrameworks>
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -12,12 +15,29 @@
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RioJoy.Core\RioJoy.Core.csproj" />
|
||||
<ProjectReference Include="..\RioJoy.Overlay\RioJoy.Overlay.csproj" />
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net48'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net40'">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net40'">
|
||||
<!-- net40 has no System.Net.Http assembly for the implicit global using. -->
|
||||
<Using Remove="System.Net.Http" />
|
||||
<!-- Wallpaper maker needs the SkiaSharp renderer (RioJoy.Overlay) — modern only. -->
|
||||
<Compile Remove="Editor\WallpaperMakerForm.cs" />
|
||||
<Compile Remove="Editor\WallpaperCanvas.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RioJoy.Core\RioJoy.Core.csproj" />
|
||||
<ProjectReference Include="..\RioJoy.Overlay\RioJoy.Overlay.csproj" Condition="'$(TargetFramework)' == 'net48'" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
|
||||
<PackageReference Include="PolySharp" Version="1.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -74,10 +74,12 @@ internal sealed class TrayApplicationContext : ApplicationContext
|
||||
RebuildEditMenu(editMenu);
|
||||
menu.Items.Add(editMenu);
|
||||
|
||||
#if !NET40 // wallpaper maker needs SkiaSharp — modern flavor only (XP applies pre-rendered)
|
||||
var wallpaperMenu = new ToolStripMenuItem("Wallpaper maker");
|
||||
wallpaperMenu.DropDownOpening += (_, _) => RebuildWallpaperMenu(wallpaperMenu);
|
||||
RebuildWallpaperMenu(wallpaperMenu);
|
||||
menu.Items.Add(wallpaperMenu);
|
||||
#endif
|
||||
|
||||
menu.Items.Add("Import .ini…", null, (_, _) => ImportIniProfiles());
|
||||
|
||||
@@ -155,6 +157,7 @@ internal sealed class TrayApplicationContext : ApplicationContext
|
||||
editMenu.DropDownItems.Add(new ToolStripMenuItem("New profile…", null, (_, _) => OpenEditor(NewProfile())));
|
||||
}
|
||||
|
||||
#if !NET40
|
||||
// Rebuild the "Wallpaper maker" submenu: one entry per profile.
|
||||
private void RebuildWallpaperMenu(ToolStripMenuItem wallpaperMenu)
|
||||
{
|
||||
@@ -205,6 +208,7 @@ internal sealed class TrayApplicationContext : ApplicationContext
|
||||
"RIOJoy", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create, register and persist a fresh empty profile with a unique name.
|
||||
private RioProfile NewProfile()
|
||||
|
||||
@@ -30,6 +30,22 @@ public static class WallpaperApplier
|
||||
if (!File.Exists(full))
|
||||
throw new FileNotFoundException("Wallpaper image not found.", full);
|
||||
|
||||
#if NET40
|
||||
// Windows XP's SPI_SETDESKWALLPAPER accepts only BMP. Wallpapers are
|
||||
// authored as PNG on a modern machine, so convert beside the source on
|
||||
// apply (idempotent: reuses an up-to-date .bmp).
|
||||
if (!full.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string bmp = Path.ChangeExtension(full, ".bmp");
|
||||
if (!File.Exists(bmp) || File.GetLastWriteTimeUtc(bmp) < File.GetLastWriteTimeUtc(full))
|
||||
{
|
||||
using var image = System.Drawing.Image.FromFile(full);
|
||||
image.Save(bmp, System.Drawing.Imaging.ImageFormat.Bmp);
|
||||
}
|
||||
full = bmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
return SystemParametersInfo(
|
||||
SPI_SETDESKWALLPAPER, 0, full, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user