From 36fe6e3caf4e975545d375ab831e50c5b2f3666d Mon Sep 17 00:00:00 2001 From: marshmallow Date: Mon, 13 Apr 2026 20:14:57 +1000 Subject: [PATCH] test opening file in VM --- flake.lock | 8 +- flake.nix | 2 +- packages/apl/default.nix | 25 +- packages/apl/no-login.patch | 590 ++++++++++++++++++++++++++++++++++++ tests/default.nix | 7 +- tests/v3-test-file.af | Bin 0 -> 32020 bytes tests/v3.py | 12 +- 7 files changed, 615 insertions(+), 29 deletions(-) create mode 100644 packages/apl/no-login.patch create mode 100644 tests/v3-test-file.af diff --git a/flake.lock b/flake.lock index 0de6359..3a388ca 100644 --- a/flake.lock +++ b/flake.lock @@ -221,16 +221,16 @@ "plugin-loader-src": { "flake": false, "locked": { - "lastModified": 1775508798, - "narHash": "sha256-cD2G3M83wCEXVMMl9YoHMLxPIvbXeESgQTkXiM+lzns=", + "lastModified": 1775957426, + "narHash": "sha256-RTwD3e40ZORv3K+R9e/Dy3v6Y5MLfV8GKv11hnvQNpI=", "owner": "noahc3", "repo": "AffinityPluginLoader", - "rev": "bd93bbf9f84735d8ba6538596620bbe8d8b90efd", + "rev": "19ef92212adfbfbec6e5bbaf6a1d48999ef1577b", "type": "github" }, "original": { "owner": "noahc3", - "ref": "v0.3.0", + "ref": "fix-document-path-cli-arg-handler", "repo": "AffinityPluginLoader", "type": "github" } diff --git a/flake.nix b/flake.nix index 7c5f78b..f957bc2 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,7 @@ }; plugin-loader-src = { - url = "github:noahc3/AffinityPluginLoader/v0.3.0"; + url = "github:noahc3/AffinityPluginLoader/fix-document-path-cli-arg-handler"; flake = false; }; diff --git a/packages/apl/default.nix b/packages/apl/default.nix index c22d528..f67dd1a 100644 --- a/packages/apl/default.nix +++ b/packages/apl/default.nix @@ -20,6 +20,10 @@ projectFile = "AffinityPluginLoader.sln"; + patches = [ + ./no-login.patch + ]; + nugetDeps = ./deps.json; postInstall = '' @@ -64,32 +68,11 @@ ''; }; - d2d1 = pkgs.stdenv.mkDerivation rec { - src = "${inputs.plugin-loader-src}/WineFix/lib/d2d1"; - pname = "d2d1"; - inherit version; - - nativeBuildInputs = [ pkgs.wine64 ]; - - TARGET = "x86_64-unix"; - - env.NIX_CFLAGS_COMPILE = toString [ - "-Wno-error=incompatible-pointer-types" - "-Wno-error=discarded-qualifiers" - ]; - - installPhase = '' - mkdir -p $out/lib/AffinityPluginLoader - cp build/${TARGET}/d2d1.dll.so $out/lib/AffinityPluginLoader/d2d1.dll - ''; - }; - apl-combined = pkgs.symlinkJoin { pname = "apl-combined"; inherit version; paths = [ self'.packages.apl - self'.packages.d2d1 self'.packages.bootstrap ]; diff --git a/packages/apl/no-login.patch b/packages/apl/no-login.patch new file mode 100644 index 0000000..03fd24d --- /dev/null +++ b/packages/apl/no-login.patch @@ -0,0 +1,590 @@ +From 45ff4e8c5615af1e8f0a41e92580506788a10b24 Mon Sep 17 00:00:00 2001 +From: marshmallow +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 +-{ +- /// +- /// 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. +- /// +- 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("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)); +- } +- +- 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() +- 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(existingContent, +- c => c.ContentTemplateSelector != null); +- if (contentControl != null) +- { +- var backButton = FindChild