This commit is contained in:
2026-09-01 10:45:21 -04:00
commit ac32649783
51 changed files with 4837 additions and 0 deletions
+589
View File
@@ -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