From a13131cb2581f691611a866292fbeb8828710c5a Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Tue, 30 Apr 2024 19:50:53 +0200 Subject: [PATCH 01/14] Bump DBus stack - Use new Variant system --- .../Avalonia.FreeDesktop.csproj | 4 +- .../DBusIme/Fcitx/FcitxICWrapper.cs | 4 +- .../DBusIme/Fcitx/FcitxX11TextInputMethod.cs | 2 +- .../DBusIme/IBus/IBusX11TextInputMethod.cs | 21 ++-- src/Avalonia.FreeDesktop/DBusMenuExporter.cs | 85 +++++++------- .../DBusPlatformSettings.cs | 36 +++--- src/Avalonia.FreeDesktop/DBusSystemDialog.cs | 109 ++++++++---------- src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs | 27 ++--- 8 files changed, 126 insertions(+), 162 deletions(-) diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index 76bd8275b1f..39ba1dfc175 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -12,8 +12,10 @@ + + - + diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs index 94709926e79..8cb5d5761c5 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs @@ -45,7 +45,7 @@ public ValueTask WatchCommitStringAsync(Action public ValueTask WatchForwardKeyAsync(Action handler) => _old?.WatchForwardKeyAsync(handler) - ?? _modern?.WatchForwardKeyAsync((e, ev) => handler.Invoke(e, (ev.keyval, ev.state, ev.type ? 1 : 0))) + ?? _modern?.WatchForwardKeyAsync((e, ev) => handler.Invoke(e, (ev.Keyval, ev.State, ev.Type ? 1 : 0))) ?? new ValueTask(Disposable.Empty); public ValueTask WatchUpdateFormattedPreeditAsync( @@ -53,7 +53,7 @@ public ValueTask WatchUpdateFormattedPreeditAsync( _old?.WatchUpdateFormattedPreeditAsync(handler!) ?? _modern?.WatchUpdateFormattedPreeditAsync(handler!) ?? new ValueTask(Disposable.Empty); - + public Task SetCapacityAsync(uint flags) => _old?.SetCapacityAsync(flags) ?? _modern?.SetCapabilityAsync(flags) ?? Task.CompletedTask; } diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs index efd173d9a6a..eaa5c8f13e2 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs @@ -29,7 +29,7 @@ protected override async Task Connect(string name) var resp = await method.CreateICv3Async(GetAppName(), Process.GetCurrentProcess().Id); - var proxy = new OrgFcitxFcitxInputContext(Connection, name, $"/inputcontext_{resp.icid}"); + var proxy = new OrgFcitxFcitxInputContext(Connection, name, $"/inputcontext_{resp.Icid}"); _context = new FcitxICWrapper(proxy); } else diff --git a/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs b/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs index 35dc18288a1..d81d2d73942 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs @@ -51,17 +51,16 @@ private void OnShowPreedit(Exception? obj) Client.SetPreeditText(_preeditText, _preeditText == null ? null : _preeditCursor); } - private void OnUpdatePreedit(Exception? arg1, (DBusVariantItem text, uint cursor_pos, bool visible) preeditComponents) + private void OnUpdatePreedit(Exception? arg1, (VariantValue Text, uint CursorPos, bool Visible) preeditComponents) { - if (preeditComponents.text is { Value: DBusStructItem { Count: >= 3 } structItem } && - structItem[2] is DBusStringItem stringItem) + if (preeditComponents.Text is { Type: VariantValueType.Struct, Count: >= 3 } structItem && structItem.GetItem(2) is { Type: VariantValueType.String} stringItem) { - _preeditText = stringItem.Value; + _preeditText = stringItem.GetString(); _preeditCursor = _preeditText != null ? Utf16Utils.CharacterOffsetToStringOffset(_preeditText, - (int)Math.Min(preeditComponents.cursor_pos, int.MaxValue), false) + (int)Math.Min(preeditComponents.CursorPos, int.MaxValue), false) : 0; - + _preeditShown = true; } else @@ -102,13 +101,13 @@ private void OnForwardKey(Exception? e, (uint keyval, uint keycode, uint state) }); } - private void OnCommitText(Exception? e, DBusVariantItem variantItem) + private void OnCommitText(Exception? e, VariantValue variantItem) { - if (_insideReset > 0) + if (_insideReset > 0) { // For some reason iBus can trigger a CommitText while being reset. // Thankfully the signal is sent _during_ Reset call processing, - // so it arrives on-the-wire before Reset call result, so we can + // so it arrives on-the-wire before Reset call result, so we can // check if we have any pending Reset calls and ignore the signal here return; } @@ -118,8 +117,8 @@ private void OnCommitText(Exception? e, DBusVariantItem variantItem) return; } - if (variantItem.Value is DBusStructItem { Count: >= 3 } structItem && structItem[2] is DBusStringItem stringItem) - FireCommit(stringItem.Value); + if (variantItem.Count >= 3 && variantItem.GetItem(2) is { Type: VariantValueType.String } stringItem) + FireCommit(stringItem.GetString()); } protected override Task DisconnectAsync() => _service?.DestroyAsync() ?? Task.CompletedTask; diff --git a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs index fcc118bd31f..beb949536f5 100644 --- a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs +++ b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs @@ -60,17 +60,14 @@ public DBusMenuExporterImpl(Connection connection, string path) private void InitBackingProperties() { - BackingProperties.Version = 4; - BackingProperties.Status = string.Empty; - BackingProperties.TextDirection = string.Empty; - BackingProperties.IconThemePath = Array.Empty(); + Version = 4; } protected override Connection Connection { get; } public override string Path { get; } - protected override ValueTask<(uint revision, (int, Dictionary, DBusVariantItem[]) layout)> OnGetLayoutAsync(int parentId, int recursionDepth, string[] propertyNames) + protected override ValueTask<(uint Revision, (int, Dictionary, Variant[]) Layout)> OnGetLayoutAsync(int parentId, int recursionDepth, string[] propertyNames) { var menu = GetMenu(parentId); var layout = GetLayout(menu.item, menu.menu, recursionDepth, propertyNames); @@ -80,22 +77,22 @@ private void InitBackingProperties() OnIsNativeMenuExportedChanged?.Invoke(this, EventArgs.Empty); } - return new ValueTask<(uint, (int, Dictionary, DBusVariantItem[]))>((_revision, layout)); + return new ValueTask<(uint, (int, Dictionary, Variant[]))>((_revision, layout)); } - protected override ValueTask<(int, Dictionary)[]> OnGetGroupPropertiesAsync(int[] ids, string[] propertyNames) + protected override ValueTask<(int, Dictionary)[]> OnGetGroupPropertiesAsync(int[] ids, string[] propertyNames) => new(ids.Select(id => (id, GetProperties(GetMenu(id), propertyNames))).ToArray()); - protected override ValueTask OnGetPropertyAsync(int id, string name) => - new(GetProperty(GetMenu(id), name) ?? new DBusVariantItem("i", new DBusInt32Item(0))); + protected override ValueTask OnGetPropertyAsync(int id, string name) => + new(GetProperty(GetMenu(id), name) ?? new Variant(0)); - protected override ValueTask OnEventAsync(int id, string eventId, DBusVariantItem data, uint timestamp) + protected override ValueTask OnEventAsync(int id, string eventId, VariantValue data, uint timestamp) { HandleEvent(id, eventId); return new ValueTask(); } - protected override ValueTask OnEventGroupAsync((int, string, DBusVariantItem, uint)[] events) + protected override ValueTask OnEventGroupAsync((int, string, VariantValue, uint)[] events) { foreach (var e in events) HandleEvent(e.Item1, e.Item2); @@ -104,7 +101,7 @@ protected override ValueTask OnEventGroupAsync((int, string, DBusVariantI protected override ValueTask OnAboutToShowAsync(int id) => new(false); - protected override ValueTask<(int[] updatesNeeded, int[] idErrors)> OnAboutToShowGroupAsync(int[] ids) => + protected override ValueTask<(int[] UpdatesNeeded, int[] IdErrors)> OnAboutToShowGroupAsync(int[] ids) => new((Array.Empty(), Array.Empty())); private async Task InitializeAsync() @@ -220,35 +217,32 @@ private int GetId(NativeMenuItemBase item) "type", "label", "enabled", "visible", "shortcut", "toggle-type", "children-display", "toggle-state", "icon-data" }; - private static DBusVariantItem? GetProperty((NativeMenuItemBase? item, NativeMenu? menu) i, string name) + private static Variant? GetProperty((NativeMenuItemBase? item, NativeMenu? menu) i, string name) { var (it, menu) = i; if (it is NativeMenuItemSeparator) { if (name == "type") - return new DBusVariantItem("s", new DBusStringItem("separator")); + return new Variant("separator"); } else if (it is NativeMenuItem item) { if (name == "type") return null; if (name == "label") - return new DBusVariantItem("s", new DBusStringItem(item.Header ?? "")); + return new Variant(item.Header ?? ""); if (name == "enabled") { if (item.Menu is not null && item.Menu.Items.Count == 0) - return new DBusVariantItem("b", new DBusBoolItem(false)); + return new Variant(false); if (!item.IsEnabled) - return new DBusVariantItem("b", new DBusBoolItem(false)); + return new Variant(false); return null; } - if (name == "visible") { - if (!item.IsVisible) - return new DBusVariantItem("b", new DBusBoolItem(false)); - return new DBusVariantItem("b", new DBusBoolItem(true)); - } + if (name == "visible") + return new Variant(item.IsVisible); if (name == "shortcut") { @@ -256,30 +250,30 @@ private int GetId(NativeMenuItemBase item) return null; if (item.Gesture.KeyModifiers == 0) return null; - var lst = new List(); + var lst = new Array(); var mod = item.Gesture; if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Control)) - lst.Add(new DBusStringItem("Control")); + lst.Add(new Variant("Control")); if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Alt)) - lst.Add(new DBusStringItem("Alt")); + lst.Add(new Variant("Alt")); if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Shift)) - lst.Add(new DBusStringItem("Shift")); + lst.Add(new Variant("Shift")); if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Meta)) - lst.Add(new DBusStringItem("Super")); - lst.Add(new DBusStringItem(item.Gesture.Key.ToString())); - return new DBusVariantItem("aas", new DBusArrayItem(DBusType.Array, new[] { new DBusArrayItem(DBusType.String, lst) })); + lst.Add(new Variant("Super")); + lst.Add(new Variant(item.Gesture.Key.ToString())); + return Variant.FromArray(new Array>(new[] { lst })); } if (name == "toggle-type") { if (item.ToggleType == NativeMenuItemToggleType.CheckBox) - return new DBusVariantItem("s", new DBusStringItem("checkmark")); + return new Variant("checkmark"); if (item.ToggleType == NativeMenuItemToggleType.Radio) - return new DBusVariantItem("s", new DBusStringItem("radio")); + return new Variant("radio"); } if (name == "toggle-state" && item.ToggleType != NativeMenuItemToggleType.None) - return new DBusVariantItem("i", new DBusInt32Item(item.IsChecked ? 1 : 0)); + return new Variant(item.IsChecked ? 1 : 0); if (name == "icon-data") { @@ -292,50 +286,49 @@ private int GetId(NativeMenuItemBase item) var icon = loader.LoadIcon(item.Icon.PlatformImpl.Item); using var ms = new MemoryStream(); icon.Save(ms); - return new DBusVariantItem("ay", new DBusByteArrayItem(ms.ToArray())); + return Variant.FromArray(new Array(ms.ToArray())); } } } if (name == "children-display") - return menu is not null ? new DBusVariantItem("s", new DBusStringItem("submenu")) : null; + { + if (menu is not null) + return new Variant("submenu"); + return null; + } } return null; } - private static Dictionary GetProperties((NativeMenuItemBase? item, NativeMenu? menu) i, string[] names) + private static Dictionary GetProperties((NativeMenuItemBase? item, NativeMenu? menu) i, string[] names) { if (names.Length == 0) names = s_allProperties; - var properties = new Dictionary(); + var properties = new Dictionary(); foreach (var n in names) { var v = GetProperty(i, n); - if (v is not null) - properties.Add(n, v); + if (v.HasValue) + properties.Add(n, v.Value); } return properties; } - private (int, Dictionary, DBusVariantItem[]) GetLayout(NativeMenuItemBase? item, NativeMenu? menu, int depth, string[] propertyNames) + private (int, Dictionary, Variant[]) GetLayout(NativeMenuItemBase? item, NativeMenu? menu, int depth, string[] propertyNames) { var id = item is null ? 0 : GetId(item); var props = GetProperties((item, menu), propertyNames); - var children = depth == 0 || menu is null ? Array.Empty() : new DBusVariantItem[menu.Items.Count]; + var children = depth == 0 || menu is null ? Array.Empty() : new Variant[menu.Items.Count]; if (menu is not null) { for (var c = 0; c < children.Length; c++) { var ch = menu.Items[c]; var layout = GetLayout(ch, (ch as NativeMenuItem)?.Menu, depth == -1 ? -1 : depth - 1, propertyNames); - children[c] = new DBusVariantItem("(ia{sv}av)", new DBusStructItem(new DBusItem[] - { - new DBusInt32Item(layout.Item1), - new DBusArrayItem(DBusType.DictEntry, layout.Item2.Select(static x => new DBusDictEntryItem(new DBusStringItem(x.Key), x.Value)).ToArray()), - new DBusArrayItem(DBusType.Variant, layout.Item3) - })); + children[c] = Variant.FromStruct(Struct.Create(layout.Item1, new Dict(layout.Item2), new Array(layout.Item3))); } } diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs index bfb130aadeb..6e53ca15ba8 100644 --- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs +++ b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs @@ -41,14 +41,12 @@ private async Task TryGetInitialValuesAsync() try { var version = await _settings!.GetVersionPropertyAsync(); - DBusVariantItem value; + VariantValue value; if (version >= 2) value = await _settings!.ReadOneAsync("org.freedesktop.appearance", "color-scheme"); else - value = (DBusVariantItem)(await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme")).Value; - if (value.Value is DBusUInt32Item dBusUInt32Item) - return ToColorScheme(dBusUInt32Item.Value); - return null; + value = (await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme")).GetItem(0); + return ToColorScheme(value.GetUInt32()); } catch (DBusException) { @@ -61,14 +59,12 @@ private async Task TryGetInitialValuesAsync() try { var version = await _settings!.GetVersionPropertyAsync(); - DBusVariantItem value; + VariantValue value; if (version >= 2) value = await _settings!.ReadOneAsync("org.freedesktop.appearance", "accent-color"); else - value = (DBusVariantItem)(await _settings!.ReadAsync("org.freedesktop.appearance", "accent-color")).Value; - if (value.Value is DBusStructItem dBusStructItem) - return ToAccentColor(dBusStructItem); - return null; + value = (await _settings!.ReadAsync("org.freedesktop.appearance", "accent-color")).GetItem(0); + return ToAccentColor(value); } catch (DBusException) { @@ -76,20 +72,20 @@ private async Task TryGetInitialValuesAsync() } } - private void SettingsChangedHandler(Exception? exception, (string @namespace, string key, DBusVariantItem value) valueTuple) + private void SettingsChangedHandler(Exception? exception, (string Namespace, string Key, VariantValue Value) tuple) { if (exception is not null) return; - switch (valueTuple) + switch (tuple) { - case ("org.freedesktop.appearance", "color-scheme", { } colorScheme): - _themeVariant = ToColorScheme((colorScheme.Value as DBusUInt32Item)!.Value); + case ("org.freedesktop.appearance", "color-scheme", var colorScheme): + _themeVariant = ToColorScheme(colorScheme.GetUInt32()); _lastColorValues = BuildPlatformColorValues(); OnColorValuesChanged(_lastColorValues!); break; - case ("org.freedesktop.appearance", "accent-color", { } accentColor): - _accentColor = ToAccentColor((accentColor.Value as DBusStructItem)!); + case ("org.freedesktop.appearance", "accent-color", var accentColor): + _accentColor = ToAccentColor(accentColor); _lastColorValues = BuildPlatformColorValues(); OnColorValuesChanged(_lastColorValues!); break; @@ -118,16 +114,16 @@ private static PlatformThemeVariant ToColorScheme(uint value) return isDark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light; } - private static Color? ToAccentColor(DBusStructItem value) + private static Color? ToAccentColor(VariantValue value) { /* Indicates the system's preferred accent color as a tuple of RGB values in the sRGB color space, in the range [0,1]. Out-of-range RGB values should be treated as an unset accent color. */ - var r = (value[0] as DBusDoubleItem)!.Value; - var g = (value[1] as DBusDoubleItem)!.Value; - var b = (value[2] as DBusDoubleItem)!.Value; + var r = value.GetItem(0).GetDouble(); + var g = value.GetItem(1).GetDouble(); + var b = value.GetItem(2).GetDouble(); if (r is < 0 or > 1 || g is < 0 or > 1 || b is < 0 or > 1) return null; return Color.FromRgb((byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs index 7ee963fc170..bd041a044a6 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -56,14 +56,15 @@ public override async Task> OpenFilePickerAsync(File { var parentWindow = $"x11:{_handle.Handle:X}"; ObjectPath objectPath; - var chooserOptions = new Dictionary(); - var filters = ParseFilters(options.FileTypeFilter); - if (filters is not null) + var chooserOptions = new Dictionary(); + + if (TryParseFilters(options.FileTypeFilter, out var filters)) chooserOptions.Add("filters", filters); if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath) - chooserOptions.Add("current_folder", new DBusVariantItem("ay", new DBusByteArrayItem(Encoding.UTF8.GetBytes(folderPath + "\0")))); - chooserOptions.Add("multiple", new DBusVariantItem("b", new DBusBoolItem(options.AllowMultiple))); + chooserOptions.Add("current_folder", Variant.FromArray(new Array(Encoding.UTF8.GetBytes(folderPath + "\0")))); + + chooserOptions.Add("multiple", new Variant(options.AllowMultiple)); objectPath = await _fileChooser.OpenFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions); @@ -74,7 +75,7 @@ public override async Task> OpenFilePickerAsync(File if (e is not null) tsc.TrySetException(e); else - tsc.TrySetResult((x.results["uris"].Value as DBusArrayItem)?.Select(static y => (y as DBusStringItem)!.Value).ToArray()); + tsc.TrySetResult(x.Results["uris"].GetArray()); }); var uris = await tsc.Task ?? Array.Empty(); @@ -85,15 +86,14 @@ public override async Task> OpenFilePickerAsync(File { var parentWindow = $"x11:{_handle.Handle:X}"; ObjectPath objectPath; - var chooserOptions = new Dictionary(); - var filters = ParseFilters(options.FileTypeChoices); - if (filters is not null) + var chooserOptions = new Dictionary(); + if (TryParseFilters(options.FileTypeChoices, out var filters)) chooserOptions.Add("filters", filters); if (options.SuggestedFileName is { } currentName) - chooserOptions.Add("current_name", new DBusVariantItem("s", new DBusStringItem(currentName))); + chooserOptions.Add("current_name", new Variant(currentName)); if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath) - chooserOptions.Add("current_folder", new DBusVariantItem("ay", new DBusByteArrayItem(Encoding.UTF8.GetBytes(folderPath + "\0")))); + chooserOptions.Add("current_folder", Variant.FromArray(new Array(Encoding.UTF8.GetBytes(folderPath + "\0")))); objectPath = await _fileChooser.SaveFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions); var request = new OrgFreedesktopPortalRequest(_connection, "org.freedesktop.portal.Desktop", objectPath); @@ -102,41 +102,31 @@ public override async Task> OpenFilePickerAsync(File using var disposable = await request.WatchResponseAsync((e, x) => { if (e is not null) + { tsc.TrySetException(e); + } else { - if(x.results.TryGetValue("current_filter", out var value)) + if (x.Results.TryGetValue("current_filter", out var currentFilter)) { - var currentFilter = value.Value as DBusStructItem; - if(currentFilter != null) + var name = currentFilter.GetItem(0).GetString(); + selectedType = new FilePickerFileType(name); + var patterns = new List(); + var mimeTypes = new List(); + var types = currentFilter.GetItem(1).GetArray(); + foreach(var t in types) { - var name = (currentFilter[0] as DBusStringItem)?.Value.ToString() ?? ""; - selectedType = new FilePickerFileType(name); - if(currentFilter[1] is DBusArrayItem types) - { - List filters = new List(); - List mimeTypes = new List(); - foreach(var t in types) - { - if(t is DBusStructItem filter) - { - if((filter[0] as DBusUInt32Item)?.Value == 1) - { - mimeTypes.Add((filter[1] as DBusStringItem)?.Value.ToString() ?? ""); - } - else - { - filters.Add((filter[1] as DBusStringItem)?.Value.ToString() ?? ""); - } - } - } - - selectedType.Patterns = filters; - selectedType.MimeTypes = mimeTypes; - } + if (t.GetItem(0).GetUInt32() == 1) + mimeTypes.Add(t.GetItem(1).GetString()); + else + patterns.Add(t.GetItem(1).GetString()); } + + selectedType.Patterns = patterns; + selectedType.MimeTypes = mimeTypes; } - tsc.TrySetResult((x.results["uris"].Value as DBusArrayItem)?.Select(static y => (y as DBusStringItem)!.Value).ToArray()); + + tsc.TrySetResult(x.Results["uris"].GetArray()); } }); @@ -157,16 +147,16 @@ public override async Task> OpenFolderPickerAsync( return Array.Empty(); var parentWindow = $"x11:{_handle.Handle:X}"; - var chooserOptions = new Dictionary + var chooserOptions = new Dictionary { - { "directory", new DBusVariantItem("b", new DBusBoolItem(true)) }, - { "multiple", new DBusVariantItem("b", new DBusBoolItem(options.AllowMultiple)) } + { "directory", new Variant(true) }, + { "multiple", new Variant(options.AllowMultiple) } }; if (options.SuggestedFileName is { } currentName) - chooserOptions.Add("current_name", new DBusVariantItem("s", new DBusStringItem(currentName))); + chooserOptions.Add("current_name", new Variant(currentName)); if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath) - chooserOptions.Add("current_folder", new DBusVariantItem("ay", new DBusByteArrayItem(Encoding.UTF8.GetBytes(folderPath + "\0")))); + chooserOptions.Add("current_folder", Variant.FromArray(new Array(Encoding.UTF8.GetBytes(folderPath + "\0")))); var objectPath = await _fileChooser.OpenFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions); var request = new OrgFreedesktopPortalRequest(_connection, "org.freedesktop.portal.Desktop", objectPath); @@ -176,7 +166,7 @@ public override async Task> OpenFolderPickerAsync( if (e is not null) tsc.TrySetException(e); else - tsc.TrySetResult((x.results["uris"].Value as DBusArrayItem)?.Select(static y => (y as DBusStringItem)!.Value).ToArray()); + tsc.TrySetResult(x.Results["uris"].GetArray()); }); var uris = await tsc.Task ?? Array.Empty(); @@ -187,40 +177,35 @@ public override async Task> OpenFolderPickerAsync( .Select(static path => new BclStorageFolder(new DirectoryInfo(path))).ToList(); } - private static DBusVariantItem? ParseFilters(IReadOnlyList? fileTypes) + private static bool TryParseFilters(IReadOnlyList? fileTypes, out Variant result) { const uint GlobStyle = 0u; const uint MimeStyle = 1u; // Example: [('Images', [(0, '*.ico'), (1, 'image/png')]), ('Text', [(0, '*.txt')])] if (fileTypes is null) - return null; + { + result = default; + return false; + } - var filters = new List(); + var filters = new Array>>>(); foreach (var fileType in fileTypes) { - var extensions = new List(); + var extensions = new List>(); if (fileType.Patterns?.Count > 0) - extensions.AddRange( - fileType.Patterns.Select(static pattern => - new DBusStructItem(new DBusItem[] { new DBusUInt32Item(GlobStyle), new DBusStringItem(pattern) }))); + extensions.AddRange(fileType.Patterns.Select(static pattern => Struct.Create(GlobStyle, pattern))); else if (fileType.MimeTypes?.Count > 0) - extensions.AddRange( - fileType.MimeTypes.Select(static mimeType => - new DBusStructItem(new DBusItem[] { new DBusUInt32Item(MimeStyle), new DBusStringItem(mimeType) }))); + extensions.AddRange(fileType.MimeTypes.Select(static mimeType => Struct.Create(MimeStyle, mimeType))); else continue; - filters.Add(new DBusStructItem( - new DBusItem[] - { - new DBusStringItem(fileType.Name), - new DBusArrayItem(DBusType.Struct, extensions) - })); + filters.Add(Struct.Create(fileType.Name, new Array>(extensions))); } - return filters.Count > 0 ? new DBusVariantItem("a(sa(us))", new DBusArrayItem(DBusType.Struct, filters)) : null; + result = Variant.FromArray(filters); + return true; } } } diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index 61e46b651e3..184a9ee2d19 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs @@ -77,7 +77,7 @@ private void OnNameChange(string name, string? newOwner) if (_isDisposed || _connection is null || name != "org.kde.StatusNotifierWatcher") return; - if (!_serviceConnected & newOwner is not null) + if (!_serviceConnected && newOwner is not null) { _serviceConnected = true; _statusNotifierWatcher = new OrgKdeStatusNotifierWatcher(_connection, "org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher"); @@ -212,18 +212,7 @@ internal class StatusNotifierItemDbusObj : OrgKdeStatusNotifierItem public StatusNotifierItemDbusObj(Connection connection, ObjectPath dbusMenuPath) { Connection = connection; - BackingProperties.Menu = dbusMenuPath; - BackingProperties.Category = string.Empty; - BackingProperties.Status = string.Empty; - BackingProperties.Id = string.Empty; - BackingProperties.Title = string.Empty; - BackingProperties.IconPixmap = Array.Empty<(int, int, byte[])>(); - BackingProperties.AttentionIconName = string.Empty; - BackingProperties.AttentionIconPixmap = Array.Empty<(int, int, byte[])>(); - BackingProperties.AttentionMovieName = string.Empty; - BackingProperties.OverlayIconName = string.Empty; - BackingProperties.OverlayIconPixmap = Array.Empty<(int, int, byte[])>(); - BackingProperties.ToolTip = (string.Empty, Array.Empty<(int, int, byte[])>(), string.Empty, string.Empty); + Menu = dbusMenuPath; InvalidateAll(); } @@ -252,12 +241,12 @@ public void InvalidateAll() EmitNewAttentionIcon(); EmitNewOverlayIcon(); EmitNewToolTip(); - EmitNewStatus(BackingProperties.Status); + EmitNewStatus(Status); } public void SetIcon((int, int, byte[]) dbusPixmap) { - BackingProperties.IconPixmap = new[] { dbusPixmap }; + IconPixmap = new[] { dbusPixmap }; InvalidateAll(); } @@ -266,10 +255,10 @@ public void SetTitleAndTooltip(string? text) if (text is null) return; - BackingProperties.Id = text; - BackingProperties.Category = "ApplicationStatus"; - BackingProperties.Status = text; - BackingProperties.Title = text; + Id = text; + Category = "ApplicationStatus"; + Status = text; + Title = text; InvalidateAll(); } } From 5c11cf7c6870cd377bc2a83513243905f932d037 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Wed, 1 May 2024 12:48:03 +0200 Subject: [PATCH 02/14] Fix merge issues --- src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index 39ba1dfc175..f4bdae4d030 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -14,8 +14,6 @@ - - From 87faccefbbee3afacc5dc4bb5d499ac83906ecc6 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Fri, 10 May 2024 21:01:14 +0200 Subject: [PATCH 03/14] Intentionally break the DBus spec --- src/Avalonia.FreeDesktop/DBusPlatformSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs index 6e53ca15ba8..7d88a20818b 100644 --- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs +++ b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs @@ -45,7 +45,7 @@ private async Task TryGetInitialValuesAsync() if (version >= 2) value = await _settings!.ReadOneAsync("org.freedesktop.appearance", "color-scheme"); else - value = (await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme")).GetItem(0); + value = await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme"); return ToColorScheme(value.GetUInt32()); } catch (DBusException) @@ -63,7 +63,7 @@ private async Task TryGetInitialValuesAsync() if (version >= 2) value = await _settings!.ReadOneAsync("org.freedesktop.appearance", "accent-color"); else - value = (await _settings!.ReadAsync("org.freedesktop.appearance", "accent-color")).GetItem(0); + value = await _settings!.ReadAsync("org.freedesktop.appearance", "accent-color"); return ToAccentColor(value); } catch (DBusException) From f1eae81f1caa8164522ed2d71b61b2d13850ad5b Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Fri, 10 May 2024 21:16:33 +0200 Subject: [PATCH 04/14] Dispose DBus connection and signal watchers --- .../DBusPlatformSettings.cs | 34 +++++++++++++++++-- src/Avalonia.X11/X11Platform.cs | 10 ++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs index 7d88a20818b..700f72e93cd 100644 --- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs +++ b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs @@ -7,10 +7,12 @@ namespace Avalonia.FreeDesktop { - internal class DBusPlatformSettings : DefaultPlatformSettings + internal class DBusPlatformSettings : DefaultPlatformSettings, IDisposable { private readonly OrgFreedesktopPortalSettings? _settings; + private IDisposable? _disposable; + private PlatformColorValues? _lastColorValues; private PlatformThemeVariant? _themeVariant; private Color? _accentColor; @@ -21,8 +23,29 @@ public DBusPlatformSettings() return; _settings = new OrgFreedesktopPortalSettings(DBusHelper.Connection, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); - _ = _settings.WatchSettingChangedAsync(SettingsChangedHandler); - _ = TryGetInitialValuesAsync(); + } + + public static DBusPlatformSettings? TryInitialize() + { + try + { + var platformSettings = new DBusPlatformSettings(); + _ = platformSettings.InitializeAsync(); + return platformSettings; + } + catch + { + return null; + } + } + + public async Task InitializeAsync() + { + if (_settings is null) + return; + + _disposable = await _settings.WatchSettingChangedAsync(SettingsChangedHandler); + await TryGetInitialValuesAsync(); } public override PlatformColorValues GetColorValues() => _lastColorValues ?? base.GetColorValues(); @@ -128,5 +151,10 @@ Indicates the system's preferred accent color as a tuple of RGB values return null; return Color.FromRgb((byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); } + + public void Dispose() + { + _disposable?.Dispose(); + } } } diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 7a65ef54c76..efe3b96973a 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -84,11 +84,17 @@ public void Initialize(X11PlatformOptions options) .Bind().ToFunc(() => KeyboardDevice) .Bind().ToConstant(new X11CursorFactory(Display)) .Bind().ToConstant(new X11Clipboard(this)) - .Bind().ToSingleton() .Bind().ToConstant(new X11IconLoader()) .Bind().ToConstant(new LinuxMountedVolumeInfoProvider()) .Bind().ToConstant(new X11PlatformLifetimeEvents(this)); - + + var platformSettings = DBusPlatformSettings.TryInitialize(); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(platformSettings); + + Dispatcher.UIThread.ShutdownFinished += (_, _) => platformSettings?.Dispose(); + // Connection can either be initialized via X11DBusImeHelper.DetectAndRegister() or in DBusPlatformSettings.TryInitialize() + Dispatcher.UIThread.ShutdownFinished += static (_, _) => DBusHelper.Connection?.Dispose(); + Screens = X11Screens = new X11Screens(this); if (Info.XInputVersion != null) { From c6f04becfded1d0acc6d3a824f7141fee91889f6 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Fri, 10 May 2024 21:50:38 +0200 Subject: [PATCH 05/14] Bump Tmds,DBus.Protocol --- src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index f4bdae4d030..7f7bca48051 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -12,7 +12,7 @@ - + From 39df8a03d3c0ad776579b32f062104ab024ba622 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Fri, 10 May 2024 21:51:05 +0200 Subject: [PATCH 06/14] Dispose DBus objects correctly --- src/Avalonia.FreeDesktop/DBusMenuExporter.cs | 1 + src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs | 4 +++- src/Avalonia.X11/X11Window.cs | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs index beb949536f5..32aac31f611 100644 --- a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs +++ b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs @@ -133,6 +133,7 @@ public void Dispose() _disposed = true; // Fire and forget _ = _registrar?.UnregisterWindowAsync(_xid); + Connection.RemoveMethodHandler(Path); } diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index 184a9ee2d19..c91c19fc708 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs @@ -124,14 +124,16 @@ private void DestroyTrayIcon() return; _dBus!.ReleaseNameAsync(_sysTrayServiceName); + _connection.RemoveMethodHandler(_statusNotifierItemDbusObj.Path); } public void Dispose() { IsActive = false; - _isDisposed = true; DestroyTrayIcon(); + (MenuExporter as IDisposable)?.Dispose(); _serviceWatchDisposable?.Dispose(); + _isDisposed = true; } public void SetIcon(IWindowIconImpl? icon) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 85eda994183..5116b793806 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -936,6 +936,9 @@ private void Cleanup(bool fromDestroyNotification) if (_handle != IntPtr.Zero) Closed?.Invoke(); + if (_nativeMenuExporter is IDisposable disposable) + disposable.Dispose(); + if (_rawEventGrouper != null) { _rawEventGrouper.Dispose(); From f3e247007b977c6d8e5bd1c0fce59570f80c0636 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sat, 8 Jun 2024 22:02:35 +0200 Subject: [PATCH 07/14] Use PathHandler for DBus objects --- src/Avalonia.FreeDesktop/DBusMenuExporter.cs | 30 ++++++++------------ src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs | 25 +++++++--------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs index 32aac31f611..a3a164151e4 100644 --- a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs +++ b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs @@ -29,6 +29,7 @@ private class DBusMenuExporterImpl : ComCanonicalDbusmenu, ITopLevelNativeMenuEx private readonly Dictionary _idsToItems = new(); private readonly Dictionary _itemsToIds = new(); private readonly HashSet _menus = new(); + private readonly PathHandler _pathHandler; private readonly uint _xid; private readonly bool _appMenu = true; private ComCanonicalAppMenuRegistrar? _registrar; @@ -40,32 +41,27 @@ private class DBusMenuExporterImpl : ComCanonicalDbusmenu, ITopLevelNativeMenuEx public DBusMenuExporterImpl(Connection connection, IntPtr xid) { - InitBackingProperties(); + Version = 4; Connection = connection; _xid = (uint)xid.ToInt32(); - Path = GenerateDBusMenuObjPath; + _pathHandler = new PathHandler(GenerateDBusMenuObjPath); + _pathHandler.Add(this); SetNativeMenu(new NativeMenu()); _ = InitializeAsync(); } public DBusMenuExporterImpl(Connection connection, string path) { - InitBackingProperties(); + Version = 4; Connection = connection; _appMenu = false; - Path = path; + _pathHandler = new PathHandler(path); + _pathHandler.Add(this); SetNativeMenu(new NativeMenu()); _ = InitializeAsync(); } - private void InitBackingProperties() - { - Version = 4; - } - - protected override Connection Connection { get; } - - public override string Path { get; } + public override Connection Connection { get; } protected override ValueTask<(uint Revision, (int, Dictionary, Variant[]) Layout)> OnGetLayoutAsync(int parentId, int recursionDepth, string[] propertyNames) { @@ -106,7 +102,7 @@ protected override ValueTask OnEventGroupAsync((int, string, VariantValue private async Task InitializeAsync() { - Connection.AddMethodHandler(this); + Connection.AddMethodHandler(_pathHandler); if (!_appMenu) return; @@ -114,7 +110,7 @@ private async Task InitializeAsync() try { if (!_disposed) - await _registrar.RegisterWindowAsync(_xid, Path); + await _registrar.RegisterWindowAsync(_xid, _pathHandler.Path); } catch { @@ -133,11 +129,10 @@ public void Dispose() _disposed = true; // Fire and forget _ = _registrar?.UnregisterWindowAsync(_xid); - Connection.RemoveMethodHandler(Path); + _pathHandler.Remove(this); + Connection.RemoveMethodHandler(_pathHandler.Path); } - - public bool IsNativeMenuExported { get; private set; } public event EventHandler? OnIsNativeMenuExportedChanged; @@ -336,7 +331,6 @@ private static Dictionary GetProperties((NativeMenuItemBase? it return (id, props, children); } - private void HandleEvent(int id, string eventId) { if (eventId == "clicked") diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index ab5199566ec..dc867679a8f 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Reflection; using System.Threading.Tasks; using Avalonia.Controls.Platform; using Avalonia.Logging; @@ -16,11 +14,11 @@ internal class DBusTrayIconImpl : ITrayIconImpl private static int s_trayIconInstanceId; public static readonly (int, int, byte[]) EmptyPixmap = (1, 1, new byte[] { 255, 0, 0, 0 }); - private readonly ObjectPath _dbusMenuPath; private readonly Connection? _connection; private readonly OrgFreedesktopDBus? _dBus; private IDisposable? _serviceWatchDisposable; + private readonly PathHandler _pathHandler = new("/StatusNotifierItem"); private readonly StatusNotifierItemDbusObj? _statusNotifierItemDbusObj; private OrgKdeStatusNotifierWatcher? _statusNotifierWatcher; private (int, int, byte[]) _icon; @@ -51,13 +49,14 @@ public DBusTrayIconImpl() IsActive = true; _dBus = new OrgFreedesktopDBus(_connection, "org.freedesktop.DBus", "/org/freedesktop/DBus"); - _dbusMenuPath = DBusMenuExporter.GenerateDBusMenuObjPath; + var dbusMenuPath = DBusMenuExporter.GenerateDBusMenuObjPath; + + MenuExporter = DBusMenuExporter.TryCreateDetachedNativeMenu(dbusMenuPath, _connection); + + _statusNotifierItemDbusObj = new StatusNotifierItemDbusObj(_connection, dbusMenuPath); + _pathHandler.Add(_statusNotifierItemDbusObj); + _connection.AddMethodHandler(_pathHandler); - MenuExporter = DBusMenuExporter.TryCreateDetachedNativeMenu(_dbusMenuPath, _connection); - - _statusNotifierItemDbusObj = new StatusNotifierItemDbusObj(_connection, _dbusMenuPath); - _connection.AddMethodHandler(_statusNotifierItemDbusObj); - WatchAsync(); } @@ -126,7 +125,8 @@ private void DestroyTrayIcon() return; _dBus!.ReleaseNameAsync(_sysTrayServiceName); - _connection.RemoveMethodHandler(_statusNotifierItemDbusObj.Path); + _pathHandler.Remove(_statusNotifierItemDbusObj); + _connection.RemoveMethodHandler(_pathHandler.Path); } public void Dispose() @@ -217,12 +217,9 @@ public StatusNotifierItemDbusObj(Connection connection, ObjectPath dbusMenuPath) { Connection = connection; Menu = dbusMenuPath; - InvalidateAll(); } - protected override Connection Connection { get; } - - public override string Path => "/StatusNotifierItem"; + public override Connection Connection { get; } public event Action? ActivationDelegate; From 6dd4f009d224e0f15497a3b5fa1568e9009a2ec7 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sat, 13 Jul 2024 13:58:12 +0200 Subject: [PATCH 08/14] Revert to old initialization --- .../DBusPlatformSettings.cs | 41 +++---------------- src/Avalonia.X11/X11Platform.cs | 10 +---- 2 files changed, 8 insertions(+), 43 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs index 660a821487b..4db9265db67 100644 --- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs +++ b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs @@ -8,46 +8,22 @@ namespace Avalonia.FreeDesktop { - internal class DBusPlatformSettings : DefaultPlatformSettings, IDisposable + internal class DBusPlatformSettings : DefaultPlatformSettings { private readonly OrgFreedesktopPortalSettings? _settings; - private IDisposable? _disposable; - private PlatformColorValues? _lastColorValues; private PlatformThemeVariant? _themeVariant; private Color? _accentColor; public DBusPlatformSettings() { - if (DBusHelper.DefaultConnection is not {} conn) - return; - using var restoreContext = AvaloniaSynchronizationContext.Ensure(DispatcherPriority.Input); - - _settings = new OrgFreedesktopPortalSettings(DBusHelper.Connection, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); - } - - public static DBusPlatformSettings? TryInitialize() - { - try - { - var platformSettings = new DBusPlatformSettings(); - _ = platformSettings.InitializeAsync(); - return platformSettings; - } - catch - { - return null; - } - } - - public async Task InitializeAsync() - { - if (_settings is null) + if (DBusHelper.DefaultConnection is not { } conn) return; - _disposable = await _settings.WatchSettingChangedAsync(SettingsChangedHandler); - await TryGetInitialValuesAsync(); + _settings = new OrgFreedesktopPortalSettings(conn, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); + _ = _settings.WatchSettingChangedAsync(SettingsChangedHandler); + _ = TryGetInitialValuesAsync(); } public override PlatformColorValues GetColorValues() => _lastColorValues ?? base.GetColorValues(); @@ -58,7 +34,7 @@ private async Task TryGetInitialValuesAsync() _accentColor = await TryGetAccentColorAsync(); _lastColorValues = BuildPlatformColorValues(); if (_lastColorValues is not null) - Threading.Dispatcher.UIThread.Post(() => OnColorValuesChanged(_lastColorValues)); + Dispatcher.UIThread.Post(() => OnColorValuesChanged(_lastColorValues)); } private async Task TryGetThemeVariantAsync() @@ -153,10 +129,5 @@ Indicates the system's preferred accent color as a tuple of RGB values return null; return Color.FromRgb((byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); } - - public void Dispose() - { - _disposable?.Dispose(); - } } } diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 25bd3e4f001..905e9e13466 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -82,17 +82,11 @@ public void Initialize(X11PlatformOptions options) .Bind().ToFunc(() => KeyboardDevice) .Bind().ToConstant(new X11CursorFactory(Display)) .Bind().ToConstant(new X11Clipboard(this)) + .Bind().ToSingleton() .Bind().ToConstant(new X11IconLoader()) .Bind().ToConstant(new LinuxMountedVolumeInfoProvider()) .Bind().ToConstant(new X11PlatformLifetimeEvents(this)); - - var platformSettings = DBusPlatformSettings.TryInitialize(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(platformSettings); - - Dispatcher.UIThread.ShutdownFinished += (_, _) => platformSettings?.Dispose(); - // Connection can either be initialized via X11DBusImeHelper.DetectAndRegister() or in DBusPlatformSettings.TryInitialize() - Dispatcher.UIThread.ShutdownFinished += static (_, _) => DBusHelper.Connection?.Dispose(); - + Screens = X11Screens = new X11Screens(this); if (Info.XInputVersion != null) { From dc3ce5d491cd4cf7e6fc14535f649419a6651114 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sat, 13 Jul 2024 13:58:30 +0200 Subject: [PATCH 09/14] Bump DBus packages --- src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index 7f7bca48051..fe008cb1881 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -12,8 +12,8 @@ - - + + From a4cff3cd7ae002d6e88f02cc6b1e7433e700260a Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sat, 13 Jul 2024 13:58:47 +0200 Subject: [PATCH 10/14] Fix global menu --- src/Avalonia.FreeDesktop/DBusMenuExporter.cs | 34 +++++++++----------- src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs | 4 +-- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs index d783ee30b3c..9496204cc18 100644 --- a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs +++ b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs @@ -24,11 +24,11 @@ public static INativeMenuExporter TryCreateDetachedNativeMenu(string path, Conne public static string GenerateDBusMenuObjPath => $"/net/avaloniaui/dbusmenu/{Guid.NewGuid():N}"; - private class DBusMenuExporterImpl : ComCanonicalDbusmenu, ITopLevelNativeMenuExporter, IDisposable + private sealed class DBusMenuExporterImpl : ComCanonicalDbusmenu, ITopLevelNativeMenuExporter, IDisposable { private readonly Dictionary _idsToItems = new(); private readonly Dictionary _itemsToIds = new(); - private readonly HashSet _menus = new(); + private readonly HashSet _menus = []; private readonly PathHandler _pathHandler; private readonly uint _xid; private readonly bool _appMenu = true; @@ -46,7 +46,7 @@ public DBusMenuExporterImpl(Connection connection, IntPtr xid) _xid = (uint)xid.ToInt32(); _pathHandler = new PathHandler(GenerateDBusMenuObjPath); _pathHandler.Add(this); - SetNativeMenu(new NativeMenu()); + SetNativeMenu([]); _ = InitializeAsync(); } @@ -57,7 +57,7 @@ public DBusMenuExporterImpl(Connection connection, string path) _appMenu = false; _pathHandler = new PathHandler(path); _pathHandler.Add(this); - SetNativeMenu(new NativeMenu()); + SetNativeMenu([]); _ = InitializeAsync(); } @@ -92,13 +92,13 @@ protected override ValueTask OnEventGroupAsync((int, string, VariantValue { foreach (var e in events) HandleEvent(e.Item1, e.Item2); - return new ValueTask(Array.Empty()); + return new ValueTask([]); } protected override ValueTask OnAboutToShowAsync(int id) => new(false); protected override ValueTask<(int[] UpdatesNeeded, int[] IdErrors)> OnAboutToShowGroupAsync(int[] ids) => - new((Array.Empty(), Array.Empty())); + new(([], [])); private async Task InitializeAsync() { @@ -139,7 +139,7 @@ public void Dispose() public void SetNativeMenu(NativeMenu? menu) { - menu ??= new NativeMenu(); + menu ??= []; if (_menu is not null) ((INotifyCollectionChanged)_menu.Items).CollectionChanged -= OnMenuItemsChanged; @@ -209,9 +209,7 @@ private int GetId(NativeMenuItemBase item) private void OnItemPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) => QueueReset(); - private static readonly string[] s_allProperties = { - "type", "label", "enabled", "visible", "shortcut", "toggle-type", "children-display", "toggle-state", "icon-data" - }; + private static readonly string[] s_allProperties = ["type", "label", "enabled", "visible", "shortcut", "toggle-type", "children-display", "toggle-state", "icon-data"]; private static Variant? GetProperty((NativeMenuItemBase? item, NativeMenu? menu) i, string name) { @@ -246,18 +244,18 @@ private int GetId(NativeMenuItemBase item) return null; if (item.Gesture.KeyModifiers == 0) return null; - var lst = new Array(); + var lst = new Array(); var mod = item.Gesture; if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Control)) - lst.Add(new Variant("Control")); + lst.Add("Control"); if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Alt)) - lst.Add(new Variant("Alt")); + lst.Add("Alt"); if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Shift)) - lst.Add(new Variant("Shift")); + lst.Add("Shift"); if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Meta)) - lst.Add(new Variant("Super")); - lst.Add(new Variant(item.Gesture.Key.ToString())); - return Variant.FromArray(new Array>(new[] { lst })); + lst.Add("Super"); + lst.Add(item.Gesture.Key.ToString()); + return Variant.FromArray(new Array>([lst])); } if (name == "toggle-type") @@ -317,7 +315,7 @@ private static Dictionary GetProperties((NativeMenuItemBase? it { var id = item is null ? 0 : GetId(item); var props = GetProperties((item, menu), propertyNames); - var children = depth == 0 || menu is null ? Array.Empty() : new Variant[menu.Items.Count]; + var children = depth == 0 || menu is null ? [] : new Variant[menu.Items.Count]; if (menu is not null) { for (var c = 0; c < children.Length; c++) diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index 605c581e05e..80942e93c8e 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs @@ -13,7 +13,7 @@ namespace Avalonia.FreeDesktop internal class DBusTrayIconImpl : ITrayIconImpl { private static int s_trayIconInstanceId; - public static readonly (int, int, byte[]) EmptyPixmap = (1, 1, new byte[] { 255, 0, 0, 0 }); + public static readonly (int, int, byte[]) EmptyPixmap = (1, 1, [255, 0, 0, 0]); private readonly Connection? _connection; private readonly OrgFreedesktopDBus? _dBus; @@ -249,7 +249,7 @@ public void InvalidateAll() public void SetIcon((int, int, byte[]) dbusPixmap) { - IconPixmap = new[] { dbusPixmap }; + IconPixmap = [dbusPixmap]; InvalidateAll(); } From 1cfecb75a1f4b755960c3d567e13b890568a6915 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sat, 13 Jul 2024 14:02:02 +0200 Subject: [PATCH 11/14] Add comment about wrapped variants --- src/Avalonia.FreeDesktop/DBusPlatformSettings.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs index 4db9265db67..f290d5c1ade 100644 --- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs +++ b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs @@ -46,6 +46,7 @@ private async Task TryGetInitialValuesAsync() if (version >= 2) value = await _settings!.ReadOneAsync("org.freedesktop.appearance", "color-scheme"); else + // Variants-in-Variants are automatically collapsed by Tmds.DBus.Protocol, so need to do so here as normally necessary value = await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme"); return ToColorScheme(value.GetUInt32()); } From eab37e9487a677eed01ef1c94d5f8f319fca07fc Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sat, 20 Jul 2024 19:08:35 +0200 Subject: [PATCH 12/14] Bump Tmds.DBus.SourceGenerator --- src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index fe008cb1881..f3283fb66ac 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -13,7 +13,7 @@ - + From abea696a5bcd7dfabb93539e96f88d24aa943104 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 25 Jul 2024 18:19:19 -0700 Subject: [PATCH 13/14] Update api baseline --- api/Avalonia.FreeDesktop.nupkg.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 api/Avalonia.FreeDesktop.nupkg.xml diff --git a/api/Avalonia.FreeDesktop.nupkg.xml b/api/Avalonia.FreeDesktop.nupkg.xml new file mode 100644 index 00000000000..f5fcb60bc8e --- /dev/null +++ b/api/Avalonia.FreeDesktop.nupkg.xml @@ -0,0 +1,10 @@ + + + + + CP0001 + T:Tmds.DBus.SourceGenerator.PropertyChanges`1 + baseline/netstandard2.0/Avalonia.FreeDesktop.dll + target/netstandard2.0/Avalonia.FreeDesktop.dll + + \ No newline at end of file From d4d115c67c27c9c9b3329b400d1207ac15310247 Mon Sep 17 00:00:00 2001 From: affederaffe Date: Sun, 18 Aug 2024 23:47:24 +0200 Subject: [PATCH 14/14] Bump Tmds.DBus stack --- src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index f3283fb66ac..0cb0f04bc25 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -12,8 +12,8 @@ - - + +