This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
self',
|
||||
...
|
||||
}:
|
||||
{
|
||||
packages = {
|
||||
affinity-v3 = pkgs.callPackage ./package.nix {
|
||||
inherit inputs;
|
||||
};
|
||||
|
||||
default = self'.packages.affinity-v3;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
makeDesktopItem,
|
||||
lib,
|
||||
affinity-v3 ? null,
|
||||
}:
|
||||
{
|
||||
affinity-v3 = makeDesktopItem rec {
|
||||
desktopName = "Affinity";
|
||||
name = "affinity-v3";
|
||||
exec = "${lib.getExe affinity-v3} %U";
|
||||
icon = name;
|
||||
type = "Application";
|
||||
categories = [ "Graphics" ];
|
||||
keywords = [
|
||||
"Graphics"
|
||||
"2DGraphics"
|
||||
"RasterGraphics"
|
||||
"VectorGraphics"
|
||||
"image"
|
||||
"editor"
|
||||
"vector"
|
||||
"drawing"
|
||||
];
|
||||
mimeTypes = [
|
||||
"application/afphoto"
|
||||
"application/afdesign"
|
||||
];
|
||||
startupWMClass = "affinity.exe";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
fetchurl,
|
||||
pkgs,
|
||||
lib,
|
||||
}:
|
||||
rec {
|
||||
icon = fetchurl {
|
||||
url = "https://web.archive.org/web/20260310003857/https://static.canva.com/domain-assets/affinity/static/images/apple-touch-180x180-1.png";
|
||||
sha256 = "sha256-KLT9loFiD2y8uNXNzc27DZ4A73yEs6Ntr4h3WULG5HM=";
|
||||
};
|
||||
|
||||
iconPackage = pkgs.runCommand "affinity-v3-icons" { } ''
|
||||
mkdir -p $out/share/icons/hicolor/256x256/apps
|
||||
|
||||
${lib.getExe pkgs.imagemagick} \
|
||||
${icon} \
|
||||
-resize 256x256 \
|
||||
$out/share/icons/hicolor/256x256/apps/affinity-v3.png
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
symlinkJoin,
|
||||
callPackage,
|
||||
inputs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
wine-packages = callPackage ../wine/packages.nix {
|
||||
inherit inputs;
|
||||
};
|
||||
|
||||
apl-combined = callPackage ../apl/apl-combined.nix {
|
||||
src = inputs.plugin-loader-src;
|
||||
};
|
||||
|
||||
prefixBase = callPackage ../prefixWithAffinity.nix {
|
||||
inherit inputs wine-packages apl-combined;
|
||||
};
|
||||
|
||||
runner = callPackage ../runner/package.nix {
|
||||
inherit prefixBase inputs wine-packages;
|
||||
registry-patches = (callPackage ../registry-patches.nix { }).combined;
|
||||
name = "v3";
|
||||
};
|
||||
|
||||
desktop = callPackage ./desktopItems.nix {
|
||||
affinity-v3 = runner.package;
|
||||
};
|
||||
|
||||
icons = callPackage ./icons.nix { };
|
||||
icon-package = icons.iconPackage;
|
||||
in
|
||||
symlinkJoin {
|
||||
name = "Affinity v3";
|
||||
pname = "affinity-v3";
|
||||
paths = [
|
||||
runner.package
|
||||
desktop.affinity-v3
|
||||
icon-package
|
||||
];
|
||||
meta = {
|
||||
mainProgram = "affinity-v3";
|
||||
|
||||
description = "Affinity v3";
|
||||
license = lib.licenses.unfree;
|
||||
homepage = "https://affinity.serif.com/";
|
||||
platforms = [ "x86_64-linux" ];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
makeV2Package =
|
||||
name:
|
||||
(pkgs.callPackage ./package.nix {
|
||||
inherit inputs name;
|
||||
});
|
||||
in
|
||||
{
|
||||
packages = {
|
||||
affinity-photo = makeV2Package "Photo";
|
||||
affinity-designer = makeV2Package "Designer";
|
||||
affinity-publisher = makeV2Package "Publisher";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
makeDesktopItem,
|
||||
lib,
|
||||
photo ? null,
|
||||
publisher ? null,
|
||||
designer ? null,
|
||||
}:
|
||||
{
|
||||
photo = makeDesktopItem rec {
|
||||
desktopName = "Affinity Photo 2";
|
||||
name = "affinity-photo";
|
||||
exec = "${lib.getExe photo} %U";
|
||||
icon = name;
|
||||
type = "Application";
|
||||
categories = [ "Graphics" ];
|
||||
keywords = [
|
||||
"Graphics"
|
||||
"2DGraphics"
|
||||
"RasterGraphics"
|
||||
"image"
|
||||
"editor"
|
||||
"vector"
|
||||
"drawing"
|
||||
];
|
||||
mimeTypes = [ "application/afphoto" ];
|
||||
startupWMClass = "photo.exe";
|
||||
};
|
||||
designer = makeDesktopItem rec {
|
||||
desktopName = "Affinity Designer 2";
|
||||
name = "affinity-designer";
|
||||
exec = "${lib.getExe designer} %U";
|
||||
icon = name;
|
||||
type = "Application";
|
||||
categories = [ "Graphics" ];
|
||||
keywords = [
|
||||
"Graphics"
|
||||
"2DGraphics"
|
||||
"VectorGraphics"
|
||||
"image"
|
||||
"editor"
|
||||
"vector"
|
||||
"drawing"
|
||||
];
|
||||
mimeTypes = [ "application/afdesign" ];
|
||||
startupWMClass = "designer.exe";
|
||||
};
|
||||
publisher = makeDesktopItem rec {
|
||||
desktopName = "Affinity Publisher 2";
|
||||
name = "affinity-publisher";
|
||||
exec = "${lib.getExe publisher} %U";
|
||||
icon = name;
|
||||
type = "Application";
|
||||
categories = [ "Graphics" ];
|
||||
keywords = [
|
||||
"Graphics"
|
||||
"2DGraphics"
|
||||
"RasterGraphics"
|
||||
"VectorGraphics"
|
||||
"image"
|
||||
"editor"
|
||||
"vector"
|
||||
"drawing"
|
||||
];
|
||||
mimeTypes = [
|
||||
"application/afdesign"
|
||||
"application/afphoto"
|
||||
# Note that there is no mime type for a `.afpub` file
|
||||
];
|
||||
startupWMClass = "publisher.exe";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
fetchurl,
|
||||
pkgs,
|
||||
lib,
|
||||
}:
|
||||
rec {
|
||||
icons = {
|
||||
photo = fetchurl {
|
||||
url = "https://cdn.serif.com/store/img/logos/affinity-photo-2-020520191502.svg";
|
||||
sha256 = "04bnf12znp9dgkwpk46c01381cw298gq14ga7j7dwccyl3m556d7";
|
||||
};
|
||||
designer = fetchurl {
|
||||
url = "https://cdn.serif.com/store/img/logos/affinity-designer-2-020520191502.svg";
|
||||
sha256 = "025j4aq8py2a5r34hx39nlhdmps06z6506h7wxchyxbbz6xz3d11";
|
||||
};
|
||||
publisher = fetchurl {
|
||||
url = "https://cdn.serif.com/store/img/logos/affinity-publisher-2-020520191502.svg";
|
||||
sha256 = "16ghh3mc2vjhvphbyy9zgan066ccp3xj1ilb6z5lmvsb94dp37aj";
|
||||
};
|
||||
};
|
||||
|
||||
mkIconPackageFor =
|
||||
name:
|
||||
pkgs.runCommand "${name}-icons" { } ''
|
||||
mkdir -p $out/share/icons/hicolor/{256x256,scalable}/apps
|
||||
|
||||
cp ${icons.${lib.toLower name}} \
|
||||
$out/share/icons/hicolor/scalable/apps/affinity-${lib.toLower name}.svg
|
||||
|
||||
${lib.getExe pkgs.inkscape} \
|
||||
-w 256 -h 256 \
|
||||
${icons.${lib.toLower name}} \
|
||||
-o - > $out/share/icons/hicolor/256x256/apps/affinity-${lib.toLower name}.png
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
symlinkJoin,
|
||||
callPackage,
|
||||
lib,
|
||||
name,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
wine-packages = callPackage ../wine/packages.nix {
|
||||
inherit inputs;
|
||||
};
|
||||
|
||||
apl-combined = callPackage ../apl/apl-combined.nix {
|
||||
src = inputs.plugin-loader-src;
|
||||
};
|
||||
|
||||
prefixBase = callPackage ../prefixWithAffinity.nix {
|
||||
inherit inputs wine-packages apl-combined;
|
||||
v3 = false;
|
||||
};
|
||||
|
||||
runner = callPackage ../runner/package.nix {
|
||||
inherit
|
||||
prefixBase
|
||||
inputs
|
||||
wine-packages
|
||||
name
|
||||
;
|
||||
registry-patches = (callPackage ../registry-patches.nix { }).combined;
|
||||
};
|
||||
|
||||
desktop = callPackage ./desktopItems.nix {
|
||||
${lib.toLower name} = runner.package;
|
||||
};
|
||||
|
||||
icons = callPackage ./icons.nix { };
|
||||
icon-package = icons.mkIconPackageFor name;
|
||||
|
||||
# last version of affinity v2 released
|
||||
lastV2Version = "2.6.5";
|
||||
in
|
||||
symlinkJoin {
|
||||
name = "affinity-${lib.toLower name}-${lastV2Version}";
|
||||
pname = "affinity-${lib.toLower name}";
|
||||
paths = [
|
||||
runner.package
|
||||
desktop.${lib.toLower name}
|
||||
icon-package
|
||||
];
|
||||
meta = {
|
||||
mainProgram = "affinity-${lib.toLower name}";
|
||||
|
||||
description = "Affinity ${name} ${lastV2Version}";
|
||||
license = lib.licenses.unfree;
|
||||
homepage = "https://affinity.serif.com/";
|
||||
platforms = [ "x86_64-linux" ];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
callPackage,
|
||||
symlinkJoin,
|
||||
src,
|
||||
...
|
||||
}:
|
||||
let
|
||||
version = "unstable";
|
||||
|
||||
apl = callPackage ./apl.nix {
|
||||
inherit version src;
|
||||
};
|
||||
|
||||
bootstrap = callPackage ./bootstrap.nix {
|
||||
inherit version src;
|
||||
};
|
||||
in
|
||||
symlinkJoin {
|
||||
pname = "apl-combined";
|
||||
inherit version;
|
||||
paths = [
|
||||
apl
|
||||
bootstrap
|
||||
];
|
||||
|
||||
postBuild = ''
|
||||
mv $out/lib/AffinityPluginLoader/* $out
|
||||
rm -rf $out/lib
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
buildDotnetModule,
|
||||
src,
|
||||
version,
|
||||
...
|
||||
}:
|
||||
buildDotnetModule rec {
|
||||
pname = "AffinityPluginLoader";
|
||||
|
||||
inherit version src;
|
||||
|
||||
projectFile = "AffinityPluginLoader.sln";
|
||||
|
||||
patches = [ ./no-login.patch ];
|
||||
|
||||
nugetDeps = ./deps.json;
|
||||
|
||||
postInstall = ''
|
||||
pushd $out/lib/${pname}
|
||||
rm *.pdb
|
||||
rm *.config
|
||||
|
||||
mkdir -p ./apl/plugins
|
||||
mv ./WineFix.dll ./apl/plugins
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
pkgs,
|
||||
version,
|
||||
src,
|
||||
...
|
||||
}:
|
||||
pkgs.pkgsCross.mingwW64.stdenv.mkDerivation {
|
||||
pname = "affinity-bootstrap";
|
||||
|
||||
inherit version;
|
||||
|
||||
src = "${src}/AffinityBootstrap";
|
||||
|
||||
nativeBuildInputs = [ pkgs.wine64 ];
|
||||
|
||||
buildPhase = ''
|
||||
mkdir -p build
|
||||
|
||||
cat << 'EOF' > mscoree.def
|
||||
LIBRARY mscoree.dll
|
||||
EXPORTS
|
||||
CLRCreateInstance
|
||||
EOF
|
||||
|
||||
x86_64-w64-mingw32-dlltool -d mscoree.def -l build/libmscoree.a
|
||||
|
||||
$CC -shared -o build/AffinityBootstrap.dll bootstrap.c \
|
||||
-I${pkgs.wine64}/include/wine/windows \
|
||||
-Lbuild \
|
||||
-lole32 -loleaut32 -luuid -lmscoree
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/lib/AffinityPluginLoader
|
||||
cp build/AffinityBootstrap.dll $out/lib/AffinityPluginLoader
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
packages.apl-combined = pkgs.callPackage ./apl-combined.nix {
|
||||
src = inputs.plugin-loader-src;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"pname": "Lib.Harmony",
|
||||
"version": "2.4.2",
|
||||
"hash": "sha256-1kWS5TCQRkVZ/OSGEsnKfI3HMROEE3a3qjRV9G/F1Xk="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETFramework.ReferenceAssemblies",
|
||||
"version": "1.0.3",
|
||||
"hash": "sha256-FBoJP5DHZF0QHM0xLm9yd4HJZVQOuSpSKA+VQRpphEE="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETFramework.ReferenceAssemblies.net48",
|
||||
"version": "1.0.3",
|
||||
"hash": "sha256-in40hTjn65E1FpaRFon0nj1PY/i6tRdDK74Vm4sRBKI="
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,589 @@
|
||||
From 45ff4e8c5615af1e8f0a41e92580506788a10b24 Mon Sep 17 00:00:00 2001
|
||||
From: marshmallow <git@althaea.zone>
|
||||
Date: Mon, 13 Apr 2026 20:11:35 +1000
|
||||
Subject: [PATCH] Revert "Restore Canva sign-in: Textbox to capture
|
||||
authorization token (#44)"
|
||||
|
||||
This reverts commit 915e1cc5096475f433b8447fc7c6b7cb3b52b7d9.
|
||||
---
|
||||
WineFix/Patches/CanvaSignInPatch.cs | 435 -----------------------
|
||||
WineFix/Patches/MainWindowLoadedPatch.cs | 55 +++
|
||||
WineFix/WineFix.csproj | 4 -
|
||||
WineFix/WineFixPlugin.cs | 14 -
|
||||
4 files changed, 55 insertions(+), 453 deletions(-)
|
||||
delete mode 100644 WineFix/Patches/CanvaSignInPatch.cs
|
||||
|
||||
diff --git a/WineFix/Patches/CanvaSignInPatch.cs b/WineFix/Patches/CanvaSignInPatch.cs
|
||||
deleted file mode 100644
|
||||
index 7c05fcb..0000000
|
||||
--- a/WineFix/Patches/CanvaSignInPatch.cs
|
||||
+++ /dev/null
|
||||
@@ -1,435 +0,0 @@
|
||||
-using System;
|
||||
-using System.Globalization;
|
||||
-using System.Linq;
|
||||
-using System.Reflection;
|
||||
-using System.Windows;
|
||||
-using System.Windows.Controls;
|
||||
-using System.Windows.Data;
|
||||
-using System.Windows.Media;
|
||||
-using System.Windows.Media.Imaging;
|
||||
-using HarmonyLib;
|
||||
-using AffinityPluginLoader.Core;
|
||||
-
|
||||
-namespace WineFix.Patches
|
||||
-{
|
||||
- /// <summary>
|
||||
- /// Adds a paste-URL textbox to the Canva sign-in dialog so users can manually
|
||||
- /// paste the authentication redirect URL from their browser, bypassing the need
|
||||
- /// for the affinity:// protocol handler which doesn't work under Wine.
|
||||
- /// </summary>
|
||||
- public static class CanvaSignInPatch
|
||||
- {
|
||||
- private static FieldInfo _authHookField;
|
||||
- private static object _cloudServicesService;
|
||||
- private static Border _overlayPanel;
|
||||
-
|
||||
- public static void ApplyPatches(Harmony harmony)
|
||||
- {
|
||||
- Logger.Info("Applying CanvaSignIn patch...");
|
||||
-
|
||||
- var serifAffinity = AppDomain.CurrentDomain.GetAssemblies()
|
||||
- .FirstOrDefault(a => a.GetName().Name == "Serif.Affinity");
|
||||
- var serifInterop = AppDomain.CurrentDomain.GetAssemblies()
|
||||
- .FirstOrDefault(a => a.GetName().Name == "Serif.Interop.Persona");
|
||||
-
|
||||
- if (serifAffinity == null || serifInterop == null)
|
||||
- {
|
||||
- Logger.Error("Required assemblies not found");
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- // Resolve the AuthHookReceived backing field on CloudServicesService
|
||||
- var cssType = serifInterop.GetType("Serif.Interop.Persona.Services.CloudServicesService");
|
||||
- _authHookField = cssType?.GetField("<backing_store>AuthHookReceived",
|
||||
- BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
-
|
||||
- if (_authHookField == null)
|
||||
- {
|
||||
- // Try the mangled name from decompilation
|
||||
- _authHookField = cssType?.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
- .FirstOrDefault(f => f.Name.Contains("AuthHookReceived") && f.FieldType == typeof(Action<Uri>));
|
||||
- }
|
||||
-
|
||||
- if (_authHookField == null)
|
||||
- {
|
||||
- Logger.Error("Could not find AuthHookReceived backing field");
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- // Patch ProductKeyDialog_Loaded to inject our UI
|
||||
- var dialogType = serifAffinity.GetType("Serif.Affinity.UI.ProductKeyDialog");
|
||||
- var loadedMethod = dialogType?.GetMethod("ProductKeyDialog_Loaded",
|
||||
- BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
-
|
||||
- if (loadedMethod == null)
|
||||
- {
|
||||
- Logger.Error("ProductKeyDialog_Loaded not found");
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- harmony.Patch(loadedMethod,
|
||||
- postfix: new HarmonyMethod(typeof(CanvaSignInPatch), nameof(OnDialogLoaded)));
|
||||
- Logger.Info("Patched ProductKeyDialog_Loaded");
|
||||
- }
|
||||
-
|
||||
- public static void OnDialogLoaded(object __instance)
|
||||
- {
|
||||
- try
|
||||
- {
|
||||
- var window = __instance as Window;
|
||||
- if (window == null) return;
|
||||
-
|
||||
- // Get CloudServicesService via Application.Current.GetService<T>()
|
||||
- var app = Application.Current;
|
||||
- var getServiceMethod = app.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
- .Where(m => m.Name == "GetService" && m.IsGenericMethod)
|
||||
- .FirstOrDefault();
|
||||
-
|
||||
- if (getServiceMethod != null)
|
||||
- {
|
||||
- var cssType = _authHookField.DeclaringType;
|
||||
- var bound = getServiceMethod.MakeGenericMethod(cssType);
|
||||
- _cloudServicesService = bound.Invoke(app, null);
|
||||
- }
|
||||
-
|
||||
- // Listen for DataContext property changes to detect state transitions
|
||||
- var model = ((FrameworkElement)window).DataContext;
|
||||
- if (model is System.ComponentModel.INotifyPropertyChanged npc)
|
||||
- {
|
||||
- npc.PropertyChanged += (s, e) =>
|
||||
- {
|
||||
- if (e.PropertyName == "State")
|
||||
- InjectPasteUI(window);
|
||||
- };
|
||||
- }
|
||||
-
|
||||
- // Also try immediately in case we're already in SigningIn
|
||||
- InjectPasteUI(window);
|
||||
- }
|
||||
- catch (Exception ex)
|
||||
- {
|
||||
- Logger.Error($"OnDialogLoaded failed: {ex.Message}");
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- private static void InjectPasteUI(Window window)
|
||||
- {
|
||||
- try
|
||||
- {
|
||||
- var model = ((FrameworkElement)window).DataContext;
|
||||
- var stateProp = model.GetType().GetProperty("State");
|
||||
- if (stateProp == null) return;
|
||||
-
|
||||
- var state = stateProp.GetValue(model);
|
||||
- if (state.ToString() == "SigningIn")
|
||||
- {
|
||||
- window.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Loaded,
|
||||
- new Action(() => InjectPasteUIImpl(window)));
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- HideOverlay();
|
||||
- }
|
||||
- }
|
||||
- catch (Exception ex)
|
||||
- {
|
||||
- Logger.Error($"InjectPasteUI failed: {ex.Message}");
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- private static void InjectPasteUIImpl(Window window)
|
||||
- {
|
||||
- try
|
||||
- {
|
||||
- if (_overlayPanel != null)
|
||||
- {
|
||||
- _overlayPanel.Visibility = Visibility.Visible;
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- var existingContent = window.Content as UIElement;
|
||||
- if (existingContent == null) return;
|
||||
-
|
||||
- // Wrap existing window content in a new Grid so we can overlay on top
|
||||
- var wrapperGrid = new Grid();
|
||||
- window.Content = wrapperGrid;
|
||||
- wrapperGrid.Children.Add(existingContent);
|
||||
-
|
||||
- // Build overlay content
|
||||
- var panel = new StackPanel
|
||||
- {
|
||||
- VerticalAlignment = VerticalAlignment.Center,
|
||||
- Margin = new Thickness(30)
|
||||
- };
|
||||
-
|
||||
- var heading = new TextBlock
|
||||
- {
|
||||
- Text = "Running under Wine?",
|
||||
- FontSize = 14,
|
||||
- FontWeight = FontWeights.SemiBold,
|
||||
- Foreground = new SolidColorBrush(Color.FromRgb(0x33, 0x33, 0x33)),
|
||||
- Margin = new Thickness(0, 0, 0, 8)
|
||||
- };
|
||||
-
|
||||
- var instructions = new TextBlock
|
||||
- {
|
||||
- Text = "After signing in, your browser will show a " +
|
||||
- "\"Launching Affinity\" page. Copy the full URL " +
|
||||
- "from the address bar and paste it below:",
|
||||
- Foreground = new SolidColorBrush(Color.FromRgb(0x65, 0x65, 0x65)),
|
||||
- FontSize = 12,
|
||||
- TextWrapping = TextWrapping.Wrap,
|
||||
- Margin = new Thickness(0, 0, 0, 10)
|
||||
- };
|
||||
-
|
||||
- var screenshot = LoadEmbeddedImage("WineFix.Resources.signin.png");
|
||||
- Image screenshotImage = null;
|
||||
- if (screenshot != null)
|
||||
- {
|
||||
- screenshotImage = new Image
|
||||
- {
|
||||
- Source = screenshot,
|
||||
- Stretch = Stretch.Uniform,
|
||||
- HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
- Margin = new Thickness(0, 0, 0, 12)
|
||||
- };
|
||||
- }
|
||||
-
|
||||
- var textBox = new TextBox
|
||||
- {
|
||||
- Height = 28,
|
||||
- FontSize = 12,
|
||||
- VerticalContentAlignment = VerticalAlignment.Center,
|
||||
- HorizontalAlignment = HorizontalAlignment.Stretch
|
||||
- };
|
||||
-
|
||||
- var placeholder = new TextBlock
|
||||
- {
|
||||
- Text = "https://page.service.serif.com/canva-auth-redirect/...",
|
||||
- Foreground = new SolidColorBrush(Color.FromRgb(0xAA, 0xAA, 0xAA)),
|
||||
- FontSize = 12,
|
||||
- IsHitTestVisible = false,
|
||||
- VerticalAlignment = VerticalAlignment.Center,
|
||||
- Margin = new Thickness(4, 0, 0, 0)
|
||||
- };
|
||||
- var textBoxContainer = new Grid { Height = 28, HorizontalAlignment = HorizontalAlignment.Stretch };
|
||||
- textBoxContainer.Children.Add(textBox);
|
||||
- textBoxContainer.Children.Add(placeholder);
|
||||
- textBox.TextChanged += (s, e) => placeholder.Visibility =
|
||||
- string.IsNullOrEmpty(textBox.Text) ? Visibility.Visible : Visibility.Collapsed;
|
||||
- textBox.GotFocus += (s, e) => placeholder.Visibility = Visibility.Collapsed;
|
||||
- textBox.LostFocus += (s, e) => placeholder.Visibility =
|
||||
- string.IsNullOrEmpty(textBox.Text) ? Visibility.Visible : Visibility.Collapsed;
|
||||
-
|
||||
- var button = new Button
|
||||
- {
|
||||
- Content = "Submit",
|
||||
- Margin = new Thickness(0, 8, 0, 0),
|
||||
- Padding = new Thickness(0, 8, 0, 8),
|
||||
- HorizontalAlignment = HorizontalAlignment.Stretch
|
||||
- };
|
||||
-
|
||||
- var errorText = new TextBlock
|
||||
- {
|
||||
- Foreground = Brushes.Red,
|
||||
- FontSize = 11,
|
||||
- TextWrapping = TextWrapping.Wrap,
|
||||
- Visibility = Visibility.Collapsed,
|
||||
- Margin = new Thickness(0, 4, 0, 0)
|
||||
- };
|
||||
-
|
||||
- button.Click += (s, e) => OnSubmitUrl(textBox.Text, errorText);
|
||||
-
|
||||
- panel.Children.Add(heading);
|
||||
- panel.Children.Add(instructions);
|
||||
- if (screenshotImage != null)
|
||||
- panel.Children.Add(screenshotImage);
|
||||
- panel.Children.Add(textBoxContainer);
|
||||
- panel.Children.Add(button);
|
||||
- panel.Children.Add(errorText);
|
||||
-
|
||||
- // Overlay: right-aligned, 50% width, full height, white background
|
||||
- _overlayPanel = new Border
|
||||
- {
|
||||
- Background = new SolidColorBrush(Color.FromArgb(0xF0, 0xFF, 0xFF, 0xFF)),
|
||||
- HorizontalAlignment = HorizontalAlignment.Right,
|
||||
- VerticalAlignment = VerticalAlignment.Stretch,
|
||||
- Child = panel
|
||||
- };
|
||||
- _overlayPanel.SetBinding(FrameworkElement.WidthProperty,
|
||||
- new Binding("ActualWidth")
|
||||
- {
|
||||
- Source = window,
|
||||
- Converter = new HalfWidthConverter()
|
||||
- });
|
||||
-
|
||||
- wrapperGrid.Children.Add(_overlayPanel);
|
||||
-
|
||||
- // Nudge the "Back" button left so it's not covered by the overlay
|
||||
- var contentControl = FindChild<ContentControl>(existingContent,
|
||||
- c => c.ContentTemplateSelector != null);
|
||||
- if (contentControl != null)
|
||||
- {
|
||||
- var backButton = FindChild<Button>(contentControl, b =>
|
||||
- Grid.GetRow(b) == 4 && Grid.GetColumnSpan(b) == 3
|
||||
- && b.HorizontalAlignment == HorizontalAlignment.Center);
|
||||
- if (backButton != null)
|
||||
- backButton.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
- }
|
||||
-
|
||||
- Logger.Info("Injected paste-URL overlay into SigningIn dialog");
|
||||
- }
|
||||
- catch (Exception ex)
|
||||
- {
|
||||
- Logger.Error($"InjectPasteUIImpl failed: {ex.Message}");
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- private static void HideOverlay()
|
||||
- {
|
||||
- if (_overlayPanel != null)
|
||||
- _overlayPanel.Visibility = Visibility.Collapsed;
|
||||
- }
|
||||
-
|
||||
- private static void OnSubmitUrl(string input, TextBlock errorText)
|
||||
- {
|
||||
- try
|
||||
- {
|
||||
- errorText.Visibility = Visibility.Collapsed;
|
||||
-
|
||||
- if (string.IsNullOrWhiteSpace(input))
|
||||
- {
|
||||
- ShowError(errorText, "Please paste a URL.");
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- var affinityUri = ExtractAffinityUri(input.Trim());
|
||||
- if (affinityUri == null)
|
||||
- {
|
||||
- ShowError(errorText, "Could not find an authentication URL. Please paste the full URL from your browser's address bar after signing in.");
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- if (_cloudServicesService == null || _authHookField == null)
|
||||
- {
|
||||
- ShowError(errorText, "Internal error: cloud services not available.");
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- var handler = _authHookField.GetValue(_cloudServicesService) as Action<Uri>;
|
||||
- if (handler == null)
|
||||
- {
|
||||
- ShowError(errorText, "Internal error: auth hook not registered.");
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- Logger.Info($"Invoking AuthHookReceived with: {affinityUri}");
|
||||
- handler.Invoke(affinityUri);
|
||||
- }
|
||||
- catch (Exception ex)
|
||||
- {
|
||||
- Logger.Error($"OnSubmitUrl failed: {ex}");
|
||||
- ShowError(errorText, $"Error: {ex.Message}");
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- /// <summary>
|
||||
- /// Extracts the affinity:// URI from either:
|
||||
- /// - A direct affinity://canva/authorize?... URL
|
||||
- /// - A redirect page URL: https://page.service.serif.com/canva-auth-redirect/?url=affinity%3A%2F%2F...
|
||||
- /// </summary>
|
||||
- private static Uri ExtractAffinityUri(string input)
|
||||
- {
|
||||
- if (input.StartsWith("affinity://", StringComparison.OrdinalIgnoreCase))
|
||||
- return new Uri(input);
|
||||
-
|
||||
- try
|
||||
- {
|
||||
- var uri = new Uri(input);
|
||||
- var query = uri.Query;
|
||||
- if (string.IsNullOrEmpty(query)) return null;
|
||||
-
|
||||
- var decoded = ParseQueryParam(query, "url");
|
||||
- if (decoded != null && decoded.StartsWith("affinity://", StringComparison.OrdinalIgnoreCase))
|
||||
- return new Uri(decoded);
|
||||
- }
|
||||
- catch { }
|
||||
-
|
||||
- return null;
|
||||
- }
|
||||
-
|
||||
- private static string ParseQueryParam(string query, string name)
|
||||
- {
|
||||
- if (query.StartsWith("?")) query = query.Substring(1);
|
||||
-
|
||||
- foreach (var part in query.Split('&'))
|
||||
- {
|
||||
- var eq = part.IndexOf('=');
|
||||
- if (eq < 0) continue;
|
||||
- var key = Uri.UnescapeDataString(part.Substring(0, eq));
|
||||
- if (string.Equals(key, name, StringComparison.OrdinalIgnoreCase))
|
||||
- return Uri.UnescapeDataString(part.Substring(eq + 1));
|
||||
- }
|
||||
- return null;
|
||||
- }
|
||||
-
|
||||
- private static void ShowError(TextBlock tb, string msg)
|
||||
- {
|
||||
- tb.Text = msg;
|
||||
- tb.Visibility = Visibility.Visible;
|
||||
- }
|
||||
-
|
||||
- private static BitmapImage LoadEmbeddedImage(string resourceName)
|
||||
- {
|
||||
- try
|
||||
- {
|
||||
- var assembly = typeof(CanvaSignInPatch).Assembly;
|
||||
- using (var stream = assembly.GetManifestResourceStream(resourceName))
|
||||
- {
|
||||
- if (stream == null) return null;
|
||||
- var bitmap = new BitmapImage();
|
||||
- bitmap.BeginInit();
|
||||
- bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||
- bitmap.StreamSource = stream;
|
||||
- bitmap.EndInit();
|
||||
- bitmap.Freeze();
|
||||
- return bitmap;
|
||||
- }
|
||||
- }
|
||||
- catch (Exception ex)
|
||||
- {
|
||||
- Logger.Error($"Failed to load embedded image: {ex.Message}");
|
||||
- return null;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- private static T FindChild<T>(DependencyObject parent, Func<T, bool> predicate = null)
|
||||
- where T : DependencyObject
|
||||
- {
|
||||
- var count = VisualTreeHelper.GetChildrenCount(parent);
|
||||
- for (int i = 0; i < count; i++)
|
||||
- {
|
||||
- var child = VisualTreeHelper.GetChild(parent, i);
|
||||
- if (child is T t && (predicate == null || predicate(t)))
|
||||
- return t;
|
||||
- var result = FindChild(child, predicate);
|
||||
- if (result != null) return result;
|
||||
- }
|
||||
- return null;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- internal class HalfWidthConverter : IValueConverter
|
||||
- {
|
||||
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
- {
|
||||
- if (value is double d) return d * 0.5;
|
||||
- return value;
|
||||
- }
|
||||
-
|
||||
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
- {
|
||||
- throw new NotImplementedException();
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
diff --git a/WineFix/Patches/MainWindowLoadedPatch.cs b/WineFix/Patches/MainWindowLoadedPatch.cs
|
||||
index 5c658b5..8e32a33 100644
|
||||
--- a/WineFix/Patches/MainWindowLoadedPatch.cs
|
||||
+++ b/WineFix/Patches/MainWindowLoadedPatch.cs
|
||||
@@ -52,10 +52,12 @@ namespace WineFix.Patches
|
||||
}
|
||||
|
||||
// Transpiler that replaces the call to HasPreviousPackageInstalled() with loading 'false'
|
||||
+ // and also removes the call to base.OnMainWindowLoaded()
|
||||
public static IEnumerable<CodeInstruction> OnMainWindowLoaded_Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
var codes = new List<CodeInstruction>(instructions);
|
||||
bool patchedPackageCheck = false;
|
||||
+ bool patchedBaseCall = false;
|
||||
|
||||
for (int i = 0; i < codes.Count; i++)
|
||||
{
|
||||
@@ -97,6 +99,54 @@ namespace WineFix.Patches
|
||||
patchedPackageCheck = true;
|
||||
continue;
|
||||
}
|
||||
+
|
||||
+ // Look for: call instance void Serif.Interop.Persona.Application::OnMainWindowLoaded(...)
|
||||
+ // (Index 22 in the IL - base class is Serif.Interop.Persona.Application, not System.Windows.Application!)
|
||||
+ if (!patchedBaseCall &&
|
||||
+ instruction.opcode == OpCodes.Call &&
|
||||
+ instruction.operand is MethodInfo baseMethod &&
|
||||
+ baseMethod.Name == "OnMainWindowLoaded" &&
|
||||
+ baseMethod.DeclaringType != null &&
|
||||
+ (baseMethod.DeclaringType.FullName == "Serif.Interop.Persona.Application" ||
|
||||
+ baseMethod.DeclaringType.FullName == "System.Windows.Application"))
|
||||
+ {
|
||||
+ Logger.Debug($"Found base.OnMainWindowLoaded call at instruction {i}");
|
||||
+
|
||||
+ // The IL sequence is (indices 20-22):
|
||||
+ // ldarg.0 ; load 'this'
|
||||
+ // ldarg.1 ; load 'mainWindow'
|
||||
+ // call base.OnMainWindowLoaded
|
||||
+ // Replace all three with NOPs, preserving labels
|
||||
+ if (i >= 2 &&
|
||||
+ codes[i - 2].opcode == OpCodes.Ldarg_0 &&
|
||||
+ codes[i - 1].opcode == OpCodes.Ldarg_1)
|
||||
+ {
|
||||
+ // Create NOPs but preserve labels
|
||||
+ var nop1 = new CodeInstruction(OpCodes.Nop);
|
||||
+ nop1.labels.AddRange(codes[i - 2].labels);
|
||||
+ codes[i - 2] = nop1;
|
||||
+
|
||||
+ var nop2 = new CodeInstruction(OpCodes.Nop);
|
||||
+ nop2.labels.AddRange(codes[i - 1].labels);
|
||||
+ codes[i - 1] = nop2;
|
||||
+
|
||||
+ var nop3 = new CodeInstruction(OpCodes.Nop);
|
||||
+ nop3.labels.AddRange(codes[i].labels);
|
||||
+ codes[i] = nop3;
|
||||
+
|
||||
+ Logger.Debug($"Removed base.OnMainWindowLoaded call (indices {i-2} to {i})");
|
||||
+ patchedBaseCall = true;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ Logger.Warning($"WARNING: Found base.OnMainWindowLoaded but preceding instructions don't match expected pattern");
|
||||
+ var nop = new CodeInstruction(OpCodes.Nop);
|
||||
+ nop.labels.AddRange(codes[i].labels);
|
||||
+ codes[i] = nop;
|
||||
+ patchedBaseCall = true;
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
}
|
||||
|
||||
if (!patchedPackageCheck)
|
||||
@@ -104,6 +154,11 @@ namespace WineFix.Patches
|
||||
Logger.Warning($"WARNING: Could not find HasPreviousPackageInstalled call to patch");
|
||||
}
|
||||
|
||||
+ if (!patchedBaseCall)
|
||||
+ {
|
||||
+ Logger.Warning($"WARNING: Could not find base.OnMainWindowLoaded call to patch");
|
||||
+ }
|
||||
+
|
||||
return codes.AsEnumerable();
|
||||
}
|
||||
}
|
||||
diff --git a/WineFix/WineFix.csproj b/WineFix/WineFix.csproj
|
||||
index b1d3c82..c62939d 100644
|
||||
--- a/WineFix/WineFix.csproj
|
||||
+++ b/WineFix/WineFix.csproj
|
||||
@@ -29,10 +29,6 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
- <Reference Include="System.Xaml" />
|
||||
- </ItemGroup>
|
||||
- <ItemGroup>
|
||||
- <EmbeddedResource Include="Resources\signin.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AffinityPluginLoader\AffinityPluginLoader.csproj">
|
||||
diff --git a/WineFix/WineFixPlugin.cs b/WineFix/WineFixPlugin.cs
|
||||
index 4ed9f42..a6ccd09 100644
|
||||
--- a/WineFix/WineFixPlugin.cs
|
||||
+++ b/WineFix/WineFixPlugin.cs
|
||||
@@ -25,10 +25,6 @@ namespace WineFix
|
||||
{
|
||||
return new PluginSettingsDefinition(PluginId)
|
||||
.AddSection("Patches")
|
||||
- .AddBool(SettingCanvaSignInHelper, "Canva sign-in helper",
|
||||
- defaultValue: true,
|
||||
- restartRequired: true,
|
||||
- description: "Patch the Canva sign-in dialog to include a helper textbox input and instructions to complete Canva sign-in without needing a protocol URL handler.")
|
||||
.AddBool(BezierRenderingFixKey, "Bezier curves: Fix tool preview path rendering",
|
||||
defaultValue: true,
|
||||
restartRequired: true,
|
||||
@@ -109,16 +105,6 @@ namespace WineFix
|
||||
{
|
||||
Logger.Info("Skipping ColorPicker Wayland fix (setting: " + magnifierFix + ")");
|
||||
}
|
||||
-
|
||||
- if (context.Settings.GetEffectiveValue<bool>(SettingCanvaSignInHelper))
|
||||
- {
|
||||
- context.Patch("Canva sign-in paste URL fix",
|
||||
- h => Patches.CanvaSignInPatch.ApplyPatches(h));
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- Logger.Info("Skipping Canva sign-in helper (disabled in settings)");
|
||||
- }
|
||||
}
|
||||
}
|
||||
}
|
||||
--
|
||||
2.53.0
|
||||
@@ -0,0 +1,193 @@
|
||||
{
|
||||
linkFarm,
|
||||
fetchurl,
|
||||
writeText,
|
||||
runCommand,
|
||||
callPackage,
|
||||
xvfb-run,
|
||||
lib,
|
||||
inputs,
|
||||
wine-packages,
|
||||
...
|
||||
}:
|
||||
let
|
||||
verbs = [
|
||||
"tahoma"
|
||||
"vcrun2022"
|
||||
#"dotnet20"
|
||||
"dotnet48"
|
||||
"corefonts"
|
||||
"win11"
|
||||
];
|
||||
|
||||
inherit (wine-packages)
|
||||
wine
|
||||
winetricks
|
||||
wineserver
|
||||
;
|
||||
|
||||
dependencies = callPackage ./dependencies.nix { };
|
||||
winetricksCache = linkFarm "winetricks-cache" [
|
||||
{
|
||||
name = "vcrun2022/vc_redist.x64.exe";
|
||||
path = fetchurl {
|
||||
urls = [
|
||||
"https://download.visualstudio.microsoft.com/download/pr/7ebf5fdb-36dc-4145-b0a0-90d3d5990a61/CC0FF0EB1DC3F5188AE6300FAEF32BF5BEEBA4BDD6E8E445A9184072096B713B/VC_redist.x64.exe"
|
||||
];
|
||||
hash = "sha256-zA/w6x3D9RiK5jAPrvMr9b7rpL3W6ORFqRhAcglrcTs=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "vcrun2022/vc_redist.x86.exe";
|
||||
path = fetchurl {
|
||||
urls = [
|
||||
"https://download.visualstudio.microsoft.com/download/pr/7ebf5fdb-36dc-4145-b0a0-90d3d5990a61/0C09F2611660441084CE0DF425C51C11E147E6447963C3690F97E0B25C55ED64/VC_redist.x86.exe"
|
||||
];
|
||||
hash = "sha256-DAnyYRZgRBCEzg30JcUcEeFH5kR5Y8NpD5fgslxV7WQ=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "dotnet48/ndp48-x86-x64-allos-enu.exe";
|
||||
path = fetchurl {
|
||||
urls = [
|
||||
"https://download.visualstudio.microsoft.com/download/pr/7afca223-55d2-470a-8edc-6a1739ae3252/abd170b4b0ec15ad0222a809b761a036/ndp48-x86-x64-allos-enu.exe"
|
||||
];
|
||||
hash = "sha256-lYidbePyBwwHeQrWzyAA0z2aG9/Go4FyWrgqscMU/VM=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "dotnet40/dotNetFx40_Full_x86_x64.exe";
|
||||
path = fetchurl {
|
||||
urls = [
|
||||
"https://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe"
|
||||
];
|
||||
hash = "sha256-ZeBkJY8uQYgWswT2Rv+eh68QHkyVUqsGS7dNKBw4ZZ8=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "tahoma/IELPKTH.CAB";
|
||||
path = fetchurl {
|
||||
url = "https://downloads.sourceforge.net/corefonts/OldFiles/IELPKTH.CAB";
|
||||
hash = "sha256-wb4/uPAEJXC+duxtqgOpkULIg2fBvIECQLhYJ8cVlho=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "dotnet20/NetFx64.exe";
|
||||
path = fetchurl {
|
||||
urls = [
|
||||
"https://download.microsoft.com/download/9/8/6/98610406-c2b7-45a4-bdc3-9db1b1c5f7e2/NetFx20SP1_x64.exe"
|
||||
];
|
||||
hash = "sha256-FzHlPeX0i6rgljZ3JXZg3xMpVJ6BxItNfbf38/Iymqs=";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
layer_1 = runCommand "base-prefix-1" { } ''
|
||||
set -x -e
|
||||
mkdir -p $out
|
||||
export WINEPREFIX="$out"
|
||||
export WINETRICKS_UPDATE_CHECK=0
|
||||
export WINETRICKS_LATEST_VERSION_CHECK=disabled
|
||||
|
||||
mkdir -p /tmp/cache
|
||||
export XDG_CACHE_HOME="/tmp/cache"
|
||||
|
||||
${lib.getExe wine} wineboot --update
|
||||
|
||||
# by diffing a registry dump we found that you can disable the file association
|
||||
# through a registry key.
|
||||
${lib.getExe wine} regedit /S "${(writeText "file-association-disable.reg" ''
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Wine\DllOverrides]
|
||||
"winemenubuilder.exe"=""
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Wine\FileOpenAssociations]
|
||||
"Enable"="N"
|
||||
'').outPath}"
|
||||
|
||||
${lib.getExe winetricks} renderer=vulkan
|
||||
|
||||
install -D -t "$WINEPREFIX/drive_c/windows/system32/WinMetadata/" ${dependencies}/*.winmd
|
||||
|
||||
${lib.getExe wineserver} -w
|
||||
'';
|
||||
|
||||
layer_2 =
|
||||
runCommand "base-prefix-2"
|
||||
{
|
||||
nativeBuildInputs = [
|
||||
xvfb-run
|
||||
];
|
||||
}
|
||||
''
|
||||
set -x -e
|
||||
|
||||
mkdir -p $out
|
||||
cp -a ${layer_1}/. $out
|
||||
chmod -R +w $out
|
||||
export WINEPREFIX="$out"
|
||||
|
||||
mkdir -p /tmp/cache
|
||||
export XDG_CACHE_HOME="/tmp/cache"
|
||||
|
||||
${lib.getExe wine} winecfg -v win7
|
||||
xvfb-run ${lib.getExe wine} "${dependencies}/MicrosoftEdgeWebView2RuntimeInstallerX64.exe" /silent /install
|
||||
${lib.getExe wine} winecfg -v win11
|
||||
|
||||
${lib.getExe wine} regedit /S "${(writeText "webview2-regedit-changes.reg" ''
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\edgeupdate]
|
||||
"Start"=dword:00000004
|
||||
|
||||
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\edgeupdatem]
|
||||
"Start"=dword:00000004
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Wine\AppDefaults]
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Wine\AppDefaults\msedgewebview2.exe]
|
||||
"Version"="win7"
|
||||
'').outPath}"
|
||||
|
||||
# The Edge Update service gets to start before we can deactivate it, so it must be stopped manually
|
||||
${lib.getExe wine} taskkill /f /im MicrosoftEdgeUpdate.exe
|
||||
|
||||
${lib.getExe wineserver} -w
|
||||
'';
|
||||
in
|
||||
runCommand "base-prefix-3"
|
||||
{
|
||||
nativeBuildInputs = [
|
||||
xvfb-run
|
||||
];
|
||||
}
|
||||
''
|
||||
set -x -e
|
||||
|
||||
${lib.strings.toShellVars {
|
||||
inherit verbs;
|
||||
}}
|
||||
|
||||
mkdir -p /tmp/cache/winetricks/corefonts
|
||||
|
||||
mkdir -p $out
|
||||
cp -a ${layer_2}/. $out
|
||||
chmod -R +w $out
|
||||
export WINEPREFIX="$out"
|
||||
|
||||
export XDG_CACHE_HOME="/tmp/cache"
|
||||
export WINETRICKS_UPDATE_CHECK=0
|
||||
export WINETRICKS_LATEST_VERSION_CHECK=disabled
|
||||
|
||||
export WINEDEBUG=-all
|
||||
export WINEESYNC=0
|
||||
export WINEFSYNC=0
|
||||
|
||||
cp -R ${winetricksCache}/* /tmp/cache/winetricks
|
||||
cp -R ${inputs.corefonts}/*.exe /tmp/cache/winetricks/corefonts
|
||||
|
||||
xvfb-run ${lib.getExe winetricks} -q -f "''${verbs[@]}"
|
||||
|
||||
${lib.getExe wineserver} -w
|
||||
''
|
||||
@@ -0,0 +1,28 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./wine
|
||||
./affinity
|
||||
./affinity-v3
|
||||
./apl
|
||||
./runner
|
||||
];
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
wine-packages,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# this function builds the prefix right up until the affinity sources
|
||||
# are used. this separation exists for build caching, as the affinity sources
|
||||
# should never be exposed to a cache
|
||||
packages.base-prefix-pre-affinity = pkgs.callPackage ./basePrefix.nix {
|
||||
inherit inputs wine-packages;
|
||||
};
|
||||
|
||||
_module.args = {
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
fetchurl,
|
||||
lib,
|
||||
runCommand,
|
||||
}:
|
||||
let
|
||||
paths = {
|
||||
"Windows.Services.winmd" = fetchurl {
|
||||
url = "https://archive.org/download/windows.services-system.winmd/Windows.Services.winmd";
|
||||
hash = "sha256-l9TRXyyacQFHoVsq6s8zGvJ+SLl4pTNv6yesNksAzdQ=";
|
||||
};
|
||||
"Windows.System.winmd" = fetchurl {
|
||||
url = "https://archive.org/download/windows.services-system.winmd/Windows.System.winmd";
|
||||
hash = "sha256-EOh5DJXzF+gdqW1Jc4lS1/J2x3hC/SxRkHMzzS4h0TM=";
|
||||
};
|
||||
"MicrosoftEdgeWebView2RuntimeInstallerX64.exe" = fetchurl {
|
||||
url = "https://archive.org/download/microsoft-edge-web-view-2-runtime-installer-v109.0.1518.78/MicrosoftEdgeWebView2RuntimeInstallerX64.exe";
|
||||
hash = "sha256-8sxJhj4iFALUZk2fqUSkfyJUPaLcs2NDjD5Zh4m5/Vs=";
|
||||
};
|
||||
};
|
||||
in
|
||||
runCommand "dependencies" { } ''
|
||||
mkdir -p $out
|
||||
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (name: content: ''
|
||||
ln -s ${content} $out/${name}
|
||||
'') paths
|
||||
)}
|
||||
''
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
callPackage,
|
||||
fetchzip,
|
||||
runCommand,
|
||||
lndir,
|
||||
zstd,
|
||||
lib,
|
||||
|
||||
inputs,
|
||||
v3 ? true,
|
||||
wine-packages,
|
||||
apl-combined,
|
||||
...
|
||||
}:
|
||||
let
|
||||
layer_3 = callPackage ./basePrefix.nix {
|
||||
inherit inputs wine-packages;
|
||||
};
|
||||
|
||||
installers = callPackage ./sources.nix { };
|
||||
registry-patches = callPackage ./registry-patches.nix { };
|
||||
|
||||
vkd3d = fetchzip {
|
||||
url = "https://github.com/HansKristian-Work/vkd3d-proton/releases/download/v3.0.1/vkd3d-proton-3.0.1.tar.zst";
|
||||
nativeBuildInputs = [ zstd ];
|
||||
hash = "sha256-xHJFtVDD/ZHVHF2Fn7TEEX0fMUWJvujNyNt2Xyw9F7o=";
|
||||
};
|
||||
|
||||
inherit (wine-packages) wine wineserver;
|
||||
in
|
||||
runCommand "base-prefix-4" { } ''
|
||||
set -x -e
|
||||
|
||||
mkdir -p $out
|
||||
cp -a ${layer_3}/. $out
|
||||
chmod -R +w $out
|
||||
export WINEPREFIX="$out"
|
||||
|
||||
cp ${vkd3d}/x64/d3d12.dll "$WINEPREFIX/drive_c/windows/system32"
|
||||
cp ${vkd3d}/x64/d3d12core.dll "$WINEPREFIX/drive_c/windows/system32"
|
||||
|
||||
${lib.getExe wine} regedit /S "${registry-patches.one-vkd3d}"
|
||||
|
||||
${lib.optionalString v3 ''
|
||||
${lib.getExe lndir} ${installers.v3} "$WINEPREFIX/drive_c/Program Files/"
|
||||
|
||||
pushd "$WINEPREFIX/drive_c/Program Files/Affinity/Affinity"
|
||||
cp -r "${apl-combined}/." .
|
||||
popd
|
||||
''}
|
||||
|
||||
${lib.optionalString (!v3) ''
|
||||
${lib.getExe lndir} ${installers.photo} "$WINEPREFIX/drive_c/Program Files/"
|
||||
${lib.getExe lndir} ${installers.designer} "$WINEPREFIX/drive_c/Program Files/"
|
||||
${lib.getExe lndir} ${installers.publisher} "$WINEPREFIX/drive_c/Program Files/"
|
||||
''}
|
||||
|
||||
${lib.getExe wineserver} -w
|
||||
|
||||
rm -rf $WINEPREFIX/drive_c/users/nixbld
|
||||
''
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
pkgs,
|
||||
}:
|
||||
rec {
|
||||
# vkd3d was added in revision 10
|
||||
one-vkd3d =
|
||||
(pkgs.writeText "vkd3d-regedit-changes.reg" ''
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Wine\DllOverrides]
|
||||
"d3d12"="native"
|
||||
"d3d12core"="native"
|
||||
'').outPath;
|
||||
|
||||
combined = pkgs.linkFarm "registry-patches-combined" [
|
||||
{
|
||||
name = "one.reg";
|
||||
path = one-vkd3d;
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
wine-packages,
|
||||
...
|
||||
}:
|
||||
let
|
||||
runner = pkgs.callPackage ./package.nix {
|
||||
inherit inputs wine-packages;
|
||||
|
||||
registry-patches = (pkgs.callPackage ../registry-patches.nix { }).combined;
|
||||
prefixBase = "/non-functional-runner";
|
||||
name = "v3";
|
||||
};
|
||||
in
|
||||
{
|
||||
checks = {
|
||||
runner = runner.package;
|
||||
|
||||
runner-clippy = runner.package-clippy;
|
||||
};
|
||||
|
||||
packages = {
|
||||
runner = runner.package;
|
||||
runner-artifacts = runner.package-artifacts;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
|
||||
inputs,
|
||||
registry-patches,
|
||||
wine-packages,
|
||||
prefixBase,
|
||||
name,
|
||||
...
|
||||
}:
|
||||
let
|
||||
craneLib = inputs.crane.mkLib pkgs;
|
||||
src = craneLib.cleanCargoSource ../..;
|
||||
commonArgs = {
|
||||
inherit src;
|
||||
strictDeps = true;
|
||||
};
|
||||
|
||||
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
|
||||
|
||||
fileSetForCrate =
|
||||
crate:
|
||||
lib.fileset.toSource {
|
||||
root = ../..;
|
||||
fileset = lib.fileset.unions [
|
||||
../../Cargo.toml
|
||||
../../Cargo.lock
|
||||
(craneLib.fileset.commonCargoSources crate)
|
||||
];
|
||||
};
|
||||
|
||||
env = {
|
||||
WINE = lib.getExe wine-packages.wine;
|
||||
WINESERVER = lib.getExe wine-packages.wineserver;
|
||||
WINETRICKS = lib.getExe wine-packages.winetricks;
|
||||
FUSE_OVERLAYFS = lib.getExe pkgs.fuse-overlayfs;
|
||||
GNUTAR = lib.getExe pkgs.gnutar;
|
||||
ZENITY = lib.getExe pkgs.zenity;
|
||||
RSYNC = lib.getExe pkgs.rsync;
|
||||
REGISTRY_PATCHES = registry-patches;
|
||||
ON_LINUX = inputs.on-linux.outPath;
|
||||
};
|
||||
|
||||
executableName = "affinity-${lib.toLower name}";
|
||||
|
||||
executable = craneLib.buildPackage (
|
||||
commonArgs
|
||||
// {
|
||||
inherit cargoArtifacts env;
|
||||
|
||||
pname = executableName;
|
||||
|
||||
cargoExtraArgs = "-p runner --no-default-features --features ${lib.toLower name}";
|
||||
src = fileSetForCrate ../../crates/runner;
|
||||
|
||||
meta.mainProgram = executableName;
|
||||
|
||||
postInstall = ''
|
||||
mv $out/bin/runner $out/bin/affinity-${lib.toLower name}
|
||||
'';
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
package =
|
||||
pkgs.runCommand executableName
|
||||
{
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
}
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
cp ${lib.getExe executable} $out/bin/${executableName}
|
||||
|
||||
wrapProgram $out/bin/${executableName} \
|
||||
--set "LOWER_DIR" "${prefixBase}" \
|
||||
'';
|
||||
|
||||
package-clippy = craneLib.cargoClippy (
|
||||
commonArgs
|
||||
// {
|
||||
inherit cargoArtifacts env;
|
||||
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
||||
}
|
||||
);
|
||||
|
||||
package-artifacts = cargoArtifacts;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
mkExtract =
|
||||
source: segment:
|
||||
pkgs.runCommand "affinity-extracted-sources"
|
||||
{
|
||||
nativeBuildInputs = [
|
||||
pkgs._7zz
|
||||
pkgs.msitools
|
||||
];
|
||||
|
||||
meta.license = lib.licenses.unfree;
|
||||
}
|
||||
''
|
||||
7zz e ${source} -tpe "${segment}"
|
||||
msiextract -C $out ./*
|
||||
'';
|
||||
in
|
||||
{
|
||||
v3 = mkExtract (pkgs.fetchurl {
|
||||
url = "https://downloads.affinity.studio/Affinity%20x64.exe";
|
||||
hash = "sha256-cnPdRZlZrl5Pp6Vr+GkO7br98GrcMDnnJPXcAboxkC4=";
|
||||
}) ".rsrc/2057/BIN/135";
|
||||
|
||||
photo = mkExtract (pkgs.fetchurl {
|
||||
name = "affinity-photo-msi-2.6.5.exe";
|
||||
url = "https://archive.org/download/affinity_20251030/affinity-photo-msi-2.6.5.exe";
|
||||
hash = "sha256-waCX33rWVzZ8AYZWmv2EOqS6ikT6p7s9qRJ+8rDo6Wk=";
|
||||
}) ".rsrc/2057/BIN/135";
|
||||
|
||||
designer = mkExtract (pkgs.fetchurl {
|
||||
name = "affinity-designer-msi-2.6.5.exe";
|
||||
url = "https://archive.org/download/affinity_20251030/affinity-designer-msi-2.6.5.exe";
|
||||
hash = "sha256-b5K1FtsCWCjyn/NC4trB0g9Wv+AfLoDWgaZK2ZGXW0w=";
|
||||
}) ".rsrc/2057/BIN/135";
|
||||
|
||||
publisher = mkExtract (pkgs.fetchurl {
|
||||
name = "affinity-publisher-msi-2.6.5.exe";
|
||||
url = "https://archive.org/download/affinity_20251030/affinity-publisher-msi-2.6.5.exe";
|
||||
hash = "sha256-76ikkAnbAghaSU5YLYLxXiHfAuqVfoEcQK16rHd6+7A=";
|
||||
}) ".rsrc/2057/BIN/135";
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
wine-packages = pkgs.callPackage ./packages.nix {
|
||||
inherit inputs;
|
||||
};
|
||||
in
|
||||
{
|
||||
packages.wine = wine-packages.wine;
|
||||
|
||||
_module.args = {
|
||||
inherit (wine-packages) wineUnwrapped;
|
||||
inherit wine-packages;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
stdenv,
|
||||
callPackage,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
wineUnstable = inputs.nixpkgs.legacyPackages.${stdenv.hostPlatform.system}.wineWow64Packages.full;
|
||||
|
||||
symlink = callPackage ./symlink.nix { };
|
||||
|
||||
wineUnwrapped = symlink {
|
||||
wine = wineUnstable;
|
||||
};
|
||||
|
||||
wrapWithPrefix = callPackage ./wrapWithPrefix.nix {
|
||||
inherit wineUnwrapped;
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit wineUnwrapped;
|
||||
|
||||
wine = wrapWithPrefix wineUnwrapped "wine";
|
||||
winetricks = wrapWithPrefix pkgs.winetricks "winetricks";
|
||||
wineserver = wrapWithPrefix wineUnwrapped "wineserver";
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{ runCommand }:
|
||||
{ wine }:
|
||||
runCommand "wine-symlink" { inherit wine; } ''
|
||||
mkdir -p $out/bin
|
||||
for f in $wine/bin/*; do
|
||||
cp -L "$f" $out/bin/
|
||||
done
|
||||
|
||||
ln -s "$wine/bin/wine" $out/bin/wine64
|
||||
for dir in include lib share; do
|
||||
ln -s "$wine/$dir" $out
|
||||
done
|
||||
|
||||
patchShebangs $out
|
||||
''
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
stdenv,
|
||||
lib,
|
||||
pkgs,
|
||||
wineUnwrapped,
|
||||
}:
|
||||
pkg: pname:
|
||||
stdenv.mkDerivation rec {
|
||||
name = "affinity-${pname}";
|
||||
src = ./.;
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
installPhase = ''
|
||||
makeWrapper ${lib.getExe' pkg pname} $out/bin/${name} \
|
||||
--set LD_LIBRARY_PATH "${wineUnwrapped}/lib:$LD_LIBRARY_PATH" \
|
||||
--set WINESERVER "${lib.getExe' wineUnwrapped "wineserver"}" \
|
||||
--set WINELOADER "${lib.getExe' wineUnwrapped "wine"}" \
|
||||
--set WINEDLLPATH "${wineUnwrapped}/lib/wine" \
|
||||
--set WINE "${lib.getExe' wineUnwrapped "wine"}"
|
||||
'';
|
||||
meta.mainProgram = name;
|
||||
}
|
||||
Reference in New Issue
Block a user