diff --git a/src/Avalonia.FreeDesktop/AtSpi/Accessible.cs b/src/Avalonia.FreeDesktop/AtSpi/Accessible.cs index 393beaec46..e7d822d6fd 100644 --- a/src/Avalonia.FreeDesktop/AtSpi/Accessible.cs +++ b/src/Avalonia.FreeDesktop/AtSpi/Accessible.cs @@ -2,19 +2,51 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Avalonia.Logging; using Tmds.DBus.Protocol; using Tmds.DBus.SourceGenerator; + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously namespace Avalonia.FreeDesktop.AtSpi; +internal class EventObject(Connection connection) : OrgA11yAtspiEventObject +{ + public override Connection Connection => connection; + protected override ValueTask OnDummyAsync() + { + return default; + } + + public void EmitOnPropertyChange(string? @property, int arg1, int arg2, Variant value, + Dictionary? properties) + => EmitPropertyChange(@property, arg1, arg2, value, + properties); + + public void EmitOnChildrenChange(string? operation, int indexInParent, int arg2, Variant child, + Dictionary? properties) => + EmitChildrenChanged(operation, indexInParent, arg2, child, properties); +} + +enum GtkAccessibleChildState +{ + GTK_ACCESSIBLE_CHILD_STATE_ADDED, + GTK_ACCESSIBLE_CHILD_STATE_REMOVED +}; + +enum GtkAccessibleChildChange +{ + GTK_ACCESSIBLE_CHILD_CHANGE_ADDED = 1 << GtkAccessibleChildState.GTK_ACCESSIBLE_CHILD_STATE_ADDED, + GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED = 1 << GtkAccessibleChildState.GTK_ACCESSIBLE_CHILD_STATE_REMOVED +}; + internal abstract class Accessible : OrgA11yAtspiAccessible { private readonly Accessible? _internalParent; - private PathHandler? _pathHandler; + protected PathHandler? _pathHandler; public Guid InternalGuid { get; protected init; } public CacheEntry InternalCacheEntry { get; } = new(); public string DbusPath { get; } @@ -22,37 +54,53 @@ internal abstract class Accessible : OrgA11yAtspiAccessible public override Connection Connection { get; } public Accessible? InternalParent => _internalParent; + protected Accessible(string serviceName, Accessible? internalParent, Connection? connection = null) { - if (connection is null && internalParent is not null) { _internalParent = internalParent; - Parent = internalParent.Convert(); ServiceName = serviceName; InternalGuid = Guid.NewGuid(); + DbusPath = Path.Combine(AtSpiConstants.AvaloniaPathPrefix, InternalGuid.ToString("N")); InternalCacheEntry.Accessible = (serviceName, DbusPath); InternalCacheEntry.Parent = Parent!; InternalCacheEntry.Application = internalParent.InternalCacheEntry.Application; - + this.Locale = (Environment.GetEnvironmentVariable("LANG") ?? string.Empty); - + _pathHandler = new PathHandler(DbusPath); _pathHandler.Add(this); Connection = internalParent.Connection; + _eventObject = new EventObject(Connection); + Connection.AddMethodHandler(_pathHandler); - internalParent.Connection.AddMethodHandler(_pathHandler); - internalParent.AddChild(this); } - else if(connection is not null && this.Connection is null) + else if (connection is not null && this.Connection is null) { + + InternalCacheEntry.Accessible = (serviceName, AtSpiContext.RootPath)!; + InternalCacheEntry.Application = (serviceName, AtSpiContext.RootPath)!; + InternalCacheEntry.ApplicableInterfaces = ["org.a11y.atspi.Accessible", "org.a11y.atspi.Application"]; + InternalCacheEntry.Role = AtSpiConstants.Role.Application; + InternalCacheEntry.Name = Application.Current?.Name ?? "Avalonia Application"; + InternalCacheEntry.Description = string.Empty; + InternalCacheEntry.ChildCount = 0; //TODO + InternalCacheEntry.ApplicableStates = [0, 0]; + this.InternalGuid = Guid.Empty; + Connection = connection; DbusPath = AtSpiContext.RootPath; ServiceName = serviceName; + _pathHandler = new PathHandler(DbusPath); + _eventObject = new EventObject(Connection); + _pathHandler.Add(this); + _pathHandler.Add(_eventObject); + Connection.AddMethodHandler(_pathHandler); } } @@ -60,22 +108,31 @@ internal abstract class Accessible : OrgA11yAtspiAccessible private readonly List _internalChildren = new(); + private readonly EventObject _eventObject; public void AddChild(Accessible accessible) { - if (AtSpiContext.Cache != null && AtSpiContext.Cache.TryAddEntry(accessible)) - { - _internalChildren.Add(accessible); - ChildCount = _internalChildren.Count; - } - else - { - Logger.TryGet(LogEventLevel.Error, LogArea.FreeDesktopPlatform)?.Log(this, - $"Unable to add Accessible object to the root AT-SPI cache. {accessible.InternalGuid}"); + _internalChildren.Add(accessible); + ChildCount = _internalChildren.Count; + InternalCacheEntry.ChildCount = ChildCount; + AtSpiContext.Cache.TryAddEntry(accessible); - } + var k = new Struct(ServiceName, accessible.DbusPath); + + // new Thread(async () => + // { + // for (int i = 0; i < 10; i++) + // { + // await Task.Delay(2000); + // _eventObject.EmitOnPropertyChange("accessible-childcount", 0, 0, new Variant($"test{i}"), null); + // } + // }).Start(); + + _eventObject.EmitOnChildrenChange("add", _internalChildren.IndexOf(accessible), 0, + k , null); } + public void RemoveChild(Accessible accessible) { _internalChildren.Remove(accessible); @@ -92,7 +149,7 @@ internal abstract class Accessible : OrgA11yAtspiAccessible return accessible.Convert(); } - return ("", "/org/a11y/atspi/accessible/null"); + return ("", "/org/a11y/atspi/null"); } protected override async ValueTask<(string, ObjectPath)[]> OnGetChildrenAsync() @@ -120,12 +177,12 @@ internal abstract class Accessible : OrgA11yAtspiAccessible protected override async ValueTask OnGetRoleNameAsync() { - return InternalCacheEntry.RoleName; + return AtSpiConstants.RoleNames[(int)InternalCacheEntry.Role]; } protected override async ValueTask OnGetLocalizedRoleNameAsync() { - return InternalCacheEntry.LocalizedName; + return AtSpiConstants.RoleNames[(int)InternalCacheEntry.Role]; } protected override async ValueTask OnGetStateAsync() @@ -148,5 +205,3 @@ internal abstract class Accessible : OrgA11yAtspiAccessible return InternalCacheEntry.ApplicableInterfaces; } } - - diff --git a/src/Avalonia.FreeDesktop/AtSpi/Accessibles/WindowAutomationPeer.cs b/src/Avalonia.FreeDesktop/AtSpi/Accessibles/WindowAutomationPeer.cs index d722b7abcc..a42181a49f 100644 --- a/src/Avalonia.FreeDesktop/AtSpi/Accessibles/WindowAutomationPeer.cs +++ b/src/Avalonia.FreeDesktop/AtSpi/Accessibles/WindowAutomationPeer.cs @@ -12,16 +12,17 @@ internal class WindowAccessible : Accessible WindowAutomationPeer windowAutomationPeer) : base(serviceName, internalParent) { Peer = windowAutomationPeer; - - Name = Peer.GetName(); + + Name = "Software"; + Locale = Environment.GetEnvironmentVariable("LANG"); Peer.ChildrenChanged += PeerOnChildrenChanged; - InternalCacheEntry.Role = AtSpiConstants.Role.Window; - InternalCacheEntry.LocalizedName = AtSpiConstants.RoleNames[(int)InternalCacheEntry.Role]; - InternalCacheEntry.RoleName = AtSpiConstants.RoleNames[(int)InternalCacheEntry.Role]; + InternalCacheEntry.Role = AtSpiConstants.Role.Frame; + InternalCacheEntry.Name = "Software"; + InternalCacheEntry.Description = ""; InternalCacheEntry.ChildCount = 0; //TODO - InternalCacheEntry.ApplicableStates = [(uint)(AtSpiConstants.State.Visible), 0]; + InternalCacheEntry.ApplicableStates = [(uint)(1124073472), 0]; } private void PeerOnChildrenChanged(object sender, EventArgs e) diff --git a/src/Avalonia.FreeDesktop/AtSpi/AtSpiContext.cs b/src/Avalonia.FreeDesktop/AtSpi/AtSpiContext.cs index cf8d92cf46..cb44c37acb 100644 --- a/src/Avalonia.FreeDesktop/AtSpi/AtSpiContext.cs +++ b/src/Avalonia.FreeDesktop/AtSpi/AtSpiContext.cs @@ -20,15 +20,8 @@ internal class AtSpiContext _connection = connection; if (ServiceName == null) return; - this._rootAccessible = new RootAccessible(connection, ServiceName); var ac1 = new RootApplication(connection); - var path = "/org/a11y/atspi/accessible/root"; - var pathHandler = new PathHandler(path); - - pathHandler.Add(_rootAccessible); - pathHandler.Add(ac1); - - _connection.AddMethodHandler(pathHandler); + this._rootAccessible = new RootAccessible(connection, ServiceName, ac1); var socket = new OrgA11yAtspiSocket(_connection, "org.a11y.atspi.Registry", RootPath); @@ -39,8 +32,8 @@ internal class AtSpiContext var appName = Application.Current?.Name ?? "Avalonia Application"; _rootAccessible.Name = appName; - _rootAccessible.InternalCacheEntry.RoleName = "application"; - + _rootAccessible.InternalCacheEntry.Description = "application"; + Cache?.TryAddEntry(_rootAccessible); } diff --git a/src/Avalonia.FreeDesktop/AtSpi/CacheEntry.cs b/src/Avalonia.FreeDesktop/AtSpi/CacheEntry.cs index 79c7d6d6ff..fa2453e000 100644 --- a/src/Avalonia.FreeDesktop/AtSpi/CacheEntry.cs +++ b/src/Avalonia.FreeDesktop/AtSpi/CacheEntry.cs @@ -10,9 +10,9 @@ public class CacheEntry public int IndexInParent = 0; public int ChildCount = 0; public string[] ApplicableInterfaces = ["org.a11y.atspi.Accessible"]; - public string LocalizedName = string.Empty; + public string Name = string.Empty; public AtSpiConstants.Role Role = default; - public string RoleName = string.Empty; + public string Description = string.Empty; public uint[] ApplicableStates = []; public ( @@ -31,8 +31,8 @@ public class CacheEntry IndexInParent, ChildCount, ApplicableInterfaces, - LocalizedName, + Name, (uint)Role, - RoleName, + Description, ApplicableStates); } diff --git a/src/Avalonia.FreeDesktop/AtSpi/RootAccessible.cs b/src/Avalonia.FreeDesktop/AtSpi/RootAccessible.cs index 7f060295b7..ff66630705 100644 --- a/src/Avalonia.FreeDesktop/AtSpi/RootAccessible.cs +++ b/src/Avalonia.FreeDesktop/AtSpi/RootAccessible.cs @@ -9,17 +9,9 @@ namespace Avalonia.FreeDesktop.AtSpi; internal class RootAccessible : Accessible { - public RootAccessible(Connection connection, string serviceName) : base(serviceName, null, connection) + public RootAccessible(Connection connection, string serviceName, RootApplication ac1) : base(serviceName, null, connection) { - InternalCacheEntry.Accessible = (serviceName, AtSpiContext.RootPath)!; - InternalCacheEntry.Application = (serviceName, AtSpiContext.RootPath)!; - InternalCacheEntry.ApplicableInterfaces = ["org.a11y.atspi.Accessible", "org.a11y.atspi.Application"]; - InternalCacheEntry.Role = AtSpiConstants.Role.Application; - InternalCacheEntry.LocalizedName = AtSpiConstants.RoleNames[(int)InternalCacheEntry.Role]; - InternalCacheEntry.RoleName = AtSpiConstants.RoleNames[(int)InternalCacheEntry.Role]; - InternalCacheEntry.ChildCount = 0; //TODO - InternalCacheEntry.ApplicableStates = [0, 0]; - this.InternalGuid = Guid.Empty; + _pathHandler.Add(ac1); } } diff --git a/src/Avalonia.FreeDesktop/AtSpi/RootCache.cs b/src/Avalonia.FreeDesktop/AtSpi/RootCache.cs index fcce326a8e..30fe49e558 100644 --- a/src/Avalonia.FreeDesktop/AtSpi/RootCache.cs +++ b/src/Avalonia.FreeDesktop/AtSpi/RootCache.cs @@ -58,9 +58,7 @@ internal class RootCache : OrgA11yAtspiCache uint[])[]> OnGetItemsAsync() { return default; -/* - return (_globalCache.Values.Select(x => x.InternalCacheEntry.Convert() - ).ToArray()); -*/ + // return (_globalCache.Values.Select(x => x.InternalCacheEntry.Convert() + // ).ToArray()); } } diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index 8278312e99..cb73d23eeb 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -57,8 +57,10 @@ + + + - diff --git a/src/Avalonia.FreeDesktop/DBusXml/AtSpi/Event.xml b/src/Avalonia.FreeDesktop/DBusXml/AtSpi/Event.xml index c5ab1dfa5a..f279c54a65 100644 --- a/src/Avalonia.FreeDesktop/DBusXml/AtSpi/Event.xml +++ b/src/Avalonia.FreeDesktop/DBusXml/AtSpi/Event.xml @@ -8,6 +8,9 @@ --> + + + @@ -191,6 +194,9 @@ + + + @@ -345,148 +351,148 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + +