Browse Source
* Implement AT-SPI2 Accessibility Backend * Fix Avalonia.DBus submodule inclusion * review comment fixes * update dbus * Refactor for explicit atspi accessibility node lifetime management. * update dbus * fix review comment --------- Co-authored-by: Julien Lebosquain <julien@lebosquain.net>pull/20765/head
committed by
GitHub
56 changed files with 7791 additions and 7 deletions
@ -0,0 +1,72 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="IOrgA11yAtspiAccessible"/> implementation for <see cref="ApplicationAtSpiNode"/>.
|
|||
/// </summary>
|
|||
internal sealed class ApplicationAccessibleHandler(AtSpiServer server, ApplicationAtSpiNode appNode) |
|||
: IOrgA11yAtspiAccessible |
|||
{ |
|||
private static readonly List<string> s_interfaces = |
|||
[ |
|||
AtSpiConstants.IfaceAccessible, |
|||
AtSpiConstants.IfaceApplication, |
|||
]; |
|||
|
|||
public uint Version => AtSpiConstants.AccessibleVersion; |
|||
public string Name => appNode.Name; |
|||
public string Description => string.Empty; |
|||
|
|||
public AtSpiObjectReference Parent => |
|||
new(string.Empty, new DBusObjectPath(AtSpiConstants.NullPath)); |
|||
|
|||
public int ChildCount => appNode.WindowChildren.Count; |
|||
public string Locale => AtSpiConstants.ResolveLocale(); |
|||
public string AccessibleId => string.Empty; |
|||
public string HelpText => string.Empty; |
|||
|
|||
public ValueTask<AtSpiObjectReference> GetChildAtIndexAsync(int index) |
|||
{ |
|||
var children = appNode.WindowChildren; |
|||
if (index >= 0 && index < children.Count) |
|||
return ValueTask.FromResult(server.GetReference(children[index])); |
|||
return ValueTask.FromResult(server.GetNullReference()); |
|||
} |
|||
|
|||
public ValueTask<List<AtSpiObjectReference>> GetChildrenAsync() |
|||
{ |
|||
var children = appNode.WindowChildren; |
|||
var refs = new List<AtSpiObjectReference>(children.Count); |
|||
foreach (var child in children) |
|||
refs.Add(server.GetReference(child)); |
|||
return ValueTask.FromResult(refs); |
|||
} |
|||
|
|||
public ValueTask<int> GetIndexInParentAsync() => ValueTask.FromResult(-1); |
|||
|
|||
public ValueTask<List<AtSpiRelationEntry>> GetRelationSetAsync() => |
|||
ValueTask.FromResult(new List<AtSpiRelationEntry>()); |
|||
|
|||
public ValueTask<uint> GetRoleAsync() => ValueTask.FromResult((uint)AtSpiRole.Application); |
|||
|
|||
public ValueTask<string> GetRoleNameAsync() => ValueTask.FromResult("application"); |
|||
|
|||
public ValueTask<string> GetLocalizedRoleNameAsync() => ValueTask.FromResult("application"); |
|||
|
|||
public ValueTask<List<uint>> GetStateAsync() => |
|||
ValueTask.FromResult(AtSpiConstants.BuildStateSet([AtSpiState.Active])); |
|||
|
|||
public ValueTask<AtSpiAttributeSet> GetAttributesAsync() => |
|||
ValueTask.FromResult(new AtSpiAttributeSet()); |
|||
|
|||
public ValueTask<AtSpiObjectReference> GetApplicationAsync() => |
|||
ValueTask.FromResult(server.GetRootReference()); |
|||
|
|||
public ValueTask<List<string>> GetInterfacesAsync() => |
|||
ValueTask.FromResult(s_interfaces.ToList()); |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Linq; |
|||
using Avalonia.FreeDesktop.AtSpi.Handlers; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
/// <summary>
|
|||
/// Synthetic application root node that is not backed by an <see cref="Avalonia.Automation.Peers.AutomationPeer"/>.
|
|||
/// Registered at <c>/org/a11y/atspi/accessible/root</c> and serves as the AT-SPI tree root.
|
|||
/// </summary>
|
|||
internal sealed class ApplicationAtSpiNode(string? applicationName) |
|||
{ |
|||
private readonly List<RootAtSpiNode> _windowChildren = []; |
|||
|
|||
public string Path => RootPath; |
|||
public string Name { get; } = applicationName |
|||
?? Application.Current?.Name |
|||
?? Process.GetCurrentProcess().ProcessName; |
|||
|
|||
public AtSpiRole Role => AtSpiRole.Application; |
|||
public List<RootAtSpiNode> WindowChildren => _windowChildren; |
|||
|
|||
public void AddWindowChild(RootAtSpiNode windowNode) => _windowChildren.Add(windowNode); |
|||
public void RemoveWindowChild(RootAtSpiNode windowNode) => _windowChildren.Remove(windowNode); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System.Threading.Tasks; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="IOrgA11yAtspiApplication"/> implementation for <see cref="ApplicationAtSpiNode"/>.
|
|||
/// </summary>
|
|||
internal sealed class ApplicationNodeApplicationHandler : IOrgA11yAtspiApplication |
|||
{ |
|||
public ApplicationNodeApplicationHandler() |
|||
{ |
|||
var version = AtSpiConstants.ResolveToolkitVersion(); |
|||
ToolkitName = "Avalonia"; |
|||
Version = version; |
|||
ToolkitVersion = version; |
|||
AtspiVersion = "2.1"; |
|||
InterfaceVersion = AtSpiConstants.ApplicationVersion; |
|||
} |
|||
|
|||
public string ToolkitName { get; } |
|||
public string Version { get; } |
|||
public string ToolkitVersion { get; } |
|||
public string AtspiVersion { get; } |
|||
public uint InterfaceVersion { get; } |
|||
public int Id { get; set; } |
|||
|
|||
public ValueTask<string> GetLocaleAsync(uint lctype) => |
|||
ValueTask.FromResult(AtSpiConstants.ResolveLocale()); |
|||
|
|||
public ValueTask<string> GetApplicationBusAddressAsync() => |
|||
ValueTask.FromResult(string.Empty); |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using Avalonia.Logging; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
/// <summary>
|
|||
/// Monitors the session bus to detect whether a screen reader is active.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiAccessibilityWatcher : IAsyncDisposable |
|||
{ |
|||
private DBusConnection? _sessionConnection; |
|||
private IDisposable? _propertiesWatcher; |
|||
|
|||
public bool IsEnabled { get; private set; } |
|||
public event EventHandler<bool>? IsEnabledChanged; |
|||
|
|||
public async Task InitAsync() |
|||
{ |
|||
try |
|||
{ |
|||
_sessionConnection = await DBusConnection.ConnectSessionAsync(); |
|||
var proxy = new OrgA11yStatusProxy( |
|||
_sessionConnection, BusNameA11y, new DBusObjectPath(PathA11y)); |
|||
|
|||
try |
|||
{ |
|||
var props = await proxy.GetAllPropertiesAsync(); |
|||
IsEnabled = props.IsEnabled || props.ScreenReaderEnabled; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.FreeDesktopPlatform)? |
|||
.Log(this, "AT-SPI status properties query failed, defaulting to disabled: {Exception}", e); |
|||
IsEnabled = false; |
|||
} |
|||
|
|||
_propertiesWatcher = await proxy.WatchPropertiesChangedAsync( |
|||
(changed, _, _) => |
|||
{ |
|||
var enabled = changed.IsEnabled || changed.ScreenReaderEnabled; |
|||
if (enabled == IsEnabled) return; |
|||
IsEnabled = enabled; |
|||
IsEnabledChanged?.Invoke(this, enabled); |
|||
}, |
|||
sender: null, |
|||
emitOnCapturedContext: true); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
// D-Bus session bus unavailable or org.a11y.Bus not present.
|
|||
// Silently degrade - accessibility remains disabled.
|
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.FreeDesktopPlatform)? |
|||
.Log(this, "AT-SPI watcher unavailable; accessibility remains disabled: {Exception}", e); |
|||
IsEnabled = false; |
|||
} |
|||
} |
|||
|
|||
public async ValueTask DisposeAsync() |
|||
{ |
|||
_propertiesWatcher?.Dispose(); |
|||
_propertiesWatcher = null; |
|||
|
|||
if (_sessionConnection is not null) |
|||
{ |
|||
await _sessionConnection.DisposeAsync(); |
|||
_sessionConnection = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
/// <summary>
|
|||
/// Registers a dummy AT-SPI cache interface at /org/a11y/atspi/cache.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiCacheHandler : IOrgA11yAtspiCache |
|||
{ |
|||
public uint Version => CacheVersion; |
|||
|
|||
public ValueTask<List<AtSpiAccessibleCacheItem>> GetItemsAsync() |
|||
{ |
|||
return ValueTask.FromResult(new List<AtSpiAccessibleCacheItem>()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,89 @@ |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
/// <summary>
|
|||
/// Well-known AT-SPI2 D-Bus paths, interface names, and utility methods.
|
|||
/// </summary>
|
|||
internal static class AtSpiConstants |
|||
{ |
|||
// D-Bus paths
|
|||
internal const string RootPath = "/org/a11y/atspi/accessible/root"; |
|||
internal const string CachePath = "/org/a11y/atspi/cache"; |
|||
internal const string NullPath = "/org/a11y/atspi/null"; |
|||
internal const string AppPathPrefix = "/net/avaloniaui/a11y"; |
|||
internal const string RegistryPath = "/org/a11y/atspi/registry"; |
|||
|
|||
// Interface names
|
|||
internal const string IfaceAccessible = "org.a11y.atspi.Accessible"; |
|||
internal const string IfaceApplication = "org.a11y.atspi.Application"; |
|||
internal const string IfaceComponent = "org.a11y.atspi.Component"; |
|||
internal const string IfaceAction = "org.a11y.atspi.Action"; |
|||
internal const string IfaceValue = "org.a11y.atspi.Value"; |
|||
internal const string IfaceEventObject = "org.a11y.atspi.Event.Object"; |
|||
internal const string IfaceEventWindow = "org.a11y.atspi.Event.Window"; |
|||
internal const string IfaceCache = "org.a11y.atspi.Cache"; |
|||
internal const string IfaceSelection = "org.a11y.atspi.Selection"; |
|||
internal const string IfaceImage = "org.a11y.atspi.Image"; |
|||
internal const string IfaceText = "org.a11y.atspi.Text"; |
|||
internal const string IfaceEditableText = "org.a11y.atspi.EditableText"; |
|||
internal const string IfaceCollection = "org.a11y.atspi.Collection"; |
|||
|
|||
// Bus names
|
|||
internal const string BusNameRegistry = "org.a11y.atspi.Registry"; |
|||
internal const string BusNameA11y = "org.a11y.Bus"; |
|||
internal const string PathA11y = "/org/a11y/bus"; |
|||
|
|||
// Interface versions
|
|||
internal const uint AccessibleVersion = 1; |
|||
internal const uint ApplicationVersion = 1; |
|||
internal const uint ComponentVersion = 1; |
|||
internal const uint ActionVersion = 1; |
|||
internal const uint ValueVersion = 1; |
|||
internal const uint EventObjectVersion = 1; |
|||
internal const uint EventWindowVersion = 1; |
|||
internal const uint CacheVersion = 1; |
|||
internal const uint ImageVersion = 1; |
|||
internal const uint SelectionVersion = 1; |
|||
internal const uint TextVersion = 1; |
|||
internal const uint EditableTextVersion = 1; |
|||
internal const uint CollectionVersion = 1; |
|||
|
|||
internal const uint WidgetLayer = 3; |
|||
internal const uint WindowLayer = 7; |
|||
|
|||
internal static List<uint> BuildStateSet(IReadOnlyCollection<AtSpiState>? states) |
|||
{ |
|||
if (states == null || states.Count == 0) |
|||
return [0u, 0u]; |
|||
|
|||
uint low = 0; |
|||
uint high = 0; |
|||
foreach (var state in states) |
|||
{ |
|||
var bit = (uint)state; |
|||
if (bit < 32) |
|||
low |= 1u << (int)bit; |
|||
else if (bit < 64) |
|||
high |= 1u << (int)(bit - 32); |
|||
} |
|||
|
|||
return [low, high]; |
|||
} |
|||
|
|||
internal static string ResolveLocale() |
|||
{ |
|||
var culture = CultureInfo.CurrentUICulture.Name; |
|||
if (string.IsNullOrWhiteSpace(culture)) |
|||
culture = "en_US"; |
|||
return culture.Replace('-', '_'); |
|||
} |
|||
|
|||
internal static string ResolveToolkitVersion() |
|||
{ |
|||
// TODO: Better way of doing this?
|
|||
return typeof(AtSpiConstants).Assembly.GetName().Version?.ToString() ?? "0"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Avalonia.FreeDesktop.AtSpi; |
|||
|
|||
internal enum AtSpiCoordType : uint |
|||
{ |
|||
Screen = 0, |
|||
Window = 1, |
|||
Parent = 2, |
|||
} |
|||
@ -0,0 +1,107 @@ |
|||
using Avalonia.Automation.Peers; |
|||
using Avalonia.Automation.Provider; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
internal partial class AtSpiNode |
|||
{ |
|||
public static AtSpiRole ToAtSpiRole(AutomationControlType controlType, AutomationPeer? peer = null) |
|||
{ |
|||
return controlType switch |
|||
{ |
|||
AutomationControlType.None => AtSpiRole.Panel, |
|||
AutomationControlType.Button => peer?.GetProvider<IToggleProvider>() is not null |
|||
? AtSpiRole.ToggleButton |
|||
: AtSpiRole.PushButton, |
|||
AutomationControlType.Calendar => AtSpiRole.Calendar, |
|||
AutomationControlType.CheckBox => AtSpiRole.CheckBox, |
|||
AutomationControlType.ComboBox => AtSpiRole.ComboBox, |
|||
AutomationControlType.ComboBoxItem => AtSpiRole.ListItem, |
|||
AutomationControlType.Edit => AtSpiRole.Entry, |
|||
AutomationControlType.Hyperlink => AtSpiRole.Link, |
|||
AutomationControlType.Image => AtSpiRole.Image, |
|||
AutomationControlType.ListItem => AtSpiRole.ListItem, |
|||
AutomationControlType.List => AtSpiRole.List, |
|||
AutomationControlType.Menu => AtSpiRole.Menu, |
|||
AutomationControlType.MenuBar => AtSpiRole.MenuBar, |
|||
AutomationControlType.MenuItem => AtSpiRole.MenuItem, |
|||
AutomationControlType.ProgressBar => AtSpiRole.ProgressBar, |
|||
AutomationControlType.RadioButton => AtSpiRole.RadioButton, |
|||
AutomationControlType.ScrollBar => AtSpiRole.ScrollBar, |
|||
AutomationControlType.Slider => AtSpiRole.Slider, |
|||
AutomationControlType.Spinner => AtSpiRole.SpinButton, |
|||
AutomationControlType.StatusBar => AtSpiRole.StatusBar, |
|||
AutomationControlType.Tab => AtSpiRole.PageTabList, |
|||
AutomationControlType.TabItem => AtSpiRole.PageTab, |
|||
AutomationControlType.Text => AtSpiRole.Label, |
|||
AutomationControlType.ToolBar => AtSpiRole.ToolBar, |
|||
AutomationControlType.ToolTip => AtSpiRole.ToolTip, |
|||
AutomationControlType.Tree => AtSpiRole.Tree, |
|||
AutomationControlType.TreeItem => AtSpiRole.TreeItem, |
|||
AutomationControlType.Custom => AtSpiRole.Unknown, |
|||
AutomationControlType.Group => AtSpiRole.Panel, |
|||
AutomationControlType.Thumb => AtSpiRole.PushButton, |
|||
AutomationControlType.DataGrid => AtSpiRole.TreeTable, |
|||
AutomationControlType.DataItem => AtSpiRole.TableCell, |
|||
AutomationControlType.Document => AtSpiRole.Document, |
|||
AutomationControlType.SplitButton => AtSpiRole.PushButton, |
|||
AutomationControlType.Window => AtSpiRole.Frame, |
|||
AutomationControlType.Pane => AtSpiRole.Panel, |
|||
AutomationControlType.Header => AtSpiRole.Header, |
|||
AutomationControlType.HeaderItem => AtSpiRole.ColumnHeader, |
|||
AutomationControlType.Table => AtSpiRole.Table, |
|||
AutomationControlType.TitleBar => AtSpiRole.TitleBar, |
|||
AutomationControlType.Separator => AtSpiRole.Separator, |
|||
AutomationControlType.Expander => AtSpiRole.Panel, |
|||
_ => AtSpiRole.Unknown, |
|||
}; |
|||
} |
|||
|
|||
public static string ToAtSpiRoleName(AtSpiRole role) |
|||
{ |
|||
return role switch |
|||
{ |
|||
AtSpiRole.Application => "application", |
|||
AtSpiRole.Frame => "frame", |
|||
AtSpiRole.PushButton => "push button", |
|||
AtSpiRole.ToggleButton => "toggle button", |
|||
AtSpiRole.CheckBox => "check box", |
|||
AtSpiRole.ComboBox => "combo box", |
|||
AtSpiRole.Entry => "entry", |
|||
AtSpiRole.Label => "label", |
|||
AtSpiRole.Image => "image", |
|||
AtSpiRole.List => "list", |
|||
AtSpiRole.ListItem => "list item", |
|||
AtSpiRole.Menu => "menu", |
|||
AtSpiRole.MenuBar => "menu bar", |
|||
AtSpiRole.MenuItem => "menu item", |
|||
AtSpiRole.ProgressBar => "progress bar", |
|||
AtSpiRole.RadioButton => "radio button", |
|||
AtSpiRole.ScrollBar => "scroll bar", |
|||
AtSpiRole.Slider => "slider", |
|||
AtSpiRole.SpinButton => "spin button", |
|||
AtSpiRole.StatusBar => "status bar", |
|||
AtSpiRole.PageTab => "page tab", |
|||
AtSpiRole.PageTabList => "page tab list", |
|||
AtSpiRole.ToolBar => "tool bar", |
|||
AtSpiRole.ToolTip => "tool tip", |
|||
AtSpiRole.Tree => "tree", |
|||
AtSpiRole.TreeItem => "tree item", |
|||
AtSpiRole.Panel => "panel", |
|||
AtSpiRole.Separator => "separator", |
|||
AtSpiRole.Table => "table", |
|||
AtSpiRole.TableCell => "table cell", |
|||
AtSpiRole.TreeTable => "tree table", |
|||
AtSpiRole.ColumnHeader => "column header", |
|||
AtSpiRole.Header => "header", |
|||
AtSpiRole.TitleBar => "title bar", |
|||
AtSpiRole.Document => "document frame", |
|||
AtSpiRole.Link => "link", |
|||
AtSpiRole.Calendar => "calendar", |
|||
AtSpiRole.Window => "window", |
|||
AtSpiRole.Unknown => "unknown", |
|||
_ => "unknown", |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
using System.Collections.Generic; |
|||
using Avalonia.Automation; |
|||
using Avalonia.Automation.Peers; |
|||
using Avalonia.Automation.Provider; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
internal partial class AtSpiNode |
|||
{ |
|||
public List<uint> ComputeStates() |
|||
{ |
|||
return ComputeStatesCore(); |
|||
} |
|||
|
|||
private List<uint> ComputeStatesCore() |
|||
{ |
|||
var states = new HashSet<AtSpiState>(); |
|||
|
|||
if (Peer.IsEnabled()) |
|||
{ |
|||
states.Add(AtSpiState.Enabled); |
|||
states.Add(AtSpiState.Sensitive); |
|||
} |
|||
|
|||
if (!Peer.IsOffscreen()) |
|||
{ |
|||
states.Add(AtSpiState.Visible); |
|||
states.Add(AtSpiState.Showing); |
|||
} |
|||
|
|||
if (Peer.IsKeyboardFocusable()) |
|||
states.Add(AtSpiState.Focusable); |
|||
|
|||
if (Peer.HasKeyboardFocus()) |
|||
states.Add(AtSpiState.Focused); |
|||
|
|||
// Toggle state
|
|||
if (Peer.GetProvider<IToggleProvider>() is { } toggle) |
|||
{ |
|||
states.Add(AtSpiState.Checkable); |
|||
switch (toggle.ToggleState) |
|||
{ |
|||
case ToggleState.On: |
|||
states.Add(AtSpiState.Checked); |
|||
break; |
|||
case ToggleState.Indeterminate: |
|||
states.Add(AtSpiState.Indeterminate); |
|||
break; |
|||
case ToggleState.Off: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// Expand/collapse state
|
|||
if (Peer.GetProvider<IExpandCollapseProvider>() is { } expandCollapse) |
|||
{ |
|||
states.Add(AtSpiState.Expandable); |
|||
switch (expandCollapse.ExpandCollapseState) |
|||
{ |
|||
case ExpandCollapseState.Expanded: |
|||
states.Add(AtSpiState.Expanded); |
|||
break; |
|||
case ExpandCollapseState.Collapsed: |
|||
states.Add(AtSpiState.Collapsed); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// Selection item states
|
|||
if (Peer.GetProvider<ISelectionItemProvider>() is { } selectionItem) |
|||
{ |
|||
states.Add(AtSpiState.Selectable); |
|||
if (selectionItem.IsSelected) |
|||
states.Add(AtSpiState.Selected); |
|||
} |
|||
|
|||
// Multi-selectable container
|
|||
if (Peer.GetProvider<ISelectionProvider>() is { CanSelectMultiple: true }) |
|||
states.Add(AtSpiState.MultiSelectable); |
|||
|
|||
// Value provider states (text editable/read-only)
|
|||
if (Peer.GetProvider<IValueProvider>() is { } valueProvider) |
|||
{ |
|||
if (valueProvider.IsReadOnly) |
|||
states.Add(AtSpiState.ReadOnly); |
|||
else |
|||
states.Add(AtSpiState.Editable); |
|||
} |
|||
|
|||
// Range value read-only
|
|||
if (Peer.GetProvider<IRangeValueProvider>() is { IsReadOnly: true }) |
|||
states.Add(AtSpiState.ReadOnly); |
|||
|
|||
// Required for form
|
|||
if (Peer is ControlAutomationPeer controlPeer && |
|||
AutomationProperties.GetIsRequiredForForm(controlPeer.Owner)) |
|||
states.Add(AtSpiState.Required); |
|||
|
|||
// Window-level active state and text entry states
|
|||
var controlType = Peer.GetAutomationControlType(); |
|||
if (controlType == AutomationControlType.Window) |
|||
states.Add(AtSpiState.Active); |
|||
|
|||
if (controlType == AutomationControlType.Edit) |
|||
states.Add(AtSpiState.SingleLine); |
|||
|
|||
return BuildStateSet(states); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,359 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Automation; |
|||
using Avalonia.Automation.Peers; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.Handlers; |
|||
using Avalonia.Logging; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an element in the AT-SPI tree, backed by an AutomationPeer.
|
|||
/// </summary>
|
|||
internal partial class AtSpiNode |
|||
{ |
|||
private protected bool _detached; |
|||
private bool _attached; |
|||
private bool _childrenDirty = true; |
|||
private List<AtSpiNode> _attachedChildren = []; |
|||
|
|||
private readonly string _path; |
|||
|
|||
protected AtSpiNode(AutomationPeer peer, AtSpiServer server) |
|||
{ |
|||
Peer = peer; |
|||
Server = server; |
|||
_path = server.AllocateNodePath(); |
|||
} |
|||
|
|||
public AutomationPeer Peer { get; } |
|||
public AtSpiServer Server { get; } |
|||
public string Path => _path; |
|||
internal bool IsAttached => _attached && !_detached; |
|||
internal AtSpiNode? Parent { get; private set; } |
|||
internal IReadOnlyList<AtSpiNode> AttachedChildren => _attachedChildren; |
|||
internal Task<IDisposable>? PathRegistrationTask { get; private set; } |
|||
|
|||
public HashSet<string> GetSupportedInterfaces() |
|||
{ |
|||
var interfaces = new HashSet<string>(StringComparer.Ordinal) { IfaceAccessible, IfaceComponent }; |
|||
if (ApplicationHandler is not null) interfaces.Add(IfaceApplication); |
|||
if (ActionHandler is not null) interfaces.Add(IfaceAction); |
|||
if (ValueHandler is not null) interfaces.Add(IfaceValue); |
|||
if (SelectionHandler is not null) interfaces.Add(IfaceSelection); |
|||
if (TextHandler is not null) interfaces.Add(IfaceText); |
|||
if (EditableTextHandler is not null) interfaces.Add(IfaceEditableText); |
|||
if (ImageHandler is not null) interfaces.Add(IfaceImage); |
|||
return interfaces; |
|||
} |
|||
|
|||
internal AtSpiAccessibleHandler? AccessibleHandler { get; private set; } |
|||
internal ApplicationNodeApplicationHandler? ApplicationHandler { get; private set; } |
|||
internal AtSpiComponentHandler? ComponentHandler { get; private set; } |
|||
internal AtSpiActionHandler? ActionHandler { get; private set; } |
|||
internal AtSpiValueHandler? ValueHandler { get; private set; } |
|||
internal AtSpiSelectionHandler? SelectionHandler { get; private set; } |
|||
internal AtSpiTextHandler? TextHandler { get; private set; } |
|||
internal AtSpiEditableTextHandler? EditableTextHandler { get; private set; } |
|||
internal AtSpiImageHandler? ImageHandler { get; private set; } |
|||
internal AtSpiEventObjectHandler? EventObjectHandler { get; private set; } |
|||
internal AtSpiEventWindowHandler? EventWindowHandler { get; private set; } |
|||
|
|||
internal void BuildAndRegisterHandlers( |
|||
IDBusConnection connection, |
|||
SynchronizationContext? synchronizationContext = null) |
|||
{ |
|||
var previousRegistrationTask = PathRegistrationTask; |
|||
|
|||
var targets = new List<object>(); |
|||
|
|||
// Accessible - always present
|
|||
targets.Add(AccessibleHandler = new AtSpiAccessibleHandler(Server, this)); |
|||
|
|||
if (Peer.GetProvider<IRootProvider>() is not null) |
|||
targets.Add(ApplicationHandler = new ApplicationNodeApplicationHandler()); |
|||
|
|||
// Component - all visual elements
|
|||
targets.Add(ComponentHandler = new AtSpiComponentHandler(Server, this)); |
|||
|
|||
if (Peer.GetProvider<IInvokeProvider>() is not null || |
|||
Peer.GetProvider<IToggleProvider>() is not null || |
|||
Peer.GetProvider<IExpandCollapseProvider>() is not null || |
|||
Peer.GetProvider<IScrollProvider>() is not null || |
|||
Peer.GetProvider<ISelectionItemProvider>() is not null) |
|||
{ |
|||
targets.Add(ActionHandler = new AtSpiActionHandler(Server, this)); |
|||
} |
|||
|
|||
if (Peer.GetProvider<IRangeValueProvider>() is not null) |
|||
targets.Add(ValueHandler = new AtSpiValueHandler(Server, this)); |
|||
|
|||
if (Peer.GetProvider<ISelectionProvider>() is not null) |
|||
targets.Add(SelectionHandler = new AtSpiSelectionHandler(Server, this)); |
|||
|
|||
if (Peer.GetProvider<IValueProvider>() is { } valueProvider |
|||
&& Peer.GetProvider<IRangeValueProvider>() is null) |
|||
{ |
|||
targets.Add(TextHandler = new AtSpiTextHandler(this)); |
|||
|
|||
if (!valueProvider.IsReadOnly) |
|||
targets.Add(EditableTextHandler = new AtSpiEditableTextHandler(this)); |
|||
} |
|||
|
|||
if (Peer.GetAutomationControlType() == AutomationControlType.Image) |
|||
targets.Add(ImageHandler = new AtSpiImageHandler(Server, this)); |
|||
|
|||
// Event handlers - always present
|
|||
targets.Add(EventObjectHandler = new AtSpiEventObjectHandler(Server, Path)); |
|||
|
|||
if (this is RootAtSpiNode) |
|||
targets.Add(EventWindowHandler = new AtSpiEventWindowHandler(Server, Path)); |
|||
|
|||
PathRegistrationTask = ReplacePathRegistrationAsync( |
|||
previousRegistrationTask, |
|||
connection, |
|||
targets, |
|||
synchronizationContext); |
|||
} |
|||
|
|||
internal static AtSpiNode Create(AutomationPeer peer, AtSpiServer server) |
|||
{ |
|||
return peer.GetProvider<IRootProvider>() is not null |
|||
? new RootAtSpiNode(peer, server) |
|||
: new AtSpiNode(peer, server); |
|||
} |
|||
|
|||
internal static string GetAccessibleName(AutomationPeer peer) |
|||
{ |
|||
var name = peer.GetName(); |
|||
if (!string.IsNullOrWhiteSpace(name)) |
|||
return name; |
|||
|
|||
var visualTypeName = peer.GetClassName(); |
|||
return string.IsNullOrWhiteSpace(visualTypeName) ? string.Empty : visualTypeName; |
|||
} |
|||
|
|||
internal void Attach(AtSpiNode? parent) |
|||
{ |
|||
if (_detached) |
|||
return; |
|||
|
|||
if (_attached) |
|||
{ |
|||
Parent = parent; |
|||
return; |
|||
} |
|||
|
|||
_attached = true; |
|||
_childrenDirty = true; |
|||
Parent = parent; |
|||
Peer.ChildrenChanged += OnPeerChildrenChanged; |
|||
Peer.PropertyChanged += OnPeerPropertyChanged; |
|||
|
|||
if (Server.A11yConnection is { } connection) |
|||
BuildAndRegisterHandlers(connection, Server.SyncContext); |
|||
} |
|||
|
|||
internal void SetParent(AtSpiNode? parent) => Parent = parent; |
|||
|
|||
internal bool RemoveAttachedChild(AtSpiNode child) => _attachedChildren.Remove(child); |
|||
|
|||
internal IReadOnlyList<AtSpiNode> EnsureChildren() |
|||
{ |
|||
if (!IsAttached) |
|||
return Array.Empty<AtSpiNode>(); |
|||
|
|||
if (!_childrenDirty) |
|||
return _attachedChildren; |
|||
|
|||
var childPeers = Peer.GetChildren(); |
|||
var nextChildren = new List<AtSpiNode>(childPeers.Count); |
|||
var nextChildrenSet = new HashSet<AtSpiNode>(); |
|||
foreach (var childPeer in childPeers) |
|||
{ |
|||
var childNode = Server.GetOrCreateNode(childPeer); |
|||
if (!Server.AttachNode(childNode, this)) |
|||
continue; |
|||
|
|||
nextChildren.Add(childNode); |
|||
nextChildrenSet.Add(childNode); |
|||
} |
|||
|
|||
if (_attachedChildren.Count > 0) |
|||
{ |
|||
var removed = _attachedChildren.Where(c => !nextChildrenSet.Contains(c)).ToArray(); |
|||
foreach (var removedNode in removed) |
|||
{ |
|||
if (ReferenceEquals(removedNode.Parent, this)) |
|||
Server.DetachSubtreeRecursive(removedNode); |
|||
} |
|||
} |
|||
|
|||
_attachedChildren = nextChildren; |
|||
_childrenDirty = false; |
|||
return _attachedChildren; |
|||
} |
|||
|
|||
public virtual void Detach() |
|||
{ |
|||
if (_detached) |
|||
return; |
|||
|
|||
_detached = true; |
|||
_attached = false; |
|||
_childrenDirty = true; |
|||
_attachedChildren.Clear(); |
|||
Parent = null; |
|||
Peer.ChildrenChanged -= OnPeerChildrenChanged; |
|||
Peer.PropertyChanged -= OnPeerPropertyChanged; |
|||
DisposePathRegistration(); |
|||
} |
|||
|
|||
internal async Task DisposePathRegistrationAsync() |
|||
{ |
|||
var registrationTask = PathRegistrationTask; |
|||
PathRegistrationTask = null; |
|||
await DisposeRegistrationAsync(registrationTask).ConfigureAwait(false); |
|||
} |
|||
|
|||
internal void DisposePathRegistration() |
|||
{ |
|||
var registrationTask = PathRegistrationTask; |
|||
PathRegistrationTask = null; |
|||
|
|||
if (registrationTask is null) |
|||
return; |
|||
|
|||
if (registrationTask.IsCompletedSuccessfully) |
|||
{ |
|||
registrationTask.Result.Dispose(); |
|||
return; |
|||
} |
|||
|
|||
_ = DisposeRegistrationAsync(registrationTask); |
|||
} |
|||
|
|||
private async Task<IDisposable> ReplacePathRegistrationAsync( |
|||
Task<IDisposable>? previousRegistrationTask, |
|||
IDBusConnection connection, |
|||
IReadOnlyCollection<object> targets, |
|||
SynchronizationContext? synchronizationContext) |
|||
{ |
|||
await DisposeRegistrationAsync(previousRegistrationTask).ConfigureAwait(false); |
|||
return await connection.RegisterObjects((DBusObjectPath)Path, targets, synchronizationContext) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
private static async Task DisposeRegistrationAsync(Task<IDisposable>? registrationTask) |
|||
{ |
|||
if (registrationTask is null) |
|||
return; |
|||
|
|||
try |
|||
{ |
|||
var registration = await registrationTask.ConfigureAwait(false); |
|||
registration.Dispose(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
// Best-effort cleanup: path may have failed to register or connection may be gone.
|
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.FreeDesktopPlatform)? |
|||
.Log(null, "AT-SPI node path registration cleanup failed: {Exception}", e); |
|||
} |
|||
} |
|||
|
|||
private void OnPeerChildrenChanged(object? sender, EventArgs e) |
|||
{ |
|||
if (Server.A11yConnection is null || !IsAttached) |
|||
return; |
|||
|
|||
_childrenDirty = true; |
|||
|
|||
var childPeers = Peer.GetChildren(); |
|||
if (_attachedChildren.Count > 0) |
|||
{ |
|||
var currentPeers = new HashSet<AutomationPeer>(childPeers); |
|||
var removedChildren = _attachedChildren |
|||
.Where(childNode => !currentPeers.Contains(childNode.Peer)) |
|||
.ToArray(); |
|||
|
|||
foreach (var oldChild in removedChildren) |
|||
{ |
|||
if (ReferenceEquals(oldChild.Parent, this)) |
|||
Server.DetachSubtreeRecursive(oldChild); |
|||
} |
|||
|
|||
if (removedChildren.Length > 0) |
|||
{ |
|||
var removedSet = new HashSet<AtSpiNode>(removedChildren); |
|||
_attachedChildren = _attachedChildren |
|||
.Where(childNode => !removedSet.Contains(childNode)) |
|||
.ToList(); |
|||
} |
|||
} |
|||
|
|||
if (!Server.HasEventListeners || EventObjectHandler is not { } eventHandler) return; |
|||
var reference = Server.GetReference(this); |
|||
var childVariant = new DBusVariant(reference.ToDbusStruct()); |
|||
eventHandler.EmitChildrenChangedSignal("add", 0, childVariant); |
|||
} |
|||
|
|||
private void OnPeerPropertyChanged(object? sender, AutomationPropertyChangedEventArgs e) |
|||
{ |
|||
if (Server.A11yConnection is null || !Server.HasEventListeners) |
|||
return; |
|||
|
|||
if (EventObjectHandler is not { } eventHandler) |
|||
return; |
|||
|
|||
if (e.Property == AutomationElementIdentifiers.NameProperty) |
|||
{ |
|||
eventHandler.EmitPropertyChangeSignal( |
|||
"accessible-name", |
|||
new DBusVariant(GetAccessibleName(Peer))); |
|||
} |
|||
else if (e.Property == AutomationElementIdentifiers.HelpTextProperty) |
|||
{ |
|||
eventHandler.EmitPropertyChangeSignal( |
|||
"accessible-description", |
|||
new DBusVariant(e.NewValue?.ToString() ?? string.Empty)); |
|||
} |
|||
else if (e.Property == TogglePatternIdentifiers.ToggleStateProperty) |
|||
{ |
|||
var newState = e.NewValue is ToggleState ts ? ts : ToggleState.Off; |
|||
eventHandler.EmitStateChangedSignal( |
|||
"checked", newState == ToggleState.On ? 1 : 0, new DBusVariant(0)); |
|||
eventHandler.EmitStateChangedSignal( |
|||
"indeterminate", newState == ToggleState.Indeterminate ? 1 : 0, new DBusVariant(0)); |
|||
} |
|||
else if (e.Property == ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty) |
|||
{ |
|||
var newState = e.NewValue is ExpandCollapseState ecs ? ecs : ExpandCollapseState.Collapsed; |
|||
eventHandler.EmitStateChangedSignal( |
|||
"expanded", newState == ExpandCollapseState.Expanded ? 1 : 0, new DBusVariant(0)); |
|||
eventHandler.EmitStateChangedSignal( |
|||
"collapsed", newState == ExpandCollapseState.Collapsed ? 1 : 0, new DBusVariant(0)); |
|||
} |
|||
else if (e.Property == ValuePatternIdentifiers.ValueProperty) |
|||
{ |
|||
eventHandler.EmitPropertyChangeSignal( |
|||
"accessible-value", |
|||
new DBusVariant(e.NewValue?.ToString() ?? string.Empty)); |
|||
} |
|||
else if (e.Property == SelectionPatternIdentifiers.SelectionProperty) |
|||
{ |
|||
eventHandler.EmitSelectionChangedSignal(); |
|||
} |
|||
else if (e.Property == AutomationElementIdentifiers.BoundingRectangleProperty) |
|||
{ |
|||
eventHandler.EmitBoundsChangedSignal(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,159 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using Avalonia.Logging; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
/// <summary>
|
|||
/// Monitors the AT-SPI registry to determine if any screen reader is listening for events.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiRegistryEventTracker : IDisposable |
|||
{ |
|||
private readonly DBusConnection _connection; |
|||
private readonly HashSet<string> _registeredEvents = new(StringComparer.Ordinal); |
|||
|
|||
private OrgA11yAtspiRegistryProxy? _registryProxy; |
|||
private IDisposable? _registryRegisteredSubscription; |
|||
private IDisposable? _registryDeregisteredSubscription; |
|||
private IDisposable? _registryOwnerChangedSubscription; |
|||
private string? _registryUniqueName; |
|||
|
|||
internal AtSpiRegistryEventTracker(DBusConnection connection) |
|||
{ |
|||
_connection = connection ?? throw new ArgumentNullException(nameof(connection)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicates whether any screen reader is currently listening for object events.
|
|||
/// Defaults to true (chatty) until registry tracking confirms otherwise.
|
|||
/// </summary>
|
|||
internal bool HasEventListeners { get; private set; } = true; |
|||
|
|||
internal async Task InitializeAsync() |
|||
{ |
|||
try |
|||
{ |
|||
_registryProxy ??= new OrgA11yAtspiRegistryProxy( |
|||
_connection, BusNameRegistry, new DBusObjectPath(RegistryPath)); |
|||
|
|||
// Seed from current registrations
|
|||
var events = await _registryProxy.GetRegisteredEventsAsync(); |
|||
_registeredEvents.Clear(); |
|||
foreach (var registered in events) |
|||
_registeredEvents.Add(registered.EventName); |
|||
UpdateHasEventListeners(); |
|||
|
|||
// Resolve registry unique name and subscribe to signals
|
|||
var registryOwner = await _connection.GetNameOwnerAsync(BusNameRegistry); |
|||
await SubscribeToRegistrySignalsAsync(registryOwner); |
|||
|
|||
// Watch for registry daemon restarts
|
|||
_registryOwnerChangedSubscription ??= await _connection.WatchNameOwnerChangedAsync( |
|||
(name, oldOwner, newOwner) => |
|||
{ |
|||
if (!string.Equals(name, BusNameRegistry, StringComparison.Ordinal)) |
|||
return; |
|||
|
|||
_ = SubscribeToRegistrySignalsAsync(newOwner); |
|||
}, |
|||
emitOnCapturedContext: true); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
// Registry event tracking unavailable - remain chatty.
|
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.FreeDesktopPlatform)? |
|||
.Log(this, "AT-SPI registry event tracker unavailable; remaining chatty: {Exception}", e); |
|||
HasEventListeners = true; |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_registryOwnerChangedSubscription?.Dispose(); |
|||
_registryOwnerChangedSubscription = null; |
|||
_registryRegisteredSubscription?.Dispose(); |
|||
_registryRegisteredSubscription = null; |
|||
_registryDeregisteredSubscription?.Dispose(); |
|||
_registryDeregisteredSubscription = null; |
|||
_registryProxy = null; |
|||
_registryUniqueName = null; |
|||
_registeredEvents.Clear(); |
|||
} |
|||
|
|||
private async Task SubscribeToRegistrySignalsAsync(string? registryOwner) |
|||
{ |
|||
if (string.Equals(_registryUniqueName, registryOwner, StringComparison.Ordinal)) |
|||
return; |
|||
|
|||
// Dispose old subscriptions
|
|||
_registryRegisteredSubscription?.Dispose(); |
|||
_registryRegisteredSubscription = null; |
|||
_registryDeregisteredSubscription?.Dispose(); |
|||
_registryDeregisteredSubscription = null; |
|||
_registryUniqueName = registryOwner; |
|||
|
|||
var senderFilter = string.IsNullOrWhiteSpace(registryOwner) ? null : registryOwner; |
|||
|
|||
_registryProxy ??= new OrgA11yAtspiRegistryProxy( |
|||
_connection, BusNameRegistry, new DBusObjectPath(RegistryPath)); |
|||
|
|||
try |
|||
{ |
|||
_registryRegisteredSubscription = await _registryProxy.WatchEventListenerRegisteredAsync( |
|||
OnRegistryEventListenerRegistered, |
|||
senderFilter, |
|||
emitOnCapturedContext: true); |
|||
|
|||
_registryDeregisteredSubscription = await _registryProxy.WatchEventListenerDeregisteredAsync( |
|||
OnRegistryEventListenerDeregistered, |
|||
senderFilter, |
|||
emitOnCapturedContext: true); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.FreeDesktopPlatform)? |
|||
.Log(this, "AT-SPI registry signal subscription failed; remaining chatty: {Exception}", e); |
|||
_registryRegisteredSubscription?.Dispose(); |
|||
_registryRegisteredSubscription = null; |
|||
_registryDeregisteredSubscription?.Dispose(); |
|||
_registryDeregisteredSubscription = null; |
|||
HasEventListeners = true; |
|||
} |
|||
} |
|||
|
|||
private void OnRegistryEventListenerRegistered(string bus, string @event, List<string> properties) |
|||
{ |
|||
_registeredEvents.Add(@event); |
|||
UpdateHasEventListeners(); |
|||
} |
|||
|
|||
private void OnRegistryEventListenerDeregistered(string bus, string @event) |
|||
{ |
|||
_registeredEvents.Remove(@event); |
|||
UpdateHasEventListeners(); |
|||
} |
|||
|
|||
private void UpdateHasEventListeners() |
|||
{ |
|||
HasEventListeners = _registeredEvents.Any(IsObjectEventClass); |
|||
} |
|||
|
|||
private static bool IsObjectEventClass(string eventName) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(eventName)) |
|||
return false; |
|||
|
|||
if (eventName == "*") |
|||
return true; |
|||
|
|||
return eventName.StartsWith("object:", StringComparison.OrdinalIgnoreCase) |
|||
|| eventName.StartsWith("window:", StringComparison.OrdinalIgnoreCase) |
|||
|| eventName.StartsWith("focus:", StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,126 @@ |
|||
namespace Avalonia.FreeDesktop.AtSpi; |
|||
|
|||
/// <summary>
|
|||
/// AT-SPI2 role IDs
|
|||
/// </summary>
|
|||
internal enum AtSpiRole |
|||
{ |
|||
Invalid = 0, |
|||
Accelerator = 1, |
|||
Alert = 2, |
|||
Animation = 3, |
|||
Arrow = 4, |
|||
Calendar = 5, |
|||
Canvas = 6, |
|||
CheckBox = 7, |
|||
CheckMenuItem = 8, |
|||
ColorChooser = 9, |
|||
ColumnHeader = 10, |
|||
ComboBox = 11, |
|||
DateEditor = 12, |
|||
DesktopIcon = 13, |
|||
DesktopFrame = 14, |
|||
Dial = 15, |
|||
Dialog = 16, |
|||
DirectoryPane = 17, |
|||
DrawingArea = 18, |
|||
FileChooser = 19, |
|||
Filler = 20, |
|||
FocusTraversable = 21, |
|||
Frame = 23, |
|||
GlassPane = 24, |
|||
HtmlContainer = 25, |
|||
Icon = 26, |
|||
Image = 27, |
|||
InternalFrame = 28, |
|||
Label = 29, |
|||
LayeredPane = 30, |
|||
List = 31, |
|||
ListItem = 32, |
|||
Menu = 33, |
|||
MenuBar = 34, |
|||
MenuItem = 35, |
|||
OptionPane = 36, |
|||
PageTab = 37, |
|||
PageTabList = 38, |
|||
Panel = 39, |
|||
PasswordText = 40, |
|||
PopupMenu = 41, |
|||
ProgressBar = 42, |
|||
PushButton = 43, |
|||
RadioButton = 44, |
|||
RadioMenuItem = 45, |
|||
RootPane = 46, |
|||
RowHeader = 47, |
|||
ScrollBar = 48, |
|||
ScrollPane = 49, |
|||
Separator = 50, |
|||
Slider = 51, |
|||
SpinButton = 52, |
|||
SplitPane = 53, |
|||
StatusBar = 54, |
|||
Table = 55, |
|||
TableCell = 56, |
|||
TableColumnHeader = 57, |
|||
TableRowHeader = 58, |
|||
TearoffMenuItem = 59, |
|||
Terminal = 60, |
|||
Text = 61, |
|||
ToggleButton = 62, |
|||
ToolBar = 63, |
|||
ToolTip = 64, |
|||
Tree = 65, |
|||
TreeTable = 66, |
|||
Unknown = 67, |
|||
Viewport = 68, |
|||
Window = 69, |
|||
Extended = 70, |
|||
Header = 71, |
|||
Footer = 72, |
|||
Paragraph = 73, |
|||
Ruler = 74, |
|||
Application = 75, |
|||
AutoComplete = 76, |
|||
EditBar = 77, |
|||
Embedded = 78, |
|||
Entry = 79, |
|||
Heading = 81, |
|||
Page = 82, |
|||
Document = 83, |
|||
Section = 84, |
|||
RedundantObject = 85, |
|||
Form = 86, |
|||
Link = 87, |
|||
InputMethodWindow = 88, |
|||
TableRow = 89, |
|||
TreeItem = 90, |
|||
DocumentSpreadsheet = 91, |
|||
DocumentPresentation = 92, |
|||
DocumentText = 93, |
|||
DocumentWeb = 94, |
|||
DocumentEmail = 95, |
|||
Comment = 96, |
|||
ListBox = 97, |
|||
Grouping = 98, |
|||
ImageMap = 99, |
|||
Notification = 100, |
|||
InfoBar = 101, |
|||
LevelBar = 102, |
|||
TitleBar = 103, |
|||
BlockQuote = 104, |
|||
Audio = 105, |
|||
Video = 106, |
|||
Definition = 107, |
|||
Article = 108, |
|||
Landmark = 109, |
|||
Log = 110, |
|||
Marquee = 111, |
|||
Math = 112, |
|||
Rating = 113, |
|||
Timer = 114, |
|||
Static = 116, |
|||
MathFraction = 117, |
|||
MathRoot = 118, |
|||
Subscript = 119, |
|||
Superscript = 120, |
|||
} |
|||
@ -0,0 +1,469 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Automation.Peers; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using Avalonia.FreeDesktop.AtSpi.Handlers; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Threading; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
/// <summary>
|
|||
/// Manages the AT-SPI D-Bus connection, node registration, and event emission.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiServer : IAsyncDisposable |
|||
{ |
|||
private readonly Dictionary<string, AtSpiNode> _nodesByPath = new(StringComparer.Ordinal); |
|||
private readonly Dictionary<AutomationPeer, AtSpiNode> _nodesByPeer = []; |
|||
private readonly object _embedSync = new(); |
|||
|
|||
private int _nextNodeId; |
|||
private DBusConnection? _a11yConnection; |
|||
private string _uniqueName = string.Empty; |
|||
private SynchronizationContext? _syncContext; |
|||
private ApplicationAtSpiNode? _appRoot; |
|||
private AtSpiCacheHandler? _cacheHandler; |
|||
private AtSpiEventObjectHandler? _appRootEventHandler; |
|||
private AtSpiRegistryEventTracker? _registryTracker; |
|||
private IDisposable? _appRootRegistration; |
|||
private IDisposable? _cacheRegistration; |
|||
private Task? _embedTask; |
|||
private bool _isEmbedded; |
|||
|
|||
internal DBusConnection? A11yConnection => _a11yConnection; |
|||
internal SynchronizationContext? SyncContext => _syncContext; |
|||
internal string UniqueName => _uniqueName; |
|||
|
|||
/// <summary>
|
|||
/// Indicates whether any screen reader is currently listening for object events.
|
|||
/// Defaults to true (chatty) until registry tracking confirms otherwise.
|
|||
/// </summary>
|
|||
internal bool HasEventListeners => _registryTracker?.HasEventListeners ?? true; |
|||
|
|||
/// <summary>
|
|||
/// Starts the AT-SPI server.
|
|||
/// Must be called on the UI thread.
|
|||
/// <remarks>
|
|||
/// Call order in this method is important because
|
|||
/// AT's are sensitive and/or broken.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public async Task StartAsync() |
|||
{ |
|||
lock (_embedSync) |
|||
{ |
|||
_embedTask = null; |
|||
_isEmbedded = false; |
|||
} |
|||
|
|||
_syncContext = new AvaloniaSynchronizationContext(DispatcherPriority.Normal); |
|||
|
|||
var address = await GetAccessibilityBusAddressAsync(); |
|||
|
|||
if (string.IsNullOrWhiteSpace(address)) |
|||
throw new InvalidOperationException("Failed to resolve the accessibility bus address."); |
|||
|
|||
_a11yConnection = await DBusConnection.ConnectAsync(address); |
|||
_uniqueName = await _a11yConnection.GetUniqueNameAsync() ?? string.Empty; |
|||
|
|||
_appRoot = new ApplicationAtSpiNode(null); |
|||
|
|||
_cacheHandler = new AtSpiCacheHandler(); |
|||
await BuildAndRegisterAppRootAsync(); |
|||
|
|||
await RegisterCachePathAsync(); |
|||
|
|||
_registryTracker = new AtSpiRegistryEventTracker(_a11yConnection); |
|||
_ = InitializeRegistryTrackerAsync(_registryTracker); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a window to the AT-SPI tree.
|
|||
/// </summary>
|
|||
public void AddWindow(AutomationPeer windowPeer) |
|||
{ |
|||
if (_a11yConnection is null || _appRoot is null) |
|||
return; |
|||
|
|||
// Idempotent check
|
|||
if (TryGetAttachedNode(windowPeer) is RootAtSpiNode) |
|||
return; |
|||
|
|||
if (GetOrCreateNode(windowPeer) is not RootAtSpiNode windowNode) |
|||
return; |
|||
|
|||
windowNode.AppRoot = _appRoot; |
|||
if (!AttachNode(windowNode, parent: null)) |
|||
return; |
|||
|
|||
if (!_appRoot.WindowChildren.Contains(windowNode)) |
|||
_appRoot.AddWindowChild(windowNode); |
|||
|
|||
var isEmbedded = false; |
|||
lock (_embedSync) |
|||
isEmbedded = _isEmbedded; |
|||
|
|||
// GTK-like root registration behavior:
|
|||
// embed once, then only emit incremental children-changed for later windows.
|
|||
if (isEmbedded) |
|||
{ |
|||
EmitWindowChildAdded(windowNode); |
|||
} |
|||
else |
|||
{ |
|||
// Embed may already be in flight from StartAsync. The embed completion path
|
|||
// emits children-changed for all currently tracked windows.
|
|||
_ = EnsureEmbeddedAndAnnounceAsync(); |
|||
} |
|||
} |
|||
|
|||
private Task EnsureEmbeddedAndAnnounceAsync() |
|||
{ |
|||
lock (_embedSync) |
|||
{ |
|||
if (_isEmbedded) |
|||
return Task.CompletedTask; |
|||
|
|||
// Ignore repeated embed requests while one is already in flight.
|
|||
if (_embedTask is { IsCompleted: false }) |
|||
return _embedTask; |
|||
|
|||
_embedTask = EmbedAndAnnounceOnceAsync(); |
|||
return _embedTask; |
|||
} |
|||
} |
|||
|
|||
private async Task EmbedAndAnnounceOnceAsync() |
|||
{ |
|||
try |
|||
{ |
|||
await EmbedApplicationAsync(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
// Embed failed - screen reader won't discover us.
|
|||
// Reset so the next AddWindow retries.
|
|||
Logger.TryGet(LogEventLevel.Warning, LogArea.FreeDesktopPlatform)? |
|||
.Log(this, "AT-SPI embed failed; will retry when windows are added: {Exception}", e); |
|||
lock (_embedSync) |
|||
_embedTask = null; |
|||
return; |
|||
} |
|||
|
|||
lock (_embedSync) |
|||
{ |
|||
_isEmbedded = true; |
|||
_embedTask = null; |
|||
} |
|||
|
|||
// Now that the screen reader knows about us, emit children-changed
|
|||
// for every window that was added before the embed completed.
|
|||
if (!HasEventListeners || _appRootEventHandler is not { } eventHandler || _appRoot is null) |
|||
return; |
|||
|
|||
var children = _appRoot.WindowChildren.ToArray(); |
|||
for (var i = 0; i < children.Length; i++) |
|||
{ |
|||
var childRef = GetReference(children[i]); |
|||
var childVariant = new DBusVariant(childRef.ToDbusStruct()); |
|||
eventHandler.EmitChildrenChangedSignal("add", i, childVariant); |
|||
} |
|||
} |
|||
|
|||
private void EmitWindowChildAdded(RootAtSpiNode windowNode) |
|||
{ |
|||
if (!HasEventListeners || _appRootEventHandler is not { } eventHandler) return; |
|||
var childRef = GetReference(windowNode); |
|||
var childVariant = new DBusVariant(childRef.ToDbusStruct()); |
|||
eventHandler.EmitChildrenChangedSignal( |
|||
"add", _appRoot!.WindowChildren.Count - 1, childVariant); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a window from the AT-SPI tree.
|
|||
/// </summary>
|
|||
public void RemoveWindow(AutomationPeer windowPeer) |
|||
{ |
|||
if (_a11yConnection is null || _appRoot is null) |
|||
return; |
|||
|
|||
var windowNode = TryGetAttachedNode(windowPeer) as RootAtSpiNode; |
|||
if (windowNode is null) |
|||
return; |
|||
|
|||
// Emit children-changed("remove") on app root before removal (guarded by event listeners)
|
|||
if (HasEventListeners && _appRootEventHandler is { } eventHandler) |
|||
{ |
|||
var index = _appRoot.WindowChildren.IndexOf(windowNode); |
|||
var childRef = GetReference(windowNode); |
|||
var childVariant = new DBusVariant(childRef.ToDbusStruct()); |
|||
eventHandler.EmitChildrenChangedSignal("remove", index, childVariant); |
|||
} |
|||
|
|||
DetachSubtreeRecursive(windowNode); |
|||
} |
|||
|
|||
public async ValueTask DisposeAsync() |
|||
{ |
|||
lock (_embedSync) |
|||
{ |
|||
_isEmbedded = false; |
|||
_embedTask = null; |
|||
} |
|||
|
|||
_registryTracker?.Dispose(); |
|||
_registryTracker = null; |
|||
|
|||
var nodes = _nodesByPath.Values.ToArray(); |
|||
|
|||
// Dispose all D-Bus registrations before the connection.
|
|||
foreach (var node in nodes) |
|||
{ |
|||
await node.DisposePathRegistrationAsync(); |
|||
} |
|||
|
|||
_appRootRegistration?.Dispose(); |
|||
_appRootRegistration = null; |
|||
_cacheRegistration?.Dispose(); |
|||
_cacheRegistration = null; |
|||
|
|||
if (_a11yConnection is not null) |
|||
{ |
|||
await _a11yConnection.DisposeAsync(); |
|||
_a11yConnection = null; |
|||
} |
|||
|
|||
foreach (var node in nodes) |
|||
ReleaseNode(node); |
|||
|
|||
_uniqueName = string.Empty; |
|||
_cacheHandler = null; |
|||
_appRoot = null; |
|||
_appRootEventHandler = null; |
|||
_nodesByPath.Clear(); |
|||
_nodesByPeer.Clear(); |
|||
} |
|||
|
|||
internal AtSpiObjectReference GetReference(AtSpiNode? node) |
|||
{ |
|||
if (node is null || !node.IsAttached || !_nodesByPath.ContainsKey(node.Path)) |
|||
return GetNullReference(); |
|||
|
|||
return new AtSpiObjectReference(_uniqueName, new DBusObjectPath(node.Path)); |
|||
} |
|||
|
|||
internal AtSpiObjectReference GetNullReference() |
|||
{ |
|||
return new AtSpiObjectReference(string.Empty, new DBusObjectPath(NullPath)); |
|||
} |
|||
|
|||
internal AtSpiObjectReference GetRootReference() |
|||
{ |
|||
return new AtSpiObjectReference(_uniqueName, new DBusObjectPath(RootPath)); |
|||
} |
|||
|
|||
internal void EmitWindowActivationChange(RootAtSpiNode windowNode, bool active) |
|||
{ |
|||
if (_a11yConnection is null || !HasEventListeners) |
|||
return; |
|||
|
|||
if (windowNode.EventObjectHandler is { } eventHandler) |
|||
eventHandler.EmitStateChangedSignal("active", active ? 1 : 0, new DBusVariant(0)); |
|||
|
|||
if (windowNode.EventWindowHandler is not { } windowHandler) |
|||
return; |
|||
|
|||
if (active) |
|||
windowHandler.EmitActivateSignal(); |
|||
else |
|||
windowHandler.EmitDeactivateSignal(); |
|||
} |
|||
|
|||
internal void EmitFocusChange(AtSpiNode? focusedNode) |
|||
{ |
|||
if (_a11yConnection is null || focusedNode is null || !HasEventListeners) |
|||
return; |
|||
|
|||
if (focusedNode.EventObjectHandler is { } eventHandler) |
|||
eventHandler.EmitStateChangedSignal("focused", 1, new DBusVariant(0)); |
|||
} |
|||
|
|||
internal string AllocateNodePath() |
|||
{ |
|||
return $"{AppPathPrefix}/{Interlocked.Increment(ref _nextNodeId)}"; |
|||
} |
|||
|
|||
internal AtSpiNode GetOrCreateNode(AutomationPeer peer) |
|||
{ |
|||
if (_nodesByPeer.TryGetValue(peer, out var node)) |
|||
return node; |
|||
|
|||
node = AtSpiNode.Create(peer, this); |
|||
_nodesByPeer[peer] = node; |
|||
return node; |
|||
} |
|||
|
|||
internal AtSpiNode? TryGetNode(AutomationPeer? peer) |
|||
{ |
|||
if (peer is null) |
|||
return null; |
|||
|
|||
_nodesByPeer.TryGetValue(peer, out var node); |
|||
return node; |
|||
} |
|||
|
|||
internal AtSpiNode? TryGetAttachedNode(AutomationPeer? peer) |
|||
{ |
|||
var node = TryGetNode(peer); |
|||
return node is { IsAttached: true } && _nodesByPath.ContainsKey(node.Path) ? node : null; |
|||
} |
|||
|
|||
internal bool AttachNode(AtSpiNode node, AtSpiNode? parent) |
|||
{ |
|||
if (_a11yConnection is null) |
|||
return false; |
|||
|
|||
if (parent is not null && !parent.IsAttached) |
|||
return false; |
|||
|
|||
if (node.IsAttached) |
|||
{ |
|||
if (!ReferenceEquals(node.Parent, parent)) |
|||
{ |
|||
node.Parent?.RemoveAttachedChild(node); |
|||
node.SetParent(parent); |
|||
} |
|||
|
|||
node.BuildAndRegisterHandlers(_a11yConnection, _syncContext); |
|||
_nodesByPath[node.Path] = node; |
|||
return true; |
|||
} |
|||
|
|||
node.Attach(parent); |
|||
if (!node.IsAttached) |
|||
return false; |
|||
|
|||
_nodesByPath[node.Path] = node; |
|||
return true; |
|||
} |
|||
|
|||
internal void DetachSubtreeRecursive(AtSpiNode rootNode) |
|||
{ |
|||
var toRemove = new List<AtSpiNode>(); |
|||
CollectSubtree(rootNode, toRemove); |
|||
RemoveNodes(toRemove, emitDefunct: true); |
|||
} |
|||
|
|||
private static void CollectSubtree(AtSpiNode node, List<AtSpiNode> result) |
|||
{ |
|||
foreach (var child in node.AttachedChildren.ToArray()) |
|||
CollectSubtree(child, result); |
|||
|
|||
result.Add(node); |
|||
} |
|||
|
|||
private void RemoveNodes(IEnumerable<AtSpiNode> nodes, bool emitDefunct) |
|||
{ |
|||
foreach (var node in nodes) |
|||
{ |
|||
if (!node.IsAttached || !_nodesByPath.ContainsKey(node.Path)) |
|||
continue; |
|||
|
|||
if (emitDefunct && HasEventListeners && node.EventObjectHandler is { } eventHandler) |
|||
eventHandler.EmitStateChangedSignal("defunct", 1, new DBusVariant("0")); |
|||
|
|||
_nodesByPath.Remove(node.Path); |
|||
node.Parent?.RemoveAttachedChild(node); |
|||
|
|||
if (node is RootAtSpiNode rootNode) |
|||
_appRoot?.RemoveWindowChild(rootNode); |
|||
|
|||
ReleaseNode(node); |
|||
} |
|||
} |
|||
|
|||
private async Task BuildAndRegisterAppRootAsync() |
|||
{ |
|||
if (_a11yConnection is null || _appRoot is null) |
|||
return; |
|||
|
|||
_appRootRegistration?.Dispose(); |
|||
_appRootRegistration = null; |
|||
|
|||
var accessibleHandler = new ApplicationAccessibleHandler(this, _appRoot); |
|||
var applicationHandler = new ApplicationNodeApplicationHandler(); |
|||
var eventHandler = new AtSpiEventObjectHandler(this, _appRoot.Path); |
|||
_appRootEventHandler = eventHandler; |
|||
|
|||
var targets = new List<object> { accessibleHandler, applicationHandler, eventHandler }; |
|||
_appRootRegistration = await _a11yConnection.RegisterObjects( |
|||
(DBusObjectPath)RootPath, |
|||
targets, |
|||
_syncContext); |
|||
} |
|||
|
|||
private async Task RegisterCachePathAsync() |
|||
{ |
|||
if (_a11yConnection is null || _cacheHandler is null) |
|||
return; |
|||
|
|||
_cacheRegistration?.Dispose(); |
|||
_cacheRegistration = null; |
|||
|
|||
_cacheRegistration = await _a11yConnection.RegisterObjects( |
|||
(DBusObjectPath)CachePath, |
|||
[_cacheHandler], |
|||
_syncContext); |
|||
} |
|||
|
|||
private async Task<string> GetAccessibilityBusAddressAsync() |
|||
{ |
|||
try |
|||
{ |
|||
await using var connection = await DBusConnection.ConnectSessionAsync(); |
|||
var proxy = new OrgA11yBusProxy(connection, BusNameA11y, new DBusObjectPath(PathA11y)); |
|||
return await proxy.GetAddressAsync(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.FreeDesktopPlatform)? |
|||
.Log(this, "Failed to resolve AT-SPI accessibility bus address: {Exception}", e); |
|||
return string.Empty; |
|||
} |
|||
} |
|||
|
|||
private async Task EmbedApplicationAsync() |
|||
{ |
|||
if (_a11yConnection is null) |
|||
return; |
|||
|
|||
var proxy = new OrgA11yAtspiSocketProxy(_a11yConnection, BusNameRegistry, new DBusObjectPath(RootPath)); |
|||
await proxy.EmbedAsync(new AtSpiObjectReference(_uniqueName, new DBusObjectPath(RootPath))); |
|||
} |
|||
|
|||
private static async Task InitializeRegistryTrackerAsync(AtSpiRegistryEventTracker tracker) |
|||
{ |
|||
try |
|||
{ |
|||
await tracker.InitializeAsync(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
// Registry tracking is best-effort; AT-SPI server remains functional without it.
|
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.FreeDesktopPlatform)? |
|||
.Log(tracker, "AT-SPI registry listener tracking initialization failed: {Exception}", e); |
|||
} |
|||
} |
|||
|
|||
private void ReleaseNode(AtSpiNode node) |
|||
{ |
|||
node.Detach(); |
|||
_nodesByPeer.Remove(node.Peer); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
namespace Avalonia.FreeDesktop.AtSpi; |
|||
|
|||
/// <summary>
|
|||
/// AT-SPI2 state IDs from atspi-constants.h.
|
|||
/// </summary>
|
|||
internal enum AtSpiState : uint |
|||
{ |
|||
Invalid = 0, |
|||
Active = 1, |
|||
Armed = 2, |
|||
Busy = 3, |
|||
Checked = 4, |
|||
Collapsed = 5, |
|||
Defunct = 6, |
|||
Editable = 7, |
|||
Enabled = 8, |
|||
Expandable = 9, |
|||
Expanded = 10, |
|||
Focusable = 11, |
|||
Focused = 12, |
|||
HasToolTip = 13, |
|||
Horizontal = 14, |
|||
Iconified = 15, |
|||
Modal = 16, |
|||
MultiLine = 17, |
|||
MultiSelectable = 18, |
|||
Opaque = 19, |
|||
Pressed = 20, |
|||
Resizable = 21, |
|||
Selectable = 22, |
|||
Selected = 23, |
|||
Sensitive = 24, |
|||
Showing = 25, |
|||
SingleLine = 26, |
|||
Stale = 27, |
|||
Transient = 28, |
|||
Vertical = 29, |
|||
Visible = 30, |
|||
ManagesDescendants = 31, |
|||
Indeterminate = 32, |
|||
Required = 33, |
|||
Truncated = 34, |
|||
Animated = 35, |
|||
InvalidEntry = 36, |
|||
SupportsAutoCompletion = 37, |
|||
SelectableText = 38, |
|||
IsDefault = 39, |
|||
Visited = 40, |
|||
Checkable = 41, |
|||
HasPopup = 42, |
|||
ReadOnly = 43, |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks)</TargetFrameworks> |
|||
<DefineConstants>$(DefineConstants);AVDBUS_INTERNAL</DefineConstants> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
<EnableNETAnalyzers>false</EnableNETAnalyzers> |
|||
<AvDBusInternal>true</AvDBusInternal> |
|||
<AvDBusSourcePath>../../external/Avalonia.DBus/src/Avalonia.DBus</AvDBusSourcePath> |
|||
</PropertyGroup> |
|||
|
|||
<Import Project="../../build/NullableEnable.props" /> |
|||
<Import Project="../../build/DevAnalyzers.props" /> |
|||
<Import Project="../../build/TrimmingEnable.props" /> |
|||
<Import Project="../tools/Avalonia.DBus.Generators/Avalonia.DBus.Generators.props" /> |
|||
|
|||
<ItemGroup> |
|||
<Compile Include="$(AvDBusSourcePath)/**/*.cs" |
|||
Exclude="$(AvDBusSourcePath)/obj/**/*.cs" |
|||
LinkBase="Avalonia.DBus" /> |
|||
<Compile Include="../Avalonia.Controls/Utils/StringUtils.cs"> |
|||
<Link>Shared/StringUtils.cs</Link> |
|||
</Compile> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="../../packages/Avalonia/Avalonia.csproj" /> |
|||
<ProjectReference Include="../tools/Avalonia.DBus.Generators/Avalonia.DBus.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> |
|||
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> |
|||
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup Label="InternalsVisibleTo"> |
|||
<InternalsVisibleTo Include="Avalonia.X11, PublicKey=$(AvaloniaPublicKey)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<AdditionalFiles Include="DBusXml/Status.xml" DBusGeneratorMode="Proxy" /> |
|||
<AdditionalFiles Include="DBusXml/org.a11y.Bus.xml" DBusGeneratorMode="Proxy" /> |
|||
<AdditionalFiles Include="DBusXml/Socket.xml" DBusGeneratorMode="Proxy" /> |
|||
<AdditionalFiles Include="DBusXml/Accessible.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Application.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Component.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Action.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Value.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Event.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Cache.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Image.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Selection.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Text.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/EditableText.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/Collection.xml" DBusGeneratorMode="Handler" /> |
|||
<AdditionalFiles Include="DBusXml/DeviceEventController.xml" DBusGeneratorMode="Proxy" /> |
|||
<AdditionalFiles Include="DBusXml/Registry.xml" DBusGeneratorMode="Proxy" /> |
|||
<AdditionalFiles Include="DBusXml/Types.xml" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,876 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Accessible: |
|||
@short_description: Base interface which is implemented by all accessible objects. |
|||
--> |
|||
<interface name="org.a11y.atspi.Accessible"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
Name: Human-readable, localized, short name for the object. |
|||
|
|||
Normally you need to set this for objects which do not have a labelled-by |
|||
relation. Consider a widget to select RGB colors by setting three sliders. The |
|||
names for the sliders would be "Red", "Green", "Blue", respectively, or |
|||
translations to application's locale. The names would be unnecessary if each |
|||
slider had a labelled-by relation to corresponding labels visible in the user |
|||
interface. |
|||
|
|||
In general, something is missing from your application if an object that can be |
|||
interacted with has no Name or a labelled-by relation. |
|||
--> |
|||
<property name="Name" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
Description: Human-readable, localized description of the object in more detail. |
|||
|
|||
While the Name property is meant to be a short string that screen readers say |
|||
during normal navigation, the Description property is for when the user asks for |
|||
more detail. |
|||
--> |
|||
<property name="Description" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
Parent: Accessible parent object of the current object. |
|||
|
|||
The (so) is a string for the application name, and an object path. |
|||
|
|||
Null parent: If the object has no parent (e.g. the application's root object is |
|||
being queried), return "" for the application name name and "/org/a11y/atspi/null" |
|||
for the object path. |
|||
|
|||
Root object: An application must have a single root object, called |
|||
"/org/a11y/atspi/accessible/root". All other objects should have that one as |
|||
their highest-level ancestor. |
|||
--> |
|||
<property name="Parent" type="(so)" access="read"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</property> |
|||
|
|||
<!-- |
|||
ChildCount: number of accessible children for this object. |
|||
--> |
|||
<property name="ChildCount" type="i" access="read"/> |
|||
|
|||
<!-- |
|||
Locale: Unix locale for the current object. |
|||
|
|||
For an application, this may be the locale for the language that the application |
|||
shows in its user interface. |
|||
|
|||
For a document being shown in an application, or a paragraph within a document, |
|||
the locale may refer to that object exclusively. For example, an application may |
|||
be showing itself in English ("en"), but it may be used to display a document in |
|||
Spanish ("es"). In the latter case, a screen reader will want to know that it |
|||
should switch to Spanish while reading the document. |
|||
--> |
|||
<property name="Locale" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
AccessibleId: application-specific identifier for the current object. |
|||
|
|||
This can be used to give a special id to an object for reliable identification by |
|||
ATs or for use in tests, for example, "my_widget". Note that there is no way to |
|||
directly find an object by its id; ATs may have to recursively get the children to |
|||
find a specific id. This is because accessible objects can be created dynamically, |
|||
and they do not always correspond to a static view of an application's data. |
|||
--> |
|||
<property name="AccessibleId" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
HelpText: help text for the current object. |
|||
--> |
|||
<property name="HelpText" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
GetChildAtIndex: |
|||
|
|||
@index: 0-based index of the child to query. |
|||
|
|||
Queries the Nth accessible child of the current object. It is expected that this |
|||
will correspond to the order that the GetChildren method would return. |
|||
|
|||
Returns: The (so) is a string for the application name, and an object path. |
|||
|
|||
Notes: implementations vary in their behavior when the index is out of range. |
|||
GTK4 returns an error, while atk-adaptor returns the null object path |
|||
"/org/a11y/atspi/null". To keep the type system gods happy, you should probably |
|||
return a DBus error in that case. |
|||
--> |
|||
<method name="GetChildAtIndex"> |
|||
<arg direction="in" name="index" type="i"/> |
|||
<arg direction="out" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetChildren: |
|||
|
|||
Returns a list of the object's accessible children. |
|||
|
|||
Each array element (so) is a string for the application name, and an object path. |
|||
|
|||
An implementor may return an error if the object exposes a large |
|||
number of children such that returning a list would be prohibitive. |
|||
--> |
|||
<method name="GetChildren"> |
|||
<arg direction="out" type="a(so)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetIndexInParent: |
|||
|
|||
Returns the 0-based index at which the object would be returned by calling |
|||
GetChildren on its parent, or -1 if the object has no containing |
|||
parent or on exception. |
|||
--> |
|||
<method name="GetIndexInParent"> |
|||
<arg direction="out" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetRelationSet: |
|||
|
|||
Returns a set of relationships between the current object and others. Each |
|||
element in the outermost array contains a number that indicates the relation type |
|||
(see below), and an array of references to accessible objects to which that |
|||
relationship applies. Each element in the inner array is a (so) with a string for |
|||
the application name, and an object path. |
|||
|
|||
Each relationship between objects (possibly one-to-many or many-to-one) allows |
|||
better semantic identification of how objects are associated with one another. |
|||
For instance, the ATSPI_RELATION_LABELLED_BY relationship may be used to identify |
|||
labelling information that should accompany the accessible name property when |
|||
presenting an object's content or identity to the end user. Similarly, |
|||
ATSPI_RELATION_CONTROLLER_FOR can be used to further specify the context in which |
|||
a valuator is useful, and/or the other UI components which are directly effected |
|||
by user interactions with the valuator. Common examples include association of |
|||
scrollbars with the viewport or panel which they control. |
|||
|
|||
Relation types - these are the enum values from AtspiRelationType in atspi-constants.h: |
|||
|
|||
0 - ATSPI_RELATION_NULL: Not a meaningful relationship; clients should not |
|||
normally encounter this value. |
|||
|
|||
1 - ATSPI_RELATION_LABEL_FOR: Object is a label for one or more other objects. |
|||
|
|||
2 - ATSPI_RELATION_LABELLED_BY: Object is labelled by one or more other |
|||
objects. |
|||
|
|||
3 - ATSPI_RELATION_CONTROLLER_FOR: Object is an interactive object which |
|||
modifies the state, onscreen location, or other attributes of one or more |
|||
target objects. |
|||
|
|||
4 - ATSPI_RELATION_CONTROLLED_BY: Object state, position, etc. is |
|||
modified/controlled by user interaction with one or more other objects. |
|||
For instance a viewport or scroll pane may be ATSPI_RELATION_CONTROLLED_BY |
|||
scrollbars. |
|||
|
|||
5 - ATSPI_RELATION_MEMBER_OF: Object has a grouping relationship (e.g. 'same |
|||
group as') to one or more other objects. |
|||
|
|||
6 - ATSPI_RELATION_TOOLTIP_FOR: Object is a tooltip associated with another |
|||
object. |
|||
|
|||
7 - ATSPI_RELATION_NODE_CHILD_OF: Object is a child of the target. |
|||
|
|||
8 - ATSPI_RELATION_NODE_PARENT_OF: Object is a parent of the target. |
|||
|
|||
9 - ATSPI_RELATION_EXTENDED: Used to indicate that a relationship exists, but |
|||
its type is not specified in the enumeration. |
|||
|
|||
10 - ATSPI_RELATION_FLOWS_TO: Object renders content which flows logically to |
|||
another object. For instance, text in a paragraph may flow to another |
|||
object which is not the 'next sibling' in the accessibility hierarchy. |
|||
|
|||
11 - ATSPI_RELATION_FLOWS_FROM: Reciprocal of ATSPI_RELATION_FLOWS_TO. |
|||
|
|||
12 - ATSPI_RELATION_SUBWINDOW_OF: Object is visually and semantically considered |
|||
a subwindow of another object, even though it is not the object's child. |
|||
Useful when dealing with embedded applications and other cases where the |
|||
widget hierarchy does not map cleanly to the onscreen presentation. |
|||
|
|||
13 - ATSPI_RELATION_EMBEDS: Similar to ATSPI_RELATION_SUBWINDOW_OF, but |
|||
specifically used for cross-process embedding. |
|||
|
|||
14 - ATSPI_RELATION_EMBEDDED_BY: Reciprocal of ATSPI_RELATION_EMBEDS. Used to |
|||
denote content rendered by embedded renderers that live in a separate process |
|||
space from the embedding context. |
|||
|
|||
15 - ATSPI_RELATION_POPUP_FOR: Denotes that the object is a transient window or |
|||
frame associated with another onscreen object. Similar to ATSPI_TOOLTIP_FOR, |
|||
but more general. Useful for windows which are technically toplevels |
|||
but which, for one or more reasons, do not explicitly cause their |
|||
associated window to lose 'window focus'. Creation of an ATSPI_ROLE_WINDOW |
|||
object with the ATSPI_RELATION_POPUP_FOR relation usually requires |
|||
some presentation action on the part of assistive technology clients, |
|||
even though the previous toplevel ATSPI_ROLE_FRAME object may still be |
|||
the active window. |
|||
|
|||
16 - ATSPI_RELATION_PARENT_WINDOW_OF: This is the reciprocal relation to |
|||
ATSPI_RELATION_POPUP_FOR. |
|||
|
|||
17 - ATSPI_RELATION_DESCRIPTION_FOR: Reciprocal of ATSPI_RELATION_DESCRIBED_BY. |
|||
Indicates that this object provides descriptive information about the target |
|||
object(s). See also ATSPI_RELATION_DETAILS_FOR and ATSPI_RELATION_ERROR_FOR. |
|||
|
|||
18 - ATSPI_RELATION_DESCRIBED_BY: Reciprocal of ATSPI_RELATION_DESCRIPTION_FOR. |
|||
Indicates that one or more target objects provide descriptive information |
|||
about this object. This relation type is most appropriate for information |
|||
that is not essential as its presentation may be user-configurable and/or |
|||
limited to an on-demand mechanism such as an assistive technology command. |
|||
For brief, essential information such as can be found in a widget's on-screen |
|||
label, use ATSPI_RELATION_LABELLED_BY. For an on-screen error message, use |
|||
ATSPI_RELATION_ERROR_MESSAGE. For lengthy extended descriptive information |
|||
contained in an on-screen object, consider using ATSPI_RELATION_DETAILS as |
|||
assistive technologies may provide a means for the user to navigate to |
|||
objects containing detailed descriptions so that their content can be more |
|||
closely reviewed. |
|||
|
|||
19 - ATSPI_RELATION_DETAILS: Reciprocal of ATSPI_RELATION_DETAILS_FOR. Indicates |
|||
that this object has a detailed or extended description, the contents of |
|||
which can be found in the target object(s). This relation type is most |
|||
appropriate for information that is sufficiently lengthy as to make |
|||
navigation to the container of that information desirable. For less verbose |
|||
information suitable for announcement only, see ATSPI_RELATION_DESCRIBED_BY. |
|||
If the detailed information describes an error condition, |
|||
ATSPI_RELATION_ERROR_FOR should be used instead. Since 2.26. |
|||
|
|||
20 - ATSPI_RELATION_DETAILS_FOR: Reciprocal of ATSPI_RELATION_DETAILS. Indicates |
|||
that this object provides a detailed or extended description about the target |
|||
object(s). See also ATSPI_RELATION_DESCRIPTION_FOR and ATSPI_RELATION_ERROR_FOR. |
|||
Since 2.26. |
|||
|
|||
21 - ATSPI_RELATION_ERROR_MESSAGE: Reciprocal of ATSPI_RELATION_ERROR_FOR. |
|||
Indicates that this object has one or more errors, the nature of which is |
|||
described in the contents of the target object(s). Objects that have this |
|||
relation type should also contain ATSPI_STATE_INVALID_ENTRY when their |
|||
GetState method is called. Since: 2.26. |
|||
|
|||
22 - ATSPI_RELATION_ERROR_FOR: Reciprocal of ATSPI_RELATION_ERROR_MESSAGE. |
|||
Indicates that this object contains an error message describing an invalid |
|||
condition in the target object(s). Since: 2.26. |
|||
--> |
|||
<method name="GetRelationSet"> |
|||
<arg direction="out" type="a(ua(so))"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiRelationEntry"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetRole: |
|||
|
|||
Gets the accessible role that the current object represents. Roles make it |
|||
possible for various UI toolkits to expose their controls to assistive |
|||
technologies (ATs) with a standard interface, regardless of toolkit. For example, |
|||
a widget that acts like a conventional push button (appears unpressed; presses |
|||
when acted upon; invokes a certain action when pressed) can expose an |
|||
ATSPI_ROLE_BUTTON role. |
|||
|
|||
Role values - these are the enum values from AtspiRole in atspi-constants.h: |
|||
|
|||
- 0 - ATSPI_ROLE_INVALID: A role indicating an error condition, such as |
|||
uninitialized Role data. |
|||
- 1 - ATSPI_ROLE_ACCELERATOR_LABEL: Object is a label indicating the keyboard |
|||
accelerators for the parent. |
|||
- 2 - ATSPI_ROLE_ALERT: Object is used to alert the user about something. |
|||
- 3 - ATSPI_ROLE_ANIMATION: Object contains a dynamic or moving image of some |
|||
kind. |
|||
- 4 - ATSPI_ROLE_ARROW: Object is a 2D directional indicator. |
|||
- 5 - ATSPI_ROLE_CALENDAR: Object contains one or more dates, usually arranged |
|||
into a 2D list. |
|||
- 6 - ATSPI_ROLE_CANVAS: Object that can be drawn into and is used to trap |
|||
events. |
|||
- 7 - ATSPI_ROLE_CHECK_BOX: A choice that can be checked or unchecked and |
|||
provides a separate indicator for the current state. |
|||
- 8 - ATSPI_ROLE_CHECK_MENU_ITEM: A menu item that behaves like a check box. See |
|||
ATSPI_ROLE_CHECK_BOX. |
|||
- 9 - ATSPI_ROLE_COLOR_CHOOSER: A specialized dialog that lets the user choose a |
|||
color. |
|||
- 10 - ATSPI_ROLE_COLUMN_HEADER: The header for a column of data. |
|||
- 11 - ATSPI_ROLE_COMBO_BOX: A list of choices the user can select from. |
|||
- 12 - ATSPI_ROLE_DATE_EDITOR: An object which allows entry of a date. |
|||
- 13 - ATSPI_ROLE_DESKTOP_ICON: An inconified internal frame within a DESKTOP_PANE. |
|||
- 14 - ATSPI_ROLE_DESKTOP_FRAME: A pane that supports internal frames and |
|||
iconified versions of those internal frames. |
|||
- 15 - ATSPI_ROLE_DIAL: An object that allows a value to be changed via rotating a |
|||
visual element, or which displays a value via such a rotating element. |
|||
- 16 - ATSPI_ROLE_DIALOG: A top level window with title bar and a border. |
|||
- 17 - ATSPI_ROLE_DIRECTORY_PANE: A pane that allows the user to navigate through |
|||
and select the contents of a directory. |
|||
- 18 - ATSPI_ROLE_DRAWING_AREA: An object used for drawing custom user interface |
|||
elements. |
|||
- 19 - ATSPI_ROLE_FILE_CHOOSER: A specialized dialog that displays the files in |
|||
the directory and lets the user select a file, browse a different |
|||
directory, or specify a filename. |
|||
- 20 - ATSPI_ROLE_FILLER: A object that fills up space in a user interface. |
|||
- 21 - ATSPI_ROLE_FOCUS_TRAVERSABLE: Don't use, reserved for future use. |
|||
- 22 - ATSPI_ROLE_FONT_CHOOSER: Allows selection of a display font. |
|||
- 23 - ATSPI_ROLE_FRAME: A top level window with a title bar, border, menubar, |
|||
etc. |
|||
- 24 - ATSPI_ROLE_GLASS_PANE: A pane that is guaranteed to be painted on top of |
|||
all panes beneath it. |
|||
- 25 - ATSPI_ROLE_HTML_CONTAINER: A document container for HTML, whose children |
|||
represent the document content. |
|||
- 26 - ATSPI_ROLE_ICON: A small fixed size picture, typically used to decorate |
|||
components. |
|||
- 27 - ATSPI_ROLE_IMAGE: An image, typically static. |
|||
- 28 - ATSPI_ROLE_INTERNAL_FRAME: A frame-like object that is clipped by a desktop |
|||
pane. |
|||
- 29 - ATSPI_ROLE_LABEL: An object used to present an icon or short string in an |
|||
interface. |
|||
- 30 - ATSPI_ROLE_LAYERED_PANE: A specialized pane that allows its children to be |
|||
drawn in layers, providing a form of stacking order. |
|||
- 31 - ATSPI_ROLE_LIST: An object that presents a list of objects to the user and |
|||
allows the user to select one or more of them. |
|||
- 32 - ATSPI_ROLE_LIST_ITEM: An object that represents an element of a list. |
|||
- 33 - ATSPI_ROLE_MENU: An object usually found inside a menu bar that contains a |
|||
list of actions the user can choose from. |
|||
- 34 - ATSPI_ROLE_MENU_BAR: An object usually drawn at the top of the primary |
|||
dialog box of an application that contains a list of menus the user can |
|||
x choose from. |
|||
- 35 - ATSPI_ROLE_MENU_ITEM: An object usually contained in a menu that presents |
|||
an action the user can choose. |
|||
- 36 - ATSPI_ROLE_OPTION_PANE: A specialized pane whose primary use is inside a |
|||
dialog. |
|||
- 37 - ATSPI_ROLE_PAGE_TAB: An object that is a child of a page tab list. |
|||
- 38 - ATSPI_ROLE_PAGE_TAB_LIST: An object that presents a series of panels (or |
|||
page tabs), one at a time,through some mechanism provided by the |
|||
object. |
|||
- 39 - ATSPI_ROLE_PANEL: A generic container that is often used to group objects. |
|||
- 40 - ATSPI_ROLE_PASSWORD_TEXT: A text object uses for passwords, or other places |
|||
where the text content is not shown visibly to the user. |
|||
- 41 - ATSPI_ROLE_POPUP_MENU: A temporary window that is usually used to offer the |
|||
user a list of choices, and then hides when the user selects one of those |
|||
choices. |
|||
- 42 - ATSPI_ROLE_PROGRESS_BAR: An object used to indicate how much of a task has |
|||
been completed. |
|||
- 43 - ATSPI_ROLE_BUTTON: An object the user can manipulate to tell the |
|||
application to do something. |
|||
- 44 - ATSPI_ROLE_RADIO_BUTTON: A specialized check box that will cause other |
|||
radio buttons in the same group to become unchecked when this one is |
|||
checked. |
|||
- 45 - ATSPI_ROLE_RADIO_MENU_ITEM: Object is both a menu item and a "radio button". |
|||
See ATSPI_ROLE_RADIO_BUTTON. |
|||
- 46 - ATSPI_ROLE_ROOT_PANE: A specialized pane that has a glass pane and a |
|||
layered pane as its children. |
|||
- 47 - ATSPI_ROLE_ROW_HEADER: The header for a row of data. |
|||
- 48 - ATSPI_ROLE_SCROLL_BAR: An object usually used to allow a user to |
|||
incrementally view a large amount of data by moving the bounds of a |
|||
viewport along a one-dimensional axis. |
|||
- 49 - ATSPI_ROLE_SCROLL_PANE: An object that allows a user to incrementally view |
|||
a large amount of information. Scroll pane objects are usually |
|||
accompanied by ATSPI_ROLE_SCROLL_BAR controllers, on which the |
|||
ATSPI_RELATION_CONTROLLER_FOR and ATSPI_RELATION_CONTROLLED_BY |
|||
reciprocal relations are set. See the GetRelationSet method. |
|||
- 50 - ATSPI_ROLE_SEPARATOR: An object usually contained in a menu to provide a |
|||
visible and logical separation of the contents in a menu. |
|||
- 51 - ATSPI_ROLE_SLIDER: An object that allows the user to select from a bounded range. |
|||
Unlike ATSPI_ROLE_SCROLL_BAR, ATSPI_ROLE_SLIDER objects need not control |
|||
'viewport'-like objects. |
|||
- 52 - ATSPI_ROLE_SPIN_BUTTON: An object which allows one of a set of choices to |
|||
be selected, and which displays the current choice. |
|||
- 53 - ATSPI_ROLE_SPLIT_PANE: A specialized panel that presents two other panels |
|||
at the same time. |
|||
- 54 - ATSPI_ROLE_STATUS_BAR: Object displays non-quantitative status information |
|||
(c.f. ATSPI_ROLE_PROGRESS_BAR) |
|||
- 55 - ATSPI_ROLE_TABLE: An object used to represent information in terms of rows |
|||
and columns. |
|||
- 56 - ATSPI_ROLE_TABLE_CELL: A 'cell' or discrete child within a Table. Note: |
|||
Table cells need not have ATSPI_ROLE_TABLE_CELL, other |
|||
role values are valid as well. |
|||
- 57 - ATSPI_ROLE_TABLE_COLUMN_HEADER: An object which labels a particular column |
|||
in a Table interface interface. |
|||
- 58 - ATSPI_ROLE_TABLE_ROW_HEADER: An object which labels a particular row in a |
|||
Table interface. Table rows and columns may also be labelled via the |
|||
ATSPI_RELATION_LABEL_FOR/ATSPI_RELATION_LABELLED_BY relationships; see |
|||
the GetRelationSet method. |
|||
- 59 - ATSPI_ROLE_TEAROFF_MENU_ITEM: Object allows menu to be removed from menubar |
|||
and shown in its own window. |
|||
- 60 - ATSPI_ROLE_TERMINAL: An object that emulates a terminal. |
|||
- 61 - ATSPI_ROLE_TEXT: An interactive widget that supports multiple lines of text |
|||
and optionally accepts user input, but whose purpose is not to solicit user |
|||
input. Thus ATSPI_ROLE_TEXT is appropriate for the text view in a plain text |
|||
editor but inappropriate for an input field in a dialog box or web form. For |
|||
widgets whose purpose is to solicit input from the user, see ATSPI_ROLE_ENTRY |
|||
and ATSPI_ROLE_PASSWORD_TEXT. For generic objects which display a brief amount |
|||
of textual information, see ATSPI_ROLE_STATIC. |
|||
- 62 - ATSPI_ROLE_TOGGLE_BUTTON: A specialized push button that can be checked or |
|||
unchecked, but does not procide a separate indicator for the current |
|||
state. |
|||
- 63 - ATSPI_ROLE_TOOL_BAR: A bar or palette usually composed of push buttons or |
|||
toggle buttons. |
|||
- 64 - ATSPI_ROLE_TOOL_TIP: An object that provides information about another |
|||
object. |
|||
- 65 - ATSPI_ROLE_TREE: An object used to repsent hierarchical information to the |
|||
user. |
|||
- 66 - ATSPI_ROLE_TREE_TABLE: An object that presents both tabular and |
|||
hierarchical info to the user. |
|||
- 67 - ATSPI_ROLE_UNKNOWN: The object contains some accessible information, |
|||
but its role is not known. |
|||
- 68 - ATSPI_ROLE_VIEWPORT: An object usually used in a scroll pane, or to |
|||
otherwise clip a larger object or content renderer to a specific |
|||
onscreen viewport. |
|||
- 69 - ATSPI_ROLE_WINDOW: A top level window with no title or border. |
|||
- 70 - ATSPI_ROLE_EXTENDED: means that the role for this item is known, but not |
|||
included in the core enumeration. Deprecated since 2.24. |
|||
- 71 - ATSPI_ROLE_HEADER: An object that serves as a document header. |
|||
- 72 - ATSPI_ROLE_FOOTER: An object that serves as a document footer. |
|||
- 73 - ATSPI_ROLE_PARAGRAPH: An object which is contains a single paragraph of |
|||
text content. See also ATSPI_ROLE_TEXT. |
|||
- 74 - ATSPI_ROLE_RULER: An object which describes margins and tab stops, etc. |
|||
for text objects which it controls (should have |
|||
ATSPI_RELATION_CONTROLLER_FOR relation to such). |
|||
- 75 - ATSPI_ROLE_APPLICATION: An object corresponding to the toplevel accessible |
|||
of an application, which may contain ATSPI_ROLE_FRAME objects or other |
|||
accessible objects. Children of objects with the ATSPI_ROLE_DESKTOP_FRAME role |
|||
are generally ATSPI_ROLE_APPLICATION objects. |
|||
- 76 - ATSPI_ROLE_AUTOCOMPLETE: The object is a dialog or list containing items |
|||
for insertion into an entry widget, for instance a list of words for |
|||
completion of a text entry. |
|||
- 77 - ATSPI_ROLE_EDITBAR: The object is an editable text object in a toolbar. |
|||
- 78 - ATSPI_ROLE_EMBEDDED: The object is an embedded component container. This |
|||
role is a "grouping" hint that the contained objects share a context |
|||
which is different from the container in which this accessible is |
|||
embedded. In particular, it is used for some kinds of document embedding, |
|||
and for embedding of out-of-process component, "panel applets", etc. |
|||
- 79 - ATSPI_ROLE_ENTRY: The object is a component whose textual content may be |
|||
entered or modified by the user, provided ATSPI_STATE_EDITABLE is present. |
|||
A readonly ATSPI_ROLE_ENTRY object (i.e. where ATSPI_STATE_EDITABLE is |
|||
not present) implies a read-only 'text field' in a form, as opposed to a |
|||
title, label, or caption. |
|||
- 80 - ATSPI_ROLE_CHART: The object is a graphical depiction of quantitative data. |
|||
It may contain multiple subelements whose attributes and/or description |
|||
may be queried to obtain both the quantitative data and information about |
|||
how the data is being presented. The ATSPI_LABELLED_BY relation is |
|||
particularly important in interpreting objects of this type, as is the |
|||
accessible description property. See ATSPI_ROLE_CAPTION. |
|||
- 81 - ATSPI_ROLE_CAPTION: The object contains descriptive information, usually |
|||
textual, about another user interface element such as a table, chart, or |
|||
image. |
|||
- 82 - ATSPI_ROLE_DOCUMENT_FRAME: The object is a visual frame or container which |
|||
contains a view of document content. Document frames may occur within |
|||
another Document instance, in which case the second document may be |
|||
said to be embedded in the containing instance. HTML frames are often |
|||
ATSPI_ROLE_DOCUMENT_FRAME: Either this object, or a singleton descendant, |
|||
should implement the org.a11y.atspi.Document interface. |
|||
- 83 - ATSPI_ROLE_HEADING: The object serves as a heading for content which |
|||
follows it in a document. The 'heading level' of the heading, if |
|||
available, may be obtained by querying the object's attributes. |
|||
- 84 - ATSPI_ROLE_PAGE: The object is a containing instance which encapsulates a |
|||
page of information. ATSPI_ROLE_PAGE is used in documents and content which |
|||
support a paginated navigation model. |
|||
- 85 - ATSPI_ROLE_SECTION: The object is a containing instance of document content |
|||
which constitutes a particular 'logical' section of the document. The |
|||
type of content within a section, and the nature of the section division |
|||
itself, may be obtained by querying the object's attributes. Sections |
|||
may be nested. |
|||
- 86 - ATSPI_ROLE_REDUNDANT_OBJECT: The object is redundant with another object in |
|||
the hierarchy, and is exposed for purely technical reasons. Objects of |
|||
this role should be ignored by clients, if they are encountered at all. |
|||
- 87 - ATSPI_ROLE_FORM: The object is a containing instance of document content |
|||
which has within it components with which the user can interact in order |
|||
to input information; i.e. the object is a container for pushbuttons, |
|||
comboboxes, text input fields, and other 'GUI' components. ATSPI_ROLE_FORM |
|||
should not, in general, be used for toplevel GUI containers or dialogs, |
|||
but should be reserved for 'GUI' containers which occur within document |
|||
content, for instance within Web documents, presentations, or text |
|||
documents. Unlike other GUI containers and dialogs which occur inside |
|||
application instances, ATSPI_ROLE_FORM containers' components are |
|||
associated with the current document, rather than the current foreground |
|||
application or viewer instance. |
|||
- 88 - ATSPI_ROLE_LINK: The object is a hypertext anchor, i.e. a "link" in a |
|||
hypertext document. Such objects are distinct from 'inline' content |
|||
which may also use the Hypertext/Hyperlink interfaces to indicate |
|||
the range/location within a text object where an inline or embedded object |
|||
lies. |
|||
- 89 - ATSPI_ROLE_INPUT_METHOD_WINDOW: The object is a window or similar viewport |
|||
which is used to allow composition or input of a 'complex character', |
|||
in other words it is an "input method window". |
|||
- 90 - ATSPI_ROLE_TABLE_ROW: A row in a table. |
|||
- 91 - ATSPI_ROLE_TREE_ITEM: An object that represents an element of a tree. |
|||
- 92 - ATSPI_ROLE_DOCUMENT_SPREADSHEET: A document frame which contains a |
|||
spreadsheet. |
|||
- 93 - ATSPI_ROLE_DOCUMENT_PRESENTATION: A document frame which contains a |
|||
presentation or slide content. |
|||
- 94 - ATSPI_ROLE_DOCUMENT_TEXT: A document frame which contains textual content, |
|||
such as found in a word processing application. |
|||
- 95 - ATSPI_ROLE_DOCUMENT_WEB: A document frame which contains HTML or other |
|||
markup suitable for display in a web browser. |
|||
- 96 - ATSPI_ROLE_DOCUMENT_EMAIL: A document frame which contains email content |
|||
to be displayed or composed either in plain text or HTML. |
|||
- 97 - ATSPI_ROLE_COMMENT: An object found within a document and designed to |
|||
present a comment, note, or other annotation. In some cases, this object |
|||
might not be visible until activated. |
|||
- 98 - ATSPI_ROLE_LIST_BOX: A non-collapsible list of choices the user can select from. |
|||
- 99 - ATSPI_ROLE_GROUPING: A group of related widgets. This group typically has a label. |
|||
- 100 - ATSPI_ROLE_IMAGE_MAP: An image map object. Usually a graphic with multiple |
|||
hotspots, where each hotspot can be activated resulting in the loading of |
|||
another document or section of a document. |
|||
- 101 - ATSPI_ROLE_NOTIFICATION: A transitory object designed to present a |
|||
message to the user, typically at the desktop level rather than inside a |
|||
particular application. |
|||
- 102 - ATSPI_ROLE_INFO_BAR: An object designed to present a message to the user |
|||
within an existing window. |
|||
- 103 - ATSPI_ROLE_LEVEL_BAR: A bar that serves as a level indicator to, for |
|||
instance, show the strength of a password or the state of a battery. Since: 2.8 |
|||
- 104 - ATSPI_ROLE_TITLE_BAR: A bar that serves as the title of a window or a |
|||
dialog. Since: 2.12 |
|||
- 105 - ATSPI_ROLE_BLOCK_QUOTE: An object which contains a text section |
|||
that is quoted from another source. Since: 2.12 |
|||
- 106 - ATSPI_ROLE_AUDIO: An object which represents an audio |
|||
element. Since: 2.12 |
|||
- 107 - ATSPI_ROLE_VIDEO: An object which represents a video |
|||
element. Since: 2.12 |
|||
- 108 - ATSPI_ROLE_DEFINITION: A definition of a term or concept. Since: 2.12 |
|||
- 109 - ATSPI_ROLE_ARTICLE: A section of a page that consists of a |
|||
composition that forms an independent part of a document, page, or |
|||
site. Examples: A blog entry, a news story, a forum post. Since: 2.12 |
|||
- 110 - ATSPI_ROLE_LANDMARK: A region of a web page intended as a |
|||
navigational landmark. This is designed to allow Assistive |
|||
Technologies to provide quick navigation among key regions within a |
|||
document. Since: 2.12 |
|||
- 111 - ATSPI_ROLE_LOG: A text widget or container holding log content, such |
|||
as chat history and error logs. In this role there is a |
|||
relationship between the arrival of new items in the log and the |
|||
reading order. The log contains a meaningful sequence and new |
|||
information is added only to the end of the log, not at arbitrary |
|||
points. Since: 2.12 |
|||
- 112 - ATSPI_ROLE_MARQUEE: A container where non-essential information |
|||
changes frequently. Common usages of marquee include stock tickers |
|||
and ad banners. The primary difference between a marquee and a log |
|||
is that logs usually have a meaningful order or sequence of |
|||
important content changes. Since: 2.12 |
|||
- 113 - ATSPI_ROLE_MATH: A text widget or container that holds a mathematical |
|||
expression. Since: 2.12 |
|||
- 114 - ATSPI_ROLE_RATING: A widget whose purpose is to display a rating, |
|||
such as the number of stars associated with a song in a media |
|||
player. Objects of this role should also implement |
|||
the Value interface. Since: 2.12 |
|||
- 115 - ATSPI_ROLE_TIMER: An object containing a numerical counter which |
|||
indicates an amount of elapsed time from a start point, or the time |
|||
remaining until an end point. Since: 2.12 |
|||
- 116 - ATSPI_ROLE_STATIC: A generic non-container object whose purpose is to display |
|||
a brief amount of information to the user and whose role is known by the |
|||
implementor but lacks semantic value for the user. Examples in which |
|||
ATSPI_ROLE_STATIC is appropriate include the message displayed in a message |
|||
box and an image used as an alternative means to display text. |
|||
ATSPI_ROLE_STATIC should not be applied to widgets which are traditionally |
|||
interactive, objects which display a significant amount of content, or any |
|||
object which has an accessible relation pointing to another object. The |
|||
displayed information, as a general rule, should be exposed through the |
|||
accessible name of the object. For labels which describe another widget, see |
|||
ATSPI_ROLE_LABEL. For text views, see ATSPI_ROLE_TEXT. For generic |
|||
containers, see ATSPI_ROLE_PANEL. For objects whose role is not known by the |
|||
implementor, see ATSPI_ROLE_UNKNOWN. Since: 2.16. |
|||
- 117 - ATSPI_ROLE_MATH_FRACTION: An object that represents a mathematical fraction. Since: 2.16. |
|||
- 118 - ATSPI_ROLE_MATH_ROOT: An object that represents a mathematical expression |
|||
displayed with a radical. Since: 2.16. |
|||
- 119 - ATSPI_ROLE_SUBSCRIPT: An object that contains text that is displayed as a |
|||
subscript. Since: 2.16. |
|||
- 120 - ATSPI_ROLE_SUPERSCRIPT: An object that contains text that is displayed as a |
|||
superscript. Since: 2.16. |
|||
- 121 - ATSPI_ROLE_DESCRIPTION_LIST: An object that represents a list of term-value |
|||
groups. A term-value group represents an individual description and consist |
|||
of one or more names (ATSPI_ROLE_DESCRIPTION_TERM) followed by one or more |
|||
values (ATSPI_ROLE_DESCRIPTION_VALUE). For each list, there should not be |
|||
more than one group with the same term name. Since: 2.26. |
|||
- 122 - ATSPI_ROLE_DESCRIPTION_TERM: An object that represents a term or phrase |
|||
with a corresponding definition. Since: 2.26. |
|||
- 123 - ATSPI_ROLE_DESCRIPTION_VALUE: An object that represents the description, |
|||
definition, or value of a term. Since: 2.26. |
|||
- 124 - ATSPI_ROLE_FOOTNOTE: An object that contains the text of a footnote. Since: 2.26. |
|||
- 125 - ATSPI_ROLE_CONTENT_DELETION: Content previously deleted or proposed to be |
|||
deleted, e.g. in revision history or a content view providing suggestions |
|||
from reviewers. Since: 2.34. |
|||
- 126 - ATSPI_ROLE_CONTENT_INSERTION: Content previously inserted or proposed to be |
|||
inserted, e.g. in revision history or a content view providing suggestions |
|||
from reviewers. Since: 2.34. |
|||
- 127 - ATSPI_ROLE_MARK: A run of content that is marked or highlighted, such as for |
|||
reference purposes, or to call it out as having a special purpose. If the |
|||
marked content has an associated section in the document elaborating on the |
|||
reason for the mark, then an ATSPI_RELATION_DETAILS relation should be used on the mark |
|||
to point to that associated section. In addition, the reciprocal relation |
|||
ATSPI_RELATION_DETAILS_FOR should be used on the associated content section |
|||
to point back to the mark. See the GetRelationSet method. Since: 2.36. |
|||
- 128 - ATSPI_ROLE_SUGGESTION: A container for content that is called out as a |
|||
proposed change from the current version of the document, such as by a reviewer of |
|||
the content. An object with this role should include children with |
|||
ATSPI_ROLE_CONTENT_DELETION and/or ATSPI_ROLE_CONTENT_INSERTION, in any order, to |
|||
indicate what the actual change is. Since: 2.36 |
|||
- 129 - ATSPI_ROLE_PUSH_BUTTON_MENU: A specialized push button to open a menu. Since: 2.46 |
|||
- 130 - ATSPI_ROLE_SWITCH: A switch that can be toggled on/off. Since: 2.56 |
|||
--> |
|||
<method name="GetRole"> |
|||
<arg direction="out" type="u"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetRoleName: |
|||
|
|||
Gets a UTF-8 string corresponding to the name of the role played by an object. |
|||
This method will return useful values for roles that fall outside the |
|||
enumeration used in the GetRole method. Implementing this method is |
|||
optional, and it may be removed in a future version of the API. |
|||
Libatspi will only call id in the event of an unknown role. |
|||
--> |
|||
<method name="GetRoleName"> |
|||
<arg direction="out" type="s"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetLocalizedRoleName: |
|||
|
|||
Gets a UTF-8 string corresponding to the name of the role played by an object, translated |
|||
to the current locale. |
|||
This method will return useful values for roles that fall outside the |
|||
enumeration used in the GetRole method. Implementing this method is |
|||
optional, and it may be removed in a future version of the API. |
|||
Libatspi will only call id in the event of an unknown role. |
|||
--> |
|||
<method name="GetLocalizedRoleName"> |
|||
<arg direction="out" type="s"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetState: |
|||
|
|||
Gets the set of states currently held by an object. |
|||
|
|||
Elements in the array are enumeration values from AtspiStateType in atspi-constants.h: |
|||
|
|||
- 0 - ATSPI_STATE_INVALID: Indicates an invalid state - probably an error |
|||
condition. |
|||
- 1 - ATSPI_STATE_ACTIVE: Indicates a window is currently the active window, or |
|||
an object is the active subelement within a container or table. ATSPI_STATE_ACTIVE |
|||
should not be used for objects which have ATSPI_STATE_FOCUSABLE or |
|||
ATSPI_STATE_SELECTABLE: Those objects should use |
|||
ATSPI_STATE_FOCUSED and ATSPI_STATE_SELECTED respectively. |
|||
ATSPI_STATE_ACTIVE is a means to indicate that an object which is not |
|||
focusable and not selectable is the currently-active item within its |
|||
parent container. |
|||
- 2 - ATSPI_STATE_ARMED: Indicates that the object is armed. |
|||
- 3 - ATSPI_STATE_BUSY: Indicates the current object is busy, i.e. onscreen |
|||
representation is in the process of changing, or the object is |
|||
temporarily unavailable for interaction due to activity already in progress. |
|||
- 4 - ATSPI_STATE_CHECKED: Indicates this object is currently checked. |
|||
- 5 - ATSPI_STATE_COLLAPSED: Indicates this object is collapsed. |
|||
- 6 - ATSPI_STATE_DEFUNCT: Indicates that this object no longer has a valid |
|||
backing widget (for instance, if its peer object has been destroyed). |
|||
- 7 - ATSPI_STATE_EDITABLE: Indicates the user can change the contents of this |
|||
object. |
|||
- 8 - ATSPI_STATE_ENABLED: Indicates that this object is enabled, i.e. that it |
|||
currently reflects some application state. Objects that are "greyed out" |
|||
may lack this state, and may lack the ATSPI_STATE_SENSITIVE if direct |
|||
user interaction cannot cause them to acquire ATSPI_STATE_ENABLED. |
|||
See ATSPI_STATE_SENSITIVE. |
|||
- 9 - ATSPI_STATE_EXPANDABLE: Indicates this object allows progressive |
|||
disclosure of its children. |
|||
- 10 - ATSPI_STATE_EXPANDED: Indicates this object is expanded. |
|||
- 11 - ATSPI_STATE_FOCUSABLE: Indicates this object can accept keyboard focus, |
|||
which means all events resulting from typing on the keyboard will |
|||
normally be passed to it when it has focus. |
|||
- 12 - ATSPI_STATE_FOCUSED: Indicates this object currently has the keyboard |
|||
focus. |
|||
- 13 - ATSPI_STATE_HAS_TOOLTIP: Indicates that the object has an associated |
|||
tooltip. |
|||
- 14 - ATSPI_STATE_HORIZONTAL: Indicates the orientation of this object is |
|||
horizontal. |
|||
- 15 - ATSPI_STATE_ICONIFIED: Indicates this object is minimized and is |
|||
represented only by an icon. |
|||
- 16 - ATSPI_STATE_MODAL: Indicates something must be done with this object |
|||
before the user can interact with an object in a different window. |
|||
- 17 - ATSPI_STATE_MULTI_LINE: Indicates this (text) object can contain multiple |
|||
lines of text. |
|||
- 18 - ATSPI_STATE_MULTISELECTABLE: Indicates this object allows more than one of |
|||
its children to be selected at the same time, or in the case of text |
|||
objects, that the object supports non-contiguous text selections. |
|||
- 19 - ATSPI_STATE_OPAQUE: Indicates this object paints every pixel within its |
|||
rectangular region. It also indicates an alpha value of unity, if it |
|||
supports alpha blending. |
|||
- 20 - ATSPI_STATE_PRESSED: Indicates this object is currently pressed. |
|||
- 21 - ATSPI_STATE_RESIZABLE: Indicates the size of this object's size is not |
|||
fixed. |
|||
- 22 - ATSPI_STATE_SELECTABLE: Indicates this object is the child of an object |
|||
that allows its children to be selected and that this child is one of |
|||
those children that can be selected. |
|||
- 23 - ATSPI_STATE_SELECTED: Indicates this object is the child of an object that |
|||
allows its children to be selected and that this child is one of those |
|||
children that has been selected. |
|||
- 24 - ATSPI_STATE_SENSITIVE: Indicates this object is sensitive, e.g. to user |
|||
interaction. ATSPI_STATE_SENSITIVE usually accompanies. |
|||
ATSPI_STATE_ENABLED for user-actionable controls, but may be found in the |
|||
absence of ATSPI_STATE_ENABLED if the current visible state of the control |
|||
is "disconnected" from the application state. In such cases, direct user |
|||
interaction can often result in the object gaining ATSPI_STATE_SENSITIVE, |
|||
for instance if a user makes an explicit selection using an object whose |
|||
current state is ambiguous or undefined. See ATSPI_STATE_ENABLED, |
|||
ATSPI_STATE_INDETERMINATE. |
|||
- 25 - ATSPI_STATE_SHOWING: Indicates this object, the object's parent, the |
|||
object's parent's parent, and so on, are all 'shown' to the end-user, |
|||
i.e. subject to "exposure" if blocking or obscuring objects do not |
|||
interpose between this object and the top of the window stack. |
|||
- 26 - ATSPI_STATE_SINGLE_LINE: Indicates this (text) object can contain only a |
|||
single line of text. |
|||
- 27 - ATSPI_STATE_STALE: Indicates that the information returned for this object |
|||
may no longer be synchronized with the application state. This can occur |
|||
if the object has ATSPI_STATE_TRANSIENT, and can also occur towards the |
|||
end of the object peer's lifecycle. |
|||
- 28 - ATSPI_STATE_TRANSIENT: Indicates this object is transient. |
|||
- 29 - ATSPI_STATE_VERTICAL: Indicates the orientation of this object is vertical; |
|||
for example this state may appear on such objects as scrollbars, text |
|||
objects (with vertical text flow), separators, etc. |
|||
- 30 - ATSPI_STATE_VISIBLE: Indicates this object is visible, e.g. has been |
|||
explicitly marked for exposure to the user. ATSPI_STATE_VISIBLE is no |
|||
guarantee that the object is actually unobscured on the screen, only that |
|||
it is 'potentially' visible, barring obstruction, being scrolled or clipped |
|||
out of the field of view, or having an ancestor container that has not yet |
|||
made visible. A widget is potentially onscreen if it has both |
|||
ATSPI_STATE_VISIBLE and ATSPI_STATE_SHOWING. The absence of |
|||
ATSPI_STATE_VISIBLE and ATSPI_STATE_SHOWING is |
|||
semantically equivalent to saying that an object is 'hidden'. |
|||
- 31 - ATSPI_STATE_MANAGES_DESCENDANTS: Indicates that "active-descendant-changed" |
|||
event is sent when children become 'active' (i.e. are selected or |
|||
navigated to onscreen). Used to prevent need to enumerate all children |
|||
in very large containers, like tables. The presence of |
|||
ATSPI_STATE_MANAGES_DESCENDANTS is an indication to the client that the |
|||
children should not, and need not, be enumerated by the client. |
|||
Objects implementing this state are expected to provide relevant state |
|||
notifications to listening clients, for instance notifications of |
|||
visibility changes and activation of their contained child objects, without |
|||
the client having previously requested references to those children. |
|||
- 32 - ATSPI_STATE_INDETERMINATE: Indicates that a check box or other boolean |
|||
indicator is in a state other than checked or not checked. This |
|||
usually means that the boolean value reflected or controlled by the |
|||
object does not apply consistently to the entire current context. |
|||
For example, a checkbox for the "Bold" attribute of text may have |
|||
ATSPI_STATE_INDETERMINATE if the currently selected text contains a mixture |
|||
of weight attributes. In many cases interacting with a |
|||
ATSPI_STATE_INDETERMINATE object will cause the context's corresponding |
|||
boolean attribute to be homogenized, whereupon the object will lose |
|||
ATSPI_STATE_INDETERMINATE and a corresponding state-changed event will be |
|||
fired. |
|||
- 33 - ATSPI_STATE_REQUIRED: Indicates that user interaction with this object is |
|||
'required' from the user, for instance before completing the |
|||
processing of a form. |
|||
- 34 - ATSPI_STATE_TRUNCATED: Indicates that an object's onscreen content |
|||
is truncated, e.g. a text value in a spreadsheet cell. |
|||
- 35 - ATSPI_STATE_ANIMATED: Indicates this object's visual representation is |
|||
dynamic, not static. This state may be applied to an object during an |
|||
animated 'effect' and be removed from the object once its visual |
|||
representation becomes static. Some applications, notably content viewers, |
|||
may not be able to detect all kinds of animated content. Therefore the |
|||
absence of this state should not be taken as |
|||
definitive evidence that the object's visual representation is |
|||
static; this state is advisory. |
|||
- 36 - ATSPI_STATE_INVALID_ENTRY: This object has indicated an error condition |
|||
due to failure of input validation. For instance, a form control may |
|||
acquire this state in response to invalid or malformed user input. |
|||
- 37 - ATSPI_STATE_SUPPORTS_AUTOCOMPLETION: This state indicates that the object |
|||
in question implements some form of typeahead or |
|||
pre-selection behavior whereby entering the first character of one or more |
|||
sub-elements causes those elements to scroll into view or become |
|||
selected. Subsequent character input may narrow the selection further as |
|||
long as one or more sub-elements match the string. This state is normally |
|||
only useful and encountered on objects that implement AtspiSelection. |
|||
In some cases the typeahead behavior may result in full or partial |
|||
completion of the data in the input field, in which case |
|||
these input events may trigger text-changed events from the source. |
|||
- 38 - ATSPI_STATE_SELECTABLE_TEXT: This state indicates that the object in |
|||
question supports text selection. It should only be exposed on objects |
|||
which implement the AtspiText interface, in order to distinguish this state |
|||
from ATSPI_STATE_SELECTABLE, which infers that the object in question is a |
|||
selectable child of an object which implements AtspiSelection. While |
|||
similar, text selection and subelement selection are distinct operations. |
|||
- 39 - ATSPI_STATE_IS_DEFAULT: This state indicates that the object in question is |
|||
the 'default' interaction object in a dialog, i.e. the one that gets |
|||
activated if the user presses "Enter" when the dialog is initially |
|||
posted. |
|||
- 40 - ATSPI_STATE_VISITED: This state indicates that the object (typically a |
|||
hyperlink) has already been activated or invoked, with the result that |
|||
some backing data has been downloaded or rendered. |
|||
- 41 - ATSPI_STATE_CHECKABLE: Indicates this object has the potential to |
|||
be checked, such as a checkbox or toggle-able table cell. Since: 2.12 |
|||
- 42 - ATSPI_STATE_HAS_POPUP: Indicates that the object has a popup |
|||
context menu or sub-level menu which may or may not be |
|||
showing. This means that activation renders conditional content. |
|||
Note that ordinary tooltips are not considered popups in this |
|||
context. Since: 2.12 |
|||
- 43 - ATSPI_STATE_READ_ONLY: Indicates that an object which is ENABLED and |
|||
SENSITIVE has a value which can be read, but not modified, by the |
|||
user. Since: 2.16 |
|||
--> |
|||
<method name="GetState"> |
|||
<arg direction="out" type="au"> |
|||
<av:TypeDefinition> |
|||
<av:List/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetAttributes: |
|||
|
|||
Gets a list of name/value pairs of attributes or annotations for this object. For |
|||
typographic, textual, or textually-semantic attributes, see the Text.GetAttributes |
|||
method. |
|||
|
|||
FIXME: is there a list of well-known attributes? |
|||
--> |
|||
<method name="GetAttributes"> |
|||
<arg direction="out" type="a{ss}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiAttributeSet"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetApplication: |
|||
|
|||
Returns a string for the application name, and an object path for the containing |
|||
application object. |
|||
--> |
|||
<method name="GetApplication"> |
|||
<arg direction="out" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetInterfaces: |
|||
|
|||
Returns an array of accessible interface names supported by the current object. |
|||
--> |
|||
<method name="GetInterfaces"> |
|||
<arg direction="out" type="as"/> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,144 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Action: |
|||
@short_description: Allows exploring and invoking the actions of a user-actionable UI component. |
|||
|
|||
For example, a button may expose a "click" action; a popup menu may expose an "open" |
|||
action. Components which are not "passive" providers of UI information should |
|||
implement this interface, unless there is a more specialized interface for |
|||
interaction like org.a11y.atspi.Text or org.a11y.atspi.Value. |
|||
--> |
|||
<interface name="org.a11y.atspi.Action"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
NActions: returns the number of available actions. |
|||
|
|||
By convention, if there is more than one action available, the first one is |
|||
considered the "default" action of the object. |
|||
--> |
|||
<property name="NActions" type="i" access="read"/> |
|||
|
|||
<!-- |
|||
GetDescription: |
|||
@index: 0-based index of the action to query. |
|||
|
|||
Returns: The localized description for the action at the specified @index. For |
|||
example, a screen reader will read out this description when the user asks for |
|||
extra detail on an action. For example, "Clicks the button" for the "click" |
|||
action of a button. |
|||
--> |
|||
<method name="GetDescription"> |
|||
<arg type="i" name="index" direction="in"/> |
|||
<arg type="s" direction="out"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetName: |
|||
@index: 0-based index of the action to query. |
|||
|
|||
Returns: Machine-readable name for the action at the specified @index. |
|||
--> |
|||
<method name="GetName"> |
|||
<arg type="i" name="index" direction="in"/> |
|||
<arg type="s" direction="out"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetLocalizedName: |
|||
@index: 0-based index of the action to query. |
|||
|
|||
Returns: A short, localized name for the action at the specified @index. This is |
|||
what screen readers will read out during normal navigation. For example, "Click" |
|||
for a button. |
|||
--> |
|||
<method name="GetLocalizedName"> |
|||
<arg type="i" name="index" direction="in"/> |
|||
<arg type="s" direction="out"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetKeyBinding: |
|||
@index: 0-based index of the action to query. |
|||
|
|||
Gets the keybinding which can be used to activate this action, if one |
|||
exists. The string returned should contain localized, human-readable, |
|||
key sequences as they would appear when displayed on screen. It must |
|||
be in the format "mnemonic;sequence;shortcut". |
|||
|
|||
- The mnemonic key activates the object if it is presently enabled onscreen. |
|||
This typically corresponds to the underlined letter within the widget. |
|||
Example: "n" in a traditional "New..." menu item or the "a" in "Apply" for |
|||
a button. |
|||
|
|||
- The sequence is the full list of keys which invoke the action even if the |
|||
relevant element is not currently shown on screen. For instance, for a menu |
|||
item the sequence is the keybindings used to open the parent menus before |
|||
invoking. The sequence string is colon-delimited. Example: "Alt+F:N" in a |
|||
traditional "New..." menu item. |
|||
|
|||
- The shortcut, if it exists, will invoke the same action without showing |
|||
the component or its enclosing menus or dialogs. Example: "Ctrl+N" in a |
|||
traditional "New..." menu item. |
|||
|
|||
Example: For a traditional "New..." menu item, the expected return value |
|||
would be: "N;Alt+F:N;Ctrl+N" for the English locale and "N;Alt+D:N;Strg+N" |
|||
for the German locale. If, hypothetically, this menu item lacked a mnemonic, |
|||
it would be represented by ";;Ctrl+N" and ";;Strg+N" respectively. |
|||
|
|||
If there is no key binding for this action, return "". |
|||
--> |
|||
<method name="GetKeyBinding"> |
|||
<arg type="i" name="index" direction="in"/> |
|||
<arg type="s" direction="out"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetActions: |
|||
|
|||
Returns: an array of (localized_name, localized description, keybinding) for the |
|||
actions that an object supports. See the GetKeyBinding method for a description |
|||
of that field's syntax. |
|||
|
|||
This is equivalent to using the methods GetLocalizedName, GetDescription, |
|||
GetKeyBinding for each action, but with a single call and thus less DBus traffic. |
|||
|
|||
By convention, if there is more than one action available, the first one is |
|||
considered the "default" action of the object. |
|||
--> |
|||
<method name="GetActions"> |
|||
<arg direction="out" type="a(sss)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiAction"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
|
|||
|
|||
</method> |
|||
|
|||
<!-- |
|||
DoAction: |
|||
@index: 0-based index of the action to perform. |
|||
|
|||
Performs the specified action on the object. |
|||
|
|||
Returns: true on success, false otherwise. |
|||
--> |
|||
<method name="DoAction"> |
|||
<arg direction="in" name="index" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,86 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Application: |
|||
@short_description: Interface that must be implemented by the root object of an application. |
|||
--> |
|||
<interface name="org.a11y.atspi.Application"> |
|||
|
|||
<!-- |
|||
ToolkitName: name of the toolkit used to implement the application's user interface. |
|||
--> |
|||
<property name="ToolkitName" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
Version: version of the toolkit used to implement the application's user interface. |
|||
|
|||
Deprecated: Use org.a11y.atspi.Application:ToolkitVersion instead. |
|||
--> |
|||
<property name="Version" type="s" access="read"> |
|||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/> |
|||
</property> |
|||
|
|||
<!-- |
|||
Version: version of the toolkit used to implement the application's user interface. |
|||
--> |
|||
<property name="ToolkitVersion" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
AtspiVersion: You should return "2.1" here. |
|||
|
|||
This was intended to be the version of the atspi interfaces |
|||
that the application supports, but atspi will probably move to |
|||
using versioned interface names instead. Just return "2.1" here. |
|||
--> |
|||
<property name="AtspiVersion" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
InterfaceVersion: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="InterfaceVersion" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
Id: set to an arbitrary numerical id when an application registers with the registry. |
|||
|
|||
When a freshly-started application uses the |
|||
org.a11y.atspi.Socket.Embed method to register with the |
|||
accessibility registry, the registry will set a numerical id |
|||
on the application. |
|||
|
|||
Per https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/82 it |
|||
may turn out that this id is not actually used subsequently. |
|||
This is a remnant of the time when registryd actually had to |
|||
make up identifiers for each application. With DBus, however, |
|||
it is the bus that assigns unique names to applications that |
|||
connect to it. |
|||
|
|||
Your application or toolkit can remember the Id passed when |
|||
the accessibility registry sets this property, and return it |
|||
back when the property is read. |
|||
--> |
|||
<property name="Id" type="i" access="readwrite"/> |
|||
|
|||
<!-- This method is not used. |
|||
|
|||
See https://gitlab.gnome.org/GNOME/orca/-/issues/260 |
|||
--> |
|||
<method name="GetLocale"> |
|||
<arg direction="in" name="lctype" type="u"/> |
|||
<arg direction="out" type="s"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
Get a P2P DBus address for further direct communication from the client. |
|||
|
|||
This allows for communication without being proxied by the daemon. |
|||
--> |
|||
<method name="GetApplicationBusAddress"> |
|||
<arg direction="out" type="s"/> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,114 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Cache: |
|||
@short_description: Interface to query accessible objects in bulk. |
|||
|
|||
The application should expose this interface at the /org/a11y/atspi/cache object |
|||
path. |
|||
|
|||
The org.a11y.atspi.Accessible interface has methods like GetChildren and |
|||
GetChildAtIndex, but these only transfer an object's DBus id. The caller has to |
|||
then query the object's properties individually. Transferring objects one by one and |
|||
then their properties produces a lot of traffic in the accessibility bus. |
|||
|
|||
So, this Cache interface can be used to query objects in bulk. Assistive tech |
|||
should try to do a bulk query of all the objects in a new window with the GetItems |
|||
method, and then update them dynamically from the AddAccessible and RemoveAccessible |
|||
signals. |
|||
|
|||
FIXME: Does GetItems only get called if an application implements |
|||
GetApplicationBusAddress? GTK4 doesn't implement that, but it implements GetItems - |
|||
does that ever get called? |
|||
--> |
|||
<interface name="org.a11y.atspi.Cache"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
GetItems: |
|||
|
|||
Bulk query an application's accessible objects. |
|||
|
|||
Returns: an array with one element for each available object. Each element's |
|||
fields are like this: |
|||
|
|||
- (so): accessible object reference - DBus name and object |
|||
path. The rest of the fields refer to this main object. |
|||
- (so): application reference - DBus name and object path. This is the owner of |
|||
the main object; the root path of the application that registered via |
|||
the Embed method of the org.a11y.atspi.Socket interface. |
|||
- (so): parent object reference - DBus name and object path. |
|||
If the main object has no parent: |
|||
- If it is a control, or a window, return the parent application. |
|||
- If the object has the application role, return a null reference. FIXME: |
|||
atk-adaptor/adaptors/cache-adaptor.c:append_cache_item() returns a |
|||
reference to the registry in this case (the one it obtained from the |
|||
initial Socket::Embed call); GTK4 returns a null reference. |
|||
- Otherwise, return a null reference ("" for the application name name and |
|||
"/org/a11y/atspi/null" for the object path). |
|||
- i: index in parent, or -1 for transient widgets/menu items. Equivalent to the |
|||
GetIndexInParent method of the org.a11y.atspi.Accessible interface. |
|||
- i: child count of main object, or -1 for defunct/menus. Equivalent to the |
|||
ChildCount property of the org.a11y.atspi.Accessible interface. |
|||
- as: array of names of the interfaces that the main object supports. Equivalent |
|||
to the GetInterfaces method of the org.a11y.atspi.Accessible interface. |
|||
- s: human-readable, localized, short name for the main object. Equivalent to the |
|||
Name property of the org.a11y.atspi.Accessible interface. |
|||
- u: role. Equivalent to the GetRole method of the org.a11y.atspi.Accessible interface. |
|||
- s: human-readable, localized description of the object in more detail. |
|||
Equivalent to the Description property of the org.a11y.atspi.Accessible interface. |
|||
- au: Set of states currently held by an object. Equivalent to the GetState |
|||
method of the org.a11y.atspi.Accessible interface. |
|||
|
|||
Deprecation note: The signature for the return value of this method changed in |
|||
2015, in commit b2c8c4c7. It used to be "a((so)(so)(so)a(so)assusau)". The |
|||
"a(so)" instead of "ii" is a list of references to child objects. The |
|||
implementation in atspi-misc.c can handle either version, although the intention |
|||
is to deprecate the code that handles the old version. |
|||
--> |
|||
<method name="GetItems"> |
|||
<arg direction="out" name="nodes" type="a((so)(so)(so)iiassusau)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiAccessibleCacheItem"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
AddAccessible: to be emitted when a new object is added. |
|||
|
|||
See the GetItems method for a description of the signature. |
|||
--> |
|||
<signal name="AddAccessible"> |
|||
<arg name="nodeAdded" type="((so)(so)(so)iiassusau)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiAccessibleCacheItem"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<!-- |
|||
RemoveAccessible: to be emitted when an object is no longer available. |
|||
|
|||
@nodeRemoved: (so) string for the application name and object path. |
|||
--> |
|||
<signal name="RemoveAccessible"> |
|||
<arg name="nodeRemoved" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,202 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Collection: |
|||
@short_description: An interface designed to allow accessibles which satisfy a set of |
|||
criteria to be returned. |
|||
--> |
|||
|
|||
<interface name="org.a11y.atspi.Collection"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
GetMatches: |
|||
|
|||
Return descendants which satisfy a set of criteria specified in the |
|||
match rule. |
|||
|
|||
@rule: Criteria that objects to be returned must fulfill. |
|||
This structure is composed as follows: |
|||
- ai: States (see below for more how they are encoded) |
|||
- i: Match type used for matching states |
|||
- a{ss}: Attributes. See also the Accessible.GetAttributes method |
|||
- i: Match type used for matching attributes |
|||
- ai: Roles (see below for how they are encoded) |
|||
- i: Match type used for matching roles |
|||
- as: Interfaces, by name (like "Action" or "Text"). |
|||
- i: Match type used for matching interfaces |
|||
- b: Whether to invert the criteria. If true, the match rule should |
|||
be denied (inverted); if false, it should not. For example, if |
|||
the match rule defines that a match is an object of ROLE_HEADING |
|||
which has STATE_FOCUSABLE and a click action, inverting it would |
|||
match all objects that are not of ROLE_HEADING, focusable and |
|||
clickable at the same time. |
|||
|
|||
For all criteria, the value used for the match type corresponds to the |
|||
AtspiCollectionMatchType enum values. |
|||
|
|||
The set of states and roles are bitsets encoded as an array of |
|||
multiple 32-bit integers. |
|||
A state/role with enum value N is set by setting the Nth bit in the bit |
|||
set. |
|||
The first integer in the array contains the lowest 0-31 bits, the next |
|||
integer bits 32-63, etc. |
|||
|
|||
For example, enum value AT_SPI_ROLE_COMBOBOX has a value of 83. In order to |
|||
pass a set of roles in which only this role is set, an array of integers |
|||
representing (1 << 83) = 0x800000000000000000000 would be |
|||
passed: { 0, 0, 0x80000, 0 } |
|||
|
|||
@sortby: The way objects should be sorted. |
|||
The values correspond to those defined in the AtspiCollectionSortOrder enum. |
|||
|
|||
@count: Maximum number of objects to return, or 0 for no limit. |
|||
|
|||
@traverse: Whether to return descendants from the accessible subtree |
|||
(if #TRUE) or only direct children (if #FALSE). |
|||
--> |
|||
<method name="GetMatches"> |
|||
<arg direction="in" name="rule" type="(aiia{ss}iaiiasib)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiMatchRule"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="sortby" type="u"/> |
|||
<arg direction="in" name="count" type="i"/> |
|||
<arg direction="in" name="traverse" type="b"/> |
|||
<arg direction="out" type="a(so)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetMatchesTo: |
|||
|
|||
@currentObject: The object at which to start searching. |
|||
|
|||
@rule: Criteria that objects to be returned must fulfill. |
|||
|
|||
@sortby: The way objects should be sorted. |
|||
|
|||
@tree: Specifies restrictions on the objects to be traversed. Values |
|||
correspond to the AtspiCollectionTreeTraversalType enum: |
|||
- ATSPI_Collection_TREE_RESTRICT_CHILDREN (0): Restrict children tree traversal |
|||
- 1 (ATSPI_Collection_TREE_RESTRICT_SIBLING): Restrict sibling tree traversal |
|||
- 2 (ATSPI_Collection_TREE_INORDER): In-order tree traversal. |
|||
|
|||
@limit_scope: If true, only descendants of @currentObject's parent |
|||
will be returned. Otherwise (if false), any accessible may be |
|||
returned if it would preceed @currentObject in a flattened |
|||
hierarchy. |
|||
|
|||
@count: The maximum number of results to return, or 0 for no limit. |
|||
|
|||
@traverse: Whether to traverse the accessible subtree (in case |
|||
of true) or only the direct children (on case of false). |
|||
|
|||
Gets all Accessible objects from the collection, after |
|||
@currentObject, matching a given @rule. |
|||
|
|||
Returns: All accessible objects matching the given match rule after |
|||
@currentObject. |
|||
--> |
|||
<method name="GetMatchesTo"> |
|||
<arg direction="in" name="currentObject" type="o"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="rule" type="(aiia{ss}iaiiasib)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiMatchRule"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="sortby" type="u"/> |
|||
<arg direction="in" name="tree" type="u"/> |
|||
<arg direction="in" name="limit_scope" type="b"/> |
|||
<arg direction="in" name="count" type="i"/> |
|||
<arg direction="in" name="traverse" type="b"/> |
|||
<arg direction="out" type="a(so)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetMatchesFrom: |
|||
|
|||
@currentObject: Upon reaching this object, searching should stop. |
|||
|
|||
@rule: Criteria that objects to be returned must fulfill. |
|||
|
|||
@sortby: The way objects should be sorted. |
|||
|
|||
@tree: Specifies restrictions on the objects to be traversed. Values |
|||
correspond to the AtspiCollectionTreeTraversalType enum. |
|||
|
|||
@count: The maximum number of results to return, or 0 for no limit. |
|||
|
|||
@traverse: Whether to traverse the accessible subtree (if true) |
|||
or only the direct children (if false). |
|||
|
|||
Gets all Accessible objects from the collection, before @currentObject, |
|||
matching a given @rule. |
|||
|
|||
Returns: All accessible objects matching the given match rule after |
|||
@currentObject. |
|||
--> |
|||
<method name="GetMatchesFrom"> |
|||
<arg direction="in" name="currentObject" type="o"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="rule" type="(aiia{ss}iaiiasib)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiMatchRule"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="sortby" type="u"/> |
|||
<arg direction="in" name="tree" type="u"/> |
|||
<arg direction="in" name="count" type="i"/> |
|||
<arg direction="in" name="traverse" type="b"/> |
|||
<arg direction="out" type="a(so)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetActiveDescendant: |
|||
|
|||
Returns the active descendant of the given object. Not currently |
|||
implemented in libatspi. |
|||
--> |
|||
<method name="GetActiveDescendant"> |
|||
<arg direction="out" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,344 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Component: |
|||
@short_description: Interface for GUI components like widgets or other visible elements. |
|||
--> |
|||
<interface name="org.a11y.atspi.Component"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
Contains: |
|||
|
|||
@x: X coordinate of point. |
|||
|
|||
@y: Y coordinate of point. |
|||
|
|||
@coord_type: Whether the coordinates are relative to the screen or to the |
|||
component's top level window; see the description. |
|||
|
|||
Queries whether a point (x, y) is inside the component. |
|||
|
|||
The @coord_type values are as follows, and correspond to AtkCoordType: |
|||
|
|||
- 0 - Coordinates are relative to the screen. |
|||
- 1 - Coordinates are relative to the component's toplevel window. |
|||
- 2 - Coordinates are relative to the component's immediate parent. |
|||
--> |
|||
<method name="Contains"> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="in" name="coord_type" type="u"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetAccessibleAtPoint: |
|||
|
|||
@x: X coordinate of point. |
|||
|
|||
@y: Y coordinate of point. |
|||
|
|||
@coord_type: Whether the coordinates are relative to the screen or to the |
|||
component's top level window; see the description. |
|||
|
|||
Gets a reference to the accessible object that contains an (x, y) pair of |
|||
coordinates. |
|||
|
|||
The @coord_type values are as follows, and correspond to AtkCoordType: |
|||
|
|||
- 0 - Coordinates are relative to the screen. |
|||
- 1 - Coordinates are relative to the component's toplevel window. |
|||
- 2 - Coordinates are relative to the component's immediate parent. |
|||
|
|||
Returns: A DBus name and object reference (so) for the sought object, or a null |
|||
object reference "/org/a11y/atspi/null" if there is no object at the specified |
|||
coordinates. |
|||
--> |
|||
<method name="GetAccessibleAtPoint"> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="in" name="coord_type" type="u"/> |
|||
<arg direction="out" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetExtents: |
|||
|
|||
@coord_type: Whether the coordinates are relative to the screen or to the |
|||
component's top level window; see the description. |
|||
|
|||
Queries the pixel extents of a component. |
|||
|
|||
The @coord_type values are as follows, and correspond to AtkCoordType: |
|||
|
|||
- 0 - Coordinates are relative to the screen. |
|||
- 1 - Coordinates are relative to the component's toplevel window. |
|||
- 2 - Coordinates are relative to the component's immediate parent. |
|||
|
|||
Returns: a tuple (x, y, width, height) corresponding to the rectangle for the |
|||
component's extents. |
|||
--> |
|||
<method name="GetExtents"> |
|||
<arg direction="in" name="coord_type" type="u"/> |
|||
<arg direction="out" type="(iiii)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiRect"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetPosition: |
|||
|
|||
@coord_type: Whether the coordinates are relative to the screen or to the |
|||
component's top level window; see the description. |
|||
|
|||
Queries the upper-left position of a component. |
|||
|
|||
The @coord_type values are as follows, and correspond to AtkCoordType: |
|||
|
|||
- 0 - Coordinates are relative to the screen. |
|||
- 1 - Coordinates are relative to the component's toplevel window. |
|||
- 2 - Coordinates are relative to the component's immediate parent. |
|||
|
|||
Returns: (x, y) coordinates of the component's upper-left corner. |
|||
--> |
|||
<method name="GetPosition"> |
|||
<arg direction="in" name="coord_type" type="u"/> |
|||
<arg direction="out" name="x" type="i"/> |
|||
<arg direction="out" name="y" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetSize: |
|||
Queries the pixel size of a component. |
|||
|
|||
Returns: (width, height) of the component's rectangular area. |
|||
--> |
|||
<method name="GetSize"> |
|||
<arg direction="out" name="width" type="i"/> |
|||
<arg direction="out" name="height" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetLayer: |
|||
|
|||
Queries the UI layer at which a component is rendered, which can help in |
|||
determining when components occlude one another. |
|||
|
|||
The layer of a component indicates its relative stacking order with respect to the |
|||
onscreen visual representation of the UI. The layer index, in combination |
|||
with the component's extents, can be used to compute the visibility of |
|||
all or part of a component. This is important in programmatic determination of |
|||
region-of-interest for magnification, and in flat screen review models of the |
|||
screen, as well as for other uses. Objects residing in two of the |
|||
Layer categories support further z-ordering information, with |
|||
respect to their peers in the same layer: namely, WINDOW and |
|||
MDI. Relative stacking order for other objects within the same layer |
|||
is not available; the recommended heuristic is first child paints first. In other |
|||
words, assume that the first siblings in the child list are subject to being |
|||
overpainted by later siblings if their bounds intersect. The order of layers, from |
|||
bottom to top, is as follows: |
|||
|
|||
- 0 - INVALID: Error condition. |
|||
- 1 - BACKGROUND: Reserved for the desktop background; this is the bottom-most |
|||
layer, over which everything else is painted. |
|||
- 2 - CANVAS: The 'background' layer for most content renderers and UI component containers. |
|||
- 3 - WIDGET: The layer in which the majority of ordinary 'foreground' widgets reside. |
|||
- 4 - MDI: A special layer between CANVAS and WIDGET, in which the 'pseudo-windows' |
|||
(e.g. the Multiple-Document Interface frames) reside. See the GetMDIZOrder |
|||
method. |
|||
- 5 - POPUP: Layer for popup window content, above WIDGET. |
|||
- 6 - OVERLAY: The topmost layer. |
|||
- 7 - WINDOW: The layer in which a toplevel window background usually resides. |
|||
--> |
|||
<method name="GetLayer"> |
|||
<arg direction="out" type="u"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetMDIZOrder: |
|||
|
|||
Queries the Z stacking order of a component which is in the MDI or WINDOW layer, |
|||
per the GetLayer method. Bigger z-order numbers are nearer the top. |
|||
|
|||
Returns: The z order of the component, or -1 if it is not in the MDI layer. |
|||
--> |
|||
<method name="GetMDIZOrder"> |
|||
<arg direction="out" type="n"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GrabFocus: |
|||
|
|||
Attempts to set the keyboard input focus to the component. |
|||
|
|||
Returns: true if successful, or false otherwise. |
|||
--> |
|||
<method name="GrabFocus"> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetAlpha: |
|||
|
|||
Gets the opacity/alpha value of a component, if alpha blending is in use. |
|||
|
|||
Returns: opacity value in the [0.0, 1.0] range. 0 is fully transparent and 1 is fully opaque. |
|||
--> |
|||
<method name="GetAlpha"> |
|||
<arg direction="out" type="d"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
SetExtents: |
|||
|
|||
@x: the new horizontal position to which the component should be moved. |
|||
|
|||
@y: the new vertical position to which the component should be moved. |
|||
|
|||
@width: the width to which the component should be resized. |
|||
|
|||
@height: the height to which the component should be resized. |
|||
|
|||
@coord_type: Whether the coordinates are relative to the screen or to the |
|||
component's top level window; see the description. |
|||
|
|||
Moves and resizes the component. |
|||
|
|||
The @coord_type values are as follows, and correspond to AtspiCoordType: |
|||
|
|||
- 0 - Coordinates are relative to the screen. |
|||
- 1 - Coordinates are relative to the component's toplevel window. |
|||
- 2 - Coordinates are relative to the component's immediate parent. |
|||
|
|||
Returns: true if successful, or false otherwise. |
|||
--> |
|||
<method name="SetExtents"> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="in" name="width" type="i"/> |
|||
<arg direction="in" name="height" type="i"/> |
|||
<arg direction="in" name="coord_type" type="u"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
SetPosition: |
|||
|
|||
@x: the new horizontal position to which the component should be moved. |
|||
|
|||
@y: the new vertical position to which the component should be moved. |
|||
|
|||
@coord_type: Whether the coordinates are relative to the screen or to the |
|||
component's top level window; see the description. |
|||
|
|||
Moves the component to the specified position. |
|||
|
|||
The @coord_type values are as follows, and correspond to AtkCoordType: |
|||
|
|||
0 - Coordinates are relative to the screen. |
|||
1 - Coordinates are relative to the component's toplevel window. |
|||
2 - Coordinates are relative to the component's immediate parent. |
|||
|
|||
Returns: true if successful, or false otherwise. |
|||
--> |
|||
<method name="SetPosition"> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="in" name="coord_type" type="u"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
SetSize: |
|||
|
|||
@width: the width to which the component should be resized. |
|||
|
|||
@height: the height to which the component should be resized. |
|||
|
|||
Resizes the component to the given pixel dimensions. |
|||
|
|||
Returns: true if successful, or false otherwise. |
|||
--> |
|||
<method name="SetSize"> |
|||
<arg direction="in" name="width" type="i"/> |
|||
<arg direction="in" name="height" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
ScrollTo: |
|||
|
|||
@type: How to position the component within its parent; see the description. |
|||
|
|||
Makes the component visible on the screen by scrolling all necessary parents. |
|||
|
|||
The @type corresponds to AtkScrollType: |
|||
|
|||
- 0 - TOP_LEFT: Scroll the object vertically and horizontally to bring |
|||
its top left corner to the top left corner of the window. |
|||
- 1 - BOTTOM_RIGHT: Scroll the object vertically and horizontally to |
|||
bring its bottom right corner to the bottom right corner of the window. |
|||
- 2 - TOP_EDGE: Scroll the object vertically to bring its top edge to |
|||
the top edge of the window. |
|||
- 3 - BOTTOM_EDGE: Scroll the object vertically to bring its bottom |
|||
edge to the bottom edge of the window. |
|||
- 4 - LEFT_EDGE: Scroll the object vertically and horizontally to bring |
|||
its left edge to the left edge of the window. |
|||
- 5 - RIGHT_EDGE: Scroll the object vertically and horizontally to |
|||
bring its right edge to the right edge of the window. |
|||
- 6 - ANYWHERE: Scroll the object vertically and horizontally so that |
|||
as much as possible of the object becomes visible. The exact placement is |
|||
determined by the application. |
|||
|
|||
Returns: true if successful, or false otherwise. |
|||
--> |
|||
<method name="ScrollTo"> |
|||
<arg direction="in" name="type" type="u"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
ScrollToPoint: |
|||
|
|||
@coord_type: Whether the coordinates are relative to the screen or to the |
|||
component's top level window; see the description. |
|||
|
|||
@x: X coordinate within the component to make visible. |
|||
|
|||
@y: Y coordinate within the component to make visible. |
|||
|
|||
Similar to the ScrollTo method, but makes a specific point from the component |
|||
visible in its parent. |
|||
|
|||
The @coord_type values are as follows, and correspond to AtkCoordType: |
|||
|
|||
- 0 - Coordinates are relative to the screen. |
|||
- 1 - Coordinates are relative to the component's toplevel window. |
|||
- 2 - Coordinates are relative to the component's immediate parent. |
|||
|
|||
Returns: true if successful, or false otherwise. |
|||
--> |
|||
<method name="ScrollToPoint"> |
|||
<arg direction="in" name="coord_type" type="u"/> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,142 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.DeviceEventController: |
|||
@short_description: Legacy interface for keystroke listeners and generation of keyboard/mouse events |
|||
|
|||
This interface is being replaced by the functions in atspi-device-listener.h. |
|||
--> |
|||
<interface name="org.a11y.atspi.DeviceEventController"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
RegisterKeystrokeListener: |
|||
@listener: path of object to be notified when the following keys are pressed |
|||
|
|||
@keys: array of (key_code, key_sym, key_string, unused) |
|||
|
|||
@mask: modifier mask in X11 style (see Xlib.h) |
|||
|
|||
@types: mask of press/release; see the description below. |
|||
|
|||
@mode: struct of flags (synchronous, preemptive, global), see the description below. |
|||
|
|||
The @types can be a mask of the following: |
|||
- KEY_PRESS = 1 << 0 |
|||
- KEY_RELEASE = 1 << 1 |
|||
|
|||
Note that Orca always passes (KEY_PRESS | KEY_RELEASE). |
|||
|
|||
The @mode is composed of three flags (see AtspiKeyListenerSyncType): |
|||
- synchronous: Events are delivered synchronously, before |
|||
the currently focused application sees them. If false, |
|||
events may be delivered asynchronously, which means in some |
|||
cases they may already have been delivered to the |
|||
application before the AT client receives the notification. |
|||
- preemptive: (called CANCONSUME in AtspiKeyListenerSyncType) |
|||
Events may be consumed by the AT client. Requires the synchronous flag to be set. |
|||
- global: (called ALL_WINDOWS in AtspiKeyListenerSyncType) |
|||
Events are received not from the application toolkit layer, |
|||
but from the device driver or windowing system subsystem. |
|||
|
|||
Returns: boolean indicating whether the operation was successful. This is always |
|||
TRUE for non-global listeners (c.f. @mode), and may be FALSE for global listeners |
|||
if the underlying XGrabKey() failed (see spi_dec_x11_grab_key). |
|||
--> |
|||
<method name="RegisterKeystrokeListener"> |
|||
<arg direction="in" name="listener" type="o"/> |
|||
<arg direction="in" name="keys" type="a(iisi)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiKeyDefinition"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="mask" type="u"> |
|||
<av:TypeDefinition> |
|||
<av:BitFlags Type="AtSpiModifierMask"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="types" type="u"> |
|||
<av:TypeDefinition> |
|||
<av:BitFlags Type="AtSpiEventTypeMask"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="mode" type="(bbb)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiEventMode"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<method name="DeregisterKeystrokeListener"> |
|||
<arg direction="in" name="listener" type="o"/> |
|||
<arg direction="in" name="keys" type="a(iisi)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiKeyDefinition"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="mask" type="u"> |
|||
<av:TypeDefinition> |
|||
<av:BitFlags Type="AtSpiModifierMask"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="in" name="type" type="u"> |
|||
<av:TypeDefinition> |
|||
<av:BitFlags Type="AtSpiEventTypeMask"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<method name="GetKeystrokeListeners"> |
|||
<arg direction="out" type="a(souua(iisi)u(bbb))"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiKeystrokeListener"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<method name="GenerateKeyboardEvent"> |
|||
<arg direction="in" name="keycode" type="i"/> |
|||
<arg direction="in" name="keystring" type="s"/> |
|||
<arg direction="in" name="type" type="u"/> |
|||
</method> |
|||
|
|||
<method name="GenerateMouseEvent"> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="in" name="eventName" type="s"/> |
|||
</method> |
|||
|
|||
<method name="NotifyListenersSync"> |
|||
<arg direction="in" name="event" type="(uiuuisb)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiDeviceEvent"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<method name="NotifyListenersAsync"> |
|||
<arg direction="in" name="event" type="(uiuuisb)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiDeviceEvent"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,134 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.EditableText: |
|||
@short_description: An interface that provides methods for modifying |
|||
textual content of components which support editing. |
|||
--> |
|||
<interface name="org.a11y.atspi.EditableText"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
SetTextContents: |
|||
|
|||
@newContents: a character string, encoded in UTF-8, which is to |
|||
become the new text contents of the object. |
|||
|
|||
Replace the entire text contents of the accessible with the given text. |
|||
|
|||
Returns: True if successful, false otherwise. |
|||
--> |
|||
<method name="SetTextContents"> |
|||
<arg direction="in" name="newContents" type="s"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
InsertText: |
|||
|
|||
@position: an integer indicating the character offset at which to |
|||
insert the new text. |
|||
|
|||
@text: a string representing the text to insert, in UTF-8 encoding. |
|||
@length: the number of characters of text to insert, in bytes. If the |
|||
byte count of text is less than or equal to length, the entire contents |
|||
of text will be inserted. |
|||
|
|||
Inserts text into an #AtspiEditableText object. |
|||
As with all character offsets, the specified @position may not be the |
|||
same as the resulting byte offset, since the text is in a |
|||
variable-width encoding. |
|||
|
|||
Returns: True if successful, false otherwise. |
|||
--> |
|||
<method name="InsertText"> |
|||
<arg direction="in" name="position" type="i"/> |
|||
<arg direction="in" name="text" type="s"/> |
|||
<arg direction="in" name="length" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
CopyText: |
|||
|
|||
@startPos: an integer indicating the starting character offset |
|||
of the text to copy. |
|||
|
|||
@endPos: an integer indicating the offset of the first character |
|||
past the end of the text section to be copied. |
|||
|
|||
Copies text from an accessible object into the system clipboard. |
|||
--> |
|||
<method name="CopyText"> |
|||
<arg direction="in" name="startPos" type="i"/> |
|||
<arg direction="in" name="endPos" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
CutText: |
|||
|
|||
@startPos: an integer indicating the starting character offset |
|||
of the text to cut. |
|||
|
|||
@endPos: an integer indicating the offset of the first character |
|||
past the end of the text section to be cut. |
|||
|
|||
Deletes text from an accessible object, copying the excised portion |
|||
into the system clipboard. |
|||
|
|||
Returns: True if successful, false otherwise. |
|||
--> |
|||
<method name="CutText"> |
|||
<arg direction="in" name="startPos" type="i"/> |
|||
<arg direction="in" name="endPos" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
DeleteText: |
|||
|
|||
@startPos: an integer indicating the starting character offset |
|||
of the text to delete. |
|||
|
|||
@endPos: an integer indicating the offset of the first character |
|||
past the end of the text section to be deleted. |
|||
|
|||
Deletes text from an accessible object, without copying the |
|||
excised portion into the system clipboard. |
|||
|
|||
Returns: True if successful, false otherwise. |
|||
--> |
|||
<method name="DeleteText"> |
|||
<arg direction="in" name="startPos" type="i"/> |
|||
<arg direction="in" name="endPos" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
PasteText: |
|||
|
|||
@position: an integer indicating the character offset at which to |
|||
insert the new text. |
|||
|
|||
Inserts text from the system clipboard into an accessible object. |
|||
As with all character offsets, the specified @position may not be the |
|||
same as the resulting byte offset, since the text is in a |
|||
variable-width encoding. |
|||
|
|||
Returns: True if successful, false otherwise. |
|||
--> |
|||
<method name="PasteText"> |
|||
<arg direction="in" name="position" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,742 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
|
|||
<!-- |
|||
These interfaces allow applications to send events to notify ATs of |
|||
changes that they should be aware of. For historical reasons, all |
|||
events include a string, two integers, and a variant. The meaning |
|||
of these values will depend on the specific event, and they may be |
|||
unused, in which case an empty string or a value of 0 should be |
|||
sent. In addition, a dictionary of strings/variants should be |
|||
sent. This is intended to allow applications to proactively send |
|||
additional properties that an AT might be interested in. This is |
|||
reserved for future use. Currently, this dictionary should be empty. |
|||
|
|||
TODO: Need documentation for the signals themselves. |
|||
--> |
|||
|
|||
<interface name="org.a11y.atspi.Event.Object"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<signal name="PropertyChange"> |
|||
<arg name="property" type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg name="value" type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="BoundsChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v" name="bounds"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="LinkSelected"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="StateChanged"> |
|||
<arg name="state" type="s"/> |
|||
<arg name="enabled" type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ChildrenChanged"> |
|||
<arg name="operation" type="s"/> |
|||
<arg name="index_in_parent" type="i"/> |
|||
<arg type="i"/> |
|||
<arg name="child" type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="VisibleDataChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="SelectionChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ModelChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ActiveDescendantChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg name="child" type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Announcement"> |
|||
<arg type="s"/> |
|||
<!-- politeness should be 1 (ATSPI_LIVE_POLITE) or 2 |
|||
(ATSPI_LIVE_ASSERTIVE) --> |
|||
<arg type="i" name="politeness"/> |
|||
<arg type="i"/> |
|||
<!-- The variant should enclose a string with the text to be announced --> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="AttributesChanged"> |
|||
<!-- name specifies the name of the attribute that has changed, when available --> |
|||
<arg name="name" type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<!-- The variant should include a string with the attribute's new value, when available --> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<!-- Table events. TODO: move to Table interface? --> |
|||
|
|||
<signal name="RowInserted"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="RowReordered"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="RowDeleted"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ColumnInserted"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ColumnReordered"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ColumnDeleted"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<!-- Text events. TODO: move to Text interface? --> |
|||
|
|||
<signal name="TextBoundsChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="TextSelectionChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="TextChanged"> |
|||
<arg name="detail" type="s"/> |
|||
<arg name="start_pos" type="i"/> |
|||
<arg name="length" type="i"/> |
|||
<arg name="text" type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="TextAttributesChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="TextCaretMoved"> |
|||
<arg type="s"/> |
|||
<arg name="position" type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
</interface> |
|||
|
|||
<interface name="org.a11y.atspi.Event.Window"> |
|||
<signal name="PropertyChange"> |
|||
<arg name="property" type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Minimize"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Maximize"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Restore"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Close"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Create"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Reparent"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="DesktopCreate"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="DesktopDestroy"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Destroy"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Activate"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Deactivate"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Raise"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Lower"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Move"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Resize"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Shade"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="uUshade"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Restyle"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
</interface> |
|||
|
|||
<interface name="org.a11y.atspi.Event.Mouse"> |
|||
|
|||
<signal name="Abs"> |
|||
<arg type="s"/> |
|||
<arg name="x" type="i"/> |
|||
<arg name="y" type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Rel"> |
|||
<arg type="s"/> |
|||
<arg name="x" type="i"/> |
|||
<arg name="y" type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Button"> |
|||
<arg name="detail" type="s"/> |
|||
<arg name="mouse_x" type="i"/> |
|||
<arg name="mouse_y" type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
</interface> |
|||
|
|||
<interface name="org.a11y.atspi.Event.Keyboard"> |
|||
|
|||
<signal name="Modifiers"> |
|||
<arg type="s"/> |
|||
<arg name="previous_modifiers" type="i"/> |
|||
<arg name="current_modifiers" type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
</interface> |
|||
|
|||
<interface name="org.a11y.atspi.Event.Terminal"> |
|||
|
|||
<signal name="LineChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ColumncountChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="LinecountChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ApplicationChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="CharwidthChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
</interface> |
|||
|
|||
<interface name="org.a11y.atspi.Event.Document"> |
|||
|
|||
<signal name="LoadComplete"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="Reload"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="LoadStopped"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="ContentChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="AttributesChanged"> |
|||
<!-- name specifies the name of the attribute that has changed, when available --> |
|||
<arg name="name" type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<!-- The variant should include a string with the attribute's new value, when available --> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
<signal name="PageChanged"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
</interface> |
|||
|
|||
<interface name="org.a11y.atspi.Event.Focus"> |
|||
<!-- Focus is deprecated in favor of StateChanged with focus passed as its first argument --> |
|||
<signal name="Focus"> |
|||
<arg type="s"/> |
|||
<arg type="i"/> |
|||
<arg type="i"/> |
|||
<arg type="v"/> |
|||
<arg name="properties" type="a{sv}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiEventProperties"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
</interface> |
|||
|
|||
</node> |
|||
@ -0,0 +1,86 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Image: |
|||
@short_description: Interface for accessible objects that represent |
|||
graphical images. |
|||
--> |
|||
<interface name="org.a11y.atspi.Image"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
ImageDescription: the description of the image. |
|||
--> |
|||
<property name="ImageDescription" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
ImageLocale: a POSIX LC_MESSAGES-style locale value for the image |
|||
description and text. |
|||
--> |
|||
<property name="ImageLocale" type="s" access="read"/> |
|||
|
|||
<!-- |
|||
GetImageExtents: |
|||
|
|||
@coordType: Specifies the reference point for the coordinates to |
|||
be returned. 0 = relative to the screen, 1 = relative to the |
|||
application's top-level window, 2 = relative to the accessible's |
|||
parent. Requesting coordinates relative to the screen is deprecated |
|||
and may not be supported by the application. |
|||
|
|||
Gets the bounding box of the image displayed in a |
|||
specified Image implementor. |
|||
The returned values are meaningful only if the Image has both |
|||
STATE_VISIBLE and STATE_SHOWING. |
|||
--> |
|||
<method name="GetImageExtents"> |
|||
<arg direction="in" name="coordType" type="u"/> |
|||
<arg direction="out" type="(iiii)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiRect"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetImagePosition: |
|||
|
|||
@coordType: Specifies the reference point for the coordinates to |
|||
be returned. 0 = relative to the screen, 1 = relative to the |
|||
application's top-level window, 2 = relative to the accessible's |
|||
parent. Requesting coordinates relative to the screen is deprecated |
|||
and may not be supported by the application. |
|||
|
|||
Gets the minimum x and y coordinates of the image displayed in a |
|||
specified Image implementor. |
|||
The returned values are meaningful only if the Image has both |
|||
STATE_VISIBLE and STATE_SHOWING. |
|||
--> |
|||
<method name="GetImagePosition"> |
|||
<arg direction="in" name="coordType" type="u"/> |
|||
<arg direction="out" name="x" type="i"/> |
|||
<arg direction="out" name="y" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetImageSize: |
|||
|
|||
Gets the size of the image displayed in a specified object. |
|||
The returned values are meaningful only if the Image has both |
|||
STATE_VISIBLE and STATE_SHOWING. |
|||
--> |
|||
<method name="GetImageSize"> |
|||
<arg direction="out" name="width" type="i"/> |
|||
<arg direction="out" name="height" type="i"/> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,130 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Registry: |
|||
@short_description: Interface implemented by the AT-SPI registry daemon. |
|||
|
|||
The registry daemon listens on org.a11y.atspi.Registry and exposes an |
|||
object at /org/a11y/atspi/registry that implements this interface. |
|||
--> |
|||
<interface name="org.a11y.atspi.Registry"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
RegisterEvent: |
|||
|
|||
@event_type: a character string indicating the type of events for which |
|||
notification is requested. Format is |
|||
EventClass:major_type:minor_type:detail |
|||
where all subfields other than EventClass are optional. |
|||
EventClasses include "object", "window", "mouse", |
|||
and toolkit events (e.g. "Gtk", "AWT"). |
|||
Examples: "focus:", "Gtk:GtkWidget:button_press_event". |
|||
|
|||
@properties: A list of properties that applications should pass |
|||
when sending the event specified in @event_type. This is intended |
|||
to allow an AT to proactively request the information that it is |
|||
interested in and minimize the need to make additional queries in |
|||
response to the event. This is currently unimplemented; the |
|||
specification would need to be fleshed out here. |
|||
|
|||
@app_bus_name: The application that this request pertains to. |
|||
This allows an app to indicate that it is interested in a |
|||
particular event only for a certain application. If this string is |
|||
empty, then this registration applies to all applications. |
|||
|
|||
This method is to be called by an AT to indicate that it is |
|||
interested in receiving a particular event from applications. |
|||
Applications can optionally call GetRegisteredEvents and listen for |
|||
EventListenerRewgistered and EventListenerDeregistered signals and, |
|||
in this way, only send events that an AT is interested in receiving. |
|||
--> |
|||
<method name="RegisterEvent"> |
|||
<arg direction="in" name="event" type="s"/> |
|||
<arg direction="in" name="properties" type="as"/> |
|||
<arg direction="in" name="app_bus_name" type="s"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
DeregisterEvent: |
|||
|
|||
@event_type: A character string indicating the event to be |
|||
deregistered. See RegisterEvent for a description of the format of |
|||
this string. |
|||
|
|||
Indicates that an AT is no longer interested in listening to a |
|||
particular event. |
|||
TODO: Add app_bus_name here. |
|||
--> |
|||
<method name="DeregisterEvent"> |
|||
<arg direction="in" name="event" type="s"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetRegisteredEvents: |
|||
|
|||
@events: A dictionary listing the registered events. For each entry, |
|||
the first string gives the bus name of the AT holding the |
|||
registration. The second string gives the event. See RegisterEvent |
|||
for a description of the format of this string. |
|||
--> |
|||
<method name="GetRegisteredEvents"> |
|||
<arg direction="out" name="events" type="a(ss)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiEventListener"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
EventListenerRegistered: |
|||
|
|||
@bus: The bus name of the AT registering the event. |
|||
|
|||
@event: The event being registered. See RegisterEvent for a |
|||
description of the format of this string. |
|||
|
|||
@properties: An array of properties that the AT is interested in |
|||
receiving along with the event. Currently unimplemented; the |
|||
specification for this would need to be fleshed out. |
|||
|
|||
Indicates that an AT has requested to listen for an event. |
|||
Applications can optionally listen to this signal to maintain a |
|||
list of events that ATs are interested in receiving and only send |
|||
events when needed. |
|||
--> |
|||
<signal name="EventListenerRegistered"> |
|||
<arg name="bus" type="s"/> |
|||
<arg name="event" type="s"/> |
|||
<arg name="properties" type="as"/> |
|||
</signal> |
|||
|
|||
<!-- |
|||
EventListenerDeregistered: |
|||
|
|||
@bus: The bus name of the AT registering the event. |
|||
|
|||
@event: The event being deregistered. See RegisterEvent for a |
|||
description of the format of this string. |
|||
|
|||
Indicates that an AT is no longer interested in listening for an |
|||
event. Applications can optionally listen to this signal to maintain |
|||
a list of events that ATs are interested in receiving and only send |
|||
events when needed. |
|||
--> |
|||
<signal name="EventListenerDeregistered"> |
|||
<arg name="bus" type="s"/> |
|||
<arg name="event" type="s"/> |
|||
</signal> |
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,148 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Selection: |
|||
@short_description: An interface which indicates that an object exposes |
|||
a 'selection' model, allowing the selection of one or more of its |
|||
children. |
|||
--> |
|||
<interface name="org.a11y.atspi.Selection"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
NSelectedChildren: |
|||
|
|||
The number of children which are currently selected. |
|||
--> |
|||
<property name="NSelectedChildren" type="i" access="read"/> |
|||
|
|||
<!-- |
|||
GetSelectedChild: |
|||
|
|||
@selectedChildIndex: an integer indicating which of the selected |
|||
children is specified. |
|||
|
|||
Gets the i-th selected child of the accessible. |
|||
Note that @selectedChildIndex refers to the index in the list of |
|||
selected children and generally differs from that used in |
|||
GetChildAtIndex or returned by GetIndexInParent. |
|||
@selectedChildIndex must lie between 0 and the value returned by |
|||
GetNSelectedChildren - 1, inclusive. |
|||
|
|||
Returns: a reference (bus name + object path) of the selected child |
|||
corresponding to the given index. If there is no selected child with |
|||
the given index, then the special object path "/org/a11y/atspi/null" |
|||
will be returned. |
|||
--> |
|||
<method name="GetSelectedChild"> |
|||
<arg direction="in" name="selectedChildIndex" type="i"/> |
|||
<arg direction="out" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
SelectChild: |
|||
|
|||
@childIndex: an integer indicating which child of the accessible |
|||
is to be selected. |
|||
|
|||
Adds a child to to the accessible's list of selected children. |
|||
For implementors that only allow single selections, this |
|||
may replace the (single) current selection. |
|||
|
|||
Returns: true if the child was successfully selected, false otherwise. |
|||
--> |
|||
<method name="SelectChild"> |
|||
<arg direction="in" name="childIndex" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
DeselectSelectedChild: |
|||
|
|||
@selectedChildIndex: a integer indicating which of the selected |
|||
children of the accessible is to be deselected. |
|||
|
|||
Removes a child from an accessible's list of selected children. |
|||
Note that @selectedChildIndex is the index in the selected-children list, |
|||
not the index in the parent container. @selectedChildIndex in this |
|||
method and @childIndex in #atspi_selection_select_child |
|||
are asymmetric. |
|||
|
|||
Returns: true if the child was successfully deselected, false otherwise. |
|||
--> |
|||
<method name="DeselectSelectedChild"> |
|||
<arg direction="in" name="selectedChildIndex" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
<!-- |
|||
IsChildSelected: |
|||
|
|||
@childIndex: an index into the accessible's list of children. |
|||
|
|||
Determines whether a particular child of the accessible is |
|||
currently selected. Note that @childIndex is the index into the |
|||
standard accessible container's list of children. |
|||
|
|||
Returns: true if the specified child is currently selected, |
|||
false otherwise. |
|||
--> |
|||
<method name="IsChildSelected"> |
|||
<arg direction="in" name="childIndex" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
SelectAll: |
|||
|
|||
Attempts to select all of the accessible's children. |
|||
Not all implementors support this operation. |
|||
|
|||
Returns: true if successful, false otherwise. |
|||
--> |
|||
<method name="SelectAll"> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
ClearSelection: |
|||
|
|||
Clears the current selection, removing all selected children from the |
|||
specified accessible's selection list. |
|||
|
|||
Returns: true if successful, false otherwise. |
|||
--> |
|||
<method name="ClearSelection"> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
DeselectChild: |
|||
|
|||
@childIndex: a integer indicating which of the children |
|||
of the accessible is to be de-selected. |
|||
|
|||
Deselects a specific child of an accessible. |
|||
Note that @childIndex is the index of the child in the parent |
|||
container. |
|||
|
|||
Returns: true if the child was successfully deselected, false otherwise. |
|||
--> |
|||
<method name="DeselectChild"> |
|||
<arg direction="in" name="childIndex" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,98 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Socket: |
|||
@short_description: Interface to register an application on the registry. |
|||
--> |
|||
<interface name="org.a11y.atspi.Socket"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
Embed: |
|||
|
|||
@plug: a string for the unique bus name of the application, and an object path |
|||
for the application's' root object. |
|||
|
|||
This is the entry point for an application that wants to register itself against |
|||
the accessibility registry. The application's root object, which it passes in |
|||
@plug, must support the org.a11y.atspi.Application interface. |
|||
|
|||
When an application calls this method on the registry, the following handshake happens: |
|||
|
|||
* Application calls this method on the registry to identify itself. |
|||
|
|||
* The registry sets the "Id" property on the org.a11y.atspi.Application interface on the @plug object. |
|||
|
|||
* The Embed method returns with the bus name and object path for the registry's root object. |
|||
|
|||
Returns: the bus name and object path of the registry's root object. |
|||
--> |
|||
<method name="Embed"> |
|||
<arg direction="in" name="plug" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="out" name="socket" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
Embedded: |
|||
|
|||
@path: the object path of the socket. |
|||
|
|||
This method is called by a socket to inform the plug that it is being |
|||
embedded. The plug should register the embedding socket as its parent. |
|||
--> |
|||
<method name="Embedded"> |
|||
<arg direction="in" name="path" type="s"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
Unembed: |
|||
|
|||
@plug: a string for the unique bus name of the application, and an object path |
|||
for the application's' root object. |
|||
|
|||
Unregisters an application from the accesibility registry. It is not necessary to |
|||
call this method; the accessibility registry detects when an application |
|||
disconnects from the bus. |
|||
--> |
|||
<method name="Unembed"> |
|||
<arg direction="in" name="plug" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
Available: |
|||
|
|||
@socket: application and object path for the registry's root object. |
|||
|
|||
The accessibility registry emits this signal early during startup, when it has |
|||
registered with the DBus daemon and is available for calls from applications. |
|||
--> |
|||
<signal name="Available"> |
|||
<arg name="socket" type="(so)"> |
|||
<av:TypeDefinition> |
|||
<av:Struct Type="AtSpiObjectReference"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</signal> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<interface name="org.a11y.Status"> |
|||
<property name="IsEnabled" type="b" access="readwrite"/> |
|||
<property name="ScreenReaderEnabled" type="b" access="readwrite"/> |
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,688 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Text: |
|||
@short_description: An interface implemented by objects which place |
|||
textual information onscreen. |
|||
--> |
|||
<interface name="org.a11y.atspi.Text"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
CharacterCount: The total number of characters in a text object. |
|||
This may differ from the number of bytes that would be returned |
|||
if the text is fetched in cases where characters are expressed |
|||
using multiple bytes. |
|||
--> |
|||
<property name="CharacterCount" type="i" access="read"/> |
|||
|
|||
<!-- |
|||
CaretOffset: The current character offset of the text caret in a |
|||
text object. |
|||
--> |
|||
<property name="CaretOffset" type="i" access="read"/> |
|||
|
|||
<!-- |
|||
GetStringAtOffset: |
|||
|
|||
@offset: The character offset for which the text is being requested. |
|||
|
|||
@granularity: The text granularity. Can be one of the following: |
|||
- 0 (ATSPI_TEXT_GRANULARITY_CHAR): Granularity is defined by the |
|||
boundaries between characters (including non-printing characters) |
|||
- 1 (ATSPI_TEXT_GRANULARITY_WORD): Granularity is defined by the |
|||
boundaries of a word, starting at the beginning of the current |
|||
word and finishing at the beginning of the following one, if present. |
|||
- 2 (ATSPI_TEXT_GRANULARITY_SENTENCE): Granularity is defined by the |
|||
boundaries of a sentence, starting at the beginning of the current |
|||
sentence and finishing at the beginning of the following one, |
|||
if present. |
|||
- 3 (ATSPI_TEXT_GRANULARITY_LINE): Granularity is defined by the |
|||
boundaries of a line, starting at the beginning of the current |
|||
line and finishing at the beginning of the following one, if present. |
|||
- 4 (ATSPI_TEXT_GRANULARITY_PARAGRAPH): Granularity is defined by |
|||
the boundaries of a paragraph, starting at the beginning of the |
|||
current paragraph and finishing at the beginning of the following one, if present. |
|||
|
|||
@startOffset: The starting character offset of the string being |
|||
returned. |
|||
|
|||
@endOffset: The ending character offset of the string being |
|||
returned. |
|||
|
|||
Gets a portion of the text exposed through a text object according |
|||
to a given @offset and a specific @granularity, along with the |
|||
start and end offsets defining the boundaries of such a portion |
|||
of text. |
|||
|
|||
If @granularity is ATSPI_TEXT_GRANULARITY_CHAR the character at the |
|||
offset is returned. |
|||
|
|||
If @granularity is ATSPI_TEXT_GRANULARITY_WORD the returned string |
|||
is from the word start at or before the offset to the word start after |
|||
the offset. |
|||
|
|||
The returned string will contain the word at the offset if the offset |
|||
is inside a word and will contain the word before the offset if the |
|||
offset is not inside a word. |
|||
|
|||
If @granularity is ATSPI_TEXT_GRANULARITY_SENTENCE the returned string |
|||
is from the sentence start at or before the offset to the sentence |
|||
start after the offset. |
|||
|
|||
The returned string will contain the sentence at the offset if the offset |
|||
is inside a sentence and will contain the sentence before the offset |
|||
if the offset is not inside a sentence. |
|||
|
|||
If @granularity is ATSPI_TEXT_GRANULARITY_LINE the returned string |
|||
is from the line start at or before the offset to the line |
|||
start after the offset. |
|||
|
|||
If @granularity is ATSPI_TEXT_GRANULARITY_PARAGRAPH the returned strin |
|||
is from the start of the paragraph at or before the offset to the start |
|||
of the following paragraph after the offset. |
|||
--> |
|||
<method name="GetStringAtOffset"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="in" name="granularity" type="u"/> |
|||
<arg direction="out" name="text" type="s"/> |
|||
<arg direction="out" name="startOffset" type="i"/> |
|||
<arg direction="out" name="endOffset" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetText: |
|||
|
|||
@startOffset: a integer indicating the start of the desired text |
|||
range. |
|||
|
|||
@endOffset: a integer indicating the first character past the desired |
|||
range. |
|||
|
|||
Gets a range of text from a text object. The number of bytes in |
|||
the returned string may exceed either endOffset or |
|||
start_offset, since UTF-8 is a variable-width encoding. |
|||
|
|||
Returns: a text string containing characters from @startOffset |
|||
to @endOffset-1, inclusive, encoded as UTF-8. |
|||
--> |
|||
<method name="GetText"> |
|||
<arg direction="in" name="startOffset" type="i"/> |
|||
<arg direction="in" name="endOffset" type="i"/> |
|||
<arg direction="out" type="s"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
SetCaretOffset: |
|||
|
|||
@offset: the offset to which the text caret is to be moved. |
|||
|
|||
Moves the text caret to a given position. |
|||
|
|||
Returns: true if successful, false otherwise. |
|||
--> |
|||
<method name="SetCaretOffset"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetTextBeforeOffset: |
|||
|
|||
@offset: a integer indicating the offset from which the delimiter |
|||
search is based. |
|||
|
|||
@type: Indicates whether the desired text string is a word, sentence, |
|||
line, or attribute run. See GetTextAtOffset for more information. |
|||
|
|||
@startOffset: (out) The beginning character offset of the returned |
|||
string. |
|||
|
|||
@endOffset: (out) The ending character offset of the returned |
|||
string. |
|||
|
|||
Gets delimited text from a text object which precedes a given text |
|||
offset. |
|||
|
|||
Returns: a UTF-8 string representing the delimited text, both of |
|||
whose delimiting boundaries are before the current offset, or an |
|||
empty string if no such text exists. |
|||
|
|||
Deprecated: Please use GetStringAtOffset instead. |
|||
--> |
|||
<method name="GetTextBeforeOffset"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="in" name="type" type="u"/> |
|||
<arg direction="out" name="text" type="s"/> |
|||
<arg direction="out" name="startOffset" type="i"/> |
|||
<arg direction="out" name="endOffset" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetTextAtOffset: |
|||
|
|||
@offset: a integer indicating the offset from which the delimiter |
|||
search is based. |
|||
|
|||
@type: Indicates whether the desired text string is a word, sentence, |
|||
line, or attribute run. Should have one of the following values: |
|||
|
|||
0 (ATSPI_TEXT_BOUNDARY_CHAR): The text instance is bounded by this |
|||
character only. Start and end offsets differ by one, by definition, |
|||
for this value. |
|||
|
|||
1 (ATSPI_TEXT_BOUNDARY_WORD_START): Boundary condition is start of |
|||
a word; i.e. range is from start of one word to the start of another |
|||
word. |
|||
|
|||
2 (ATSPI_TEXT_BOUNDARY_WORD_END): Boundary condition is the end of |
|||
a word; i.e. range is from the end of one word to the end of another. |
|||
Some locales may not distinguish between words and characters or |
|||
glyphs. In particular, those locales which use wholly or partially |
|||
ideographic character sets. In these cases, characters may be |
|||
returned in lieu of multi-character substrings. |
|||
|
|||
3 (ATSPI_TEXT_BOUNDARY_SENTENCE_START: Boundary condition is start |
|||
of a sentence, as determined by the application. Some locales or |
|||
character sets may not include explicit sentence delimiters, so this |
|||
boundary type can not always be honored. Some locales will return lines |
|||
of text instead of grammatical sentences. |
|||
|
|||
4 (ATSPI_TEXT_BOUNDARY_SENTENCE_END): Boundary condition is end of |
|||
a sentence, as determined by the application, including the |
|||
sentence-delimiting character, for instance '.' Some locales or |
|||
character sets may not include explicit sentence delimiters, so |
|||
this boundary type can not always be honored. Some locales will |
|||
return lines of text instead of grammatical sentences. |
|||
|
|||
5 (ATSPI_TEXT_BOUNDARY_LINE_START): Boundary condition is the start |
|||
of a line; i.e. range is from start of one line to the start of |
|||
another. This generally means that an end-of-line character will |
|||
appear at the end of the range. |
|||
|
|||
6 (ATSPI_TEXT_BOUNDARY_LINE_END): Boundary condition is the end of |
|||
a line; i.e. range is from start of one line to the start of another. |
|||
This generally means that an end-of-line character will be the |
|||
first character of the range. |
|||
|
|||
@startOffset: (out) The beginning character offset of the returned |
|||
string. |
|||
|
|||
@endOffset: (out) The ending character offset of the returned |
|||
string. |
|||
|
|||
Gets delimited text from a text object which includes a given text |
|||
offset. |
|||
|
|||
Returns: a UTF-8 string representing the delimited text, whose |
|||
delimiting boundaries bracket the current offset, or an empty string |
|||
if no such text exists. |
|||
|
|||
Deprecated: Please use GetStringAtOffset instead. |
|||
--> |
|||
<method name="GetTextAtOffset"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="in" name="type" type="u"/> |
|||
<arg direction="out" name="text" type="s"/> |
|||
<arg direction="out" name="startOffset" type="i"/> |
|||
<arg direction="out" name="endOffset" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetTextAfterOffset: |
|||
|
|||
@offset: a integer indicating the offset from which the delimiter |
|||
search is based. |
|||
|
|||
@type: Indicates whether the desired text string is a word, sentence, |
|||
line, or attribute run. See GetTextAtOffset for more information. |
|||
|
|||
@startOffset: (out) The beginning character offset of the returned |
|||
string. |
|||
|
|||
@endOffset: (out) The ending character offset of the returned |
|||
string. |
|||
|
|||
Gets delimited text from a text object which follows a given text |
|||
offset. |
|||
|
|||
Returns: a UTF-8 string representing the delimited text, both of |
|||
whose delimiting boundaries are after or inclusive of the current |
|||
offset, or an empty string if no such text exists. |
|||
|
|||
Deprecated: Please use GetStringAtOffset instead. |
|||
--> |
|||
<method name="GetTextAfterOffset"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="in" name="type" type="u"/> |
|||
<arg direction="out" name="text" type="s"/> |
|||
<arg direction="out" name="startOffset" type="i"/> |
|||
<arg direction="out" name="endOffset" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GecharacterAtOffset: |
|||
|
|||
@offset: an integer indicating the text offset where the desired |
|||
character is located. |
|||
|
|||
Gets the character at a given offset for a text object. |
|||
|
|||
Returns: an integer representing the UCS-4 unicode code point of |
|||
the given character, or 0xFFFFFFFF if the character in question |
|||
cannot be represented in the UCS-4 encoding. |
|||
--> |
|||
<method name="GetCharacterAtOffset"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="out" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetAttributeValue: |
|||
|
|||
@offset: The character offset at which to query the attribute. |
|||
@attributeName: The attribute to query. |
|||
|
|||
Gets the value of a named attribute at a given offset. |
|||
|
|||
Returns: the value of a given attribute at the given offset, or an |
|||
empty string if not present. |
|||
--> |
|||
<method name="GetAttributeValue"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="in" name="attributeName" type="s"/> |
|||
<arg direction="out" type="s"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetAttributes: |
|||
|
|||
@offset: an integer indicating the offset from which the attribute |
|||
search is based. |
|||
|
|||
@startOffset: (out): an integer pointer indicating the start of |
|||
the desired text range. |
|||
|
|||
@endOffset: (out): an integer pointer indicating the first character |
|||
past the desired range. |
|||
|
|||
Gets the attributes applied to a range of text from a text object. |
|||
The text attributes correspond to CSS attributes where possible. |
|||
|
|||
Returns: a dictionary of attribute name/value pairs representing |
|||
the attributes at the given offset, encoded as UTF-8. |
|||
--> |
|||
<method name="GetAttributes"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="out" name="attributes" type="a{ss}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiAttributeSet"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="out" name="startOffset" type="i"/> |
|||
<arg direction="out" name="endOffset" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetDefaultAttributes: |
|||
|
|||
Gets the default attributes applied to a text object. The text |
|||
attributes correspond to CSS attributes where possible. The |
|||
combination of this attribute set and the attributes reported by |
|||
GetAttributes describes the entire set of text attributes over a |
|||
range. |
|||
|
|||
Returns: A dictionary of key/value pairs containing the default |
|||
attributes applied to a text object, (exclusive of explicitly-set |
|||
attributes), encoded as UTF-8. |
|||
--> |
|||
<method name="GetDefaultAttributes"> |
|||
<arg direction="out" type="a{ss}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiAttributeSet"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetCharacterExtents: |
|||
|
|||
@offset: an integer indicating the offset of the text character for |
|||
whom boundary information is requested. |
|||
|
|||
@coordType: Specifies the coordinate system to use for the return value. |
|||
See the org.a11y.Atspi.Component documentation for a description |
|||
of the values that can be specified. |
|||
|
|||
Gets a bounding box containing the glyph representing |
|||
the character at a particular text offset. |
|||
The returned values are meaningful only if the Text has both |
|||
STATE_VISIBLE and STATE_SHOWING. |
|||
--> |
|||
<method name="GetCharacterExtents"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="in" name="coordType" type="u"/> |
|||
<arg direction="out" name="x" type="i"/> |
|||
<arg direction="out" name="y" type="i"/> |
|||
<arg direction="out" name="width" type="i"/> |
|||
<arg direction="out" name="height" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetOffsetAtPoint: |
|||
|
|||
@x: the x coordinate of the point to be queried. |
|||
|
|||
@y: the y coordinate of the point to be queried. |
|||
|
|||
@coordType: Specifies the coordinate system to use for the return value. |
|||
See the org.a11y.Atspi.Component documentation for a description |
|||
of the values that can be specified. |
|||
|
|||
Gets the character offset into the text at a given point. |
|||
|
|||
Returns: the offset (as an integer) at the point (@x, @y) |
|||
in the specified coordinate system. |
|||
--> |
|||
<method name="GetOffsetAtPoint"> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="in" name="coordType" type="u"/> |
|||
<arg direction="out" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetNSelections: |
|||
|
|||
Gets the number of active non-contiguous selections for a text |
|||
object. |
|||
|
|||
Returns: an integer indicating the current number of non-contiguous |
|||
text selections active within a text object. |
|||
--> |
|||
<method name="GetNSelections"> |
|||
<arg direction="out" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetSelection: |
|||
|
|||
@selectionNum: an integer indicating which selection to query. |
|||
|
|||
@startOffset: (out): The starting character offset of the given |
|||
selection. |
|||
|
|||
@endOffset: (out): The ending character offset of the given |
|||
selection. |
|||
|
|||
Gets the bounds of the @selectionNum-th active text selection for |
|||
a Text object. |
|||
--> |
|||
<method name="GetSelection"> |
|||
<arg direction="in" name="selectionNum" type="i"/> |
|||
<arg direction="out" name="startOffset" type="i"/> |
|||
<arg direction="out" name="endOffset" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
AddSelection: |
|||
|
|||
@startOffset: the starting character offset of the desired new |
|||
selection. |
|||
|
|||
@endOffset: the offset of the first character after the new |
|||
selection. |
|||
|
|||
Selects some text (adds a text selection) in a text object. |
|||
|
|||
Returns: true if successful, false otherwise. |
|||
--> |
|||
<method name="AddSelection"> |
|||
<arg direction="in" name="startOffset" type="i"/> |
|||
<arg direction="in" name="endOffset" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
RemoveSelection: |
|||
|
|||
@selectionNum: a integer indicating which text selection to remove. |
|||
|
|||
De-selects a text selection. |
|||
* |
|||
Returns: true if successful, false otherwise. |
|||
--> |
|||
<method name="RemoveSelection"> |
|||
<arg direction="in" name="selectionNum" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
SetSelection: |
|||
|
|||
@selectionNum: a zero-offset index indicating which text selection |
|||
to modify. |
|||
|
|||
@startOffset: an integer indicating the new starting offset for the |
|||
selection. |
|||
|
|||
@endOffset: an integer indicating the desired new offset of the |
|||
first character after the selection. |
|||
|
|||
Changes the bounds of an existing text selection. |
|||
|
|||
Returns: true if successful, false otherwise. |
|||
--> |
|||
<method name="SetSelection"> |
|||
<arg direction="in" name="selectionNum" type="i"/> |
|||
<arg direction="in" name="startOffset" type="i"/> |
|||
<arg direction="in" name="endOffset" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetRangeExtents: |
|||
|
|||
@startOffset: an integer indicating the offset of the first text |
|||
character for whom boundary information is requested. |
|||
|
|||
@endOffset: a integer indicating the offset of the text character |
|||
after the last character for whom boundary information is requested. |
|||
|
|||
@coordType: Specifies the coordinate system to use for the return value. |
|||
See the org.a11y.Atspi.Component documentation for a description |
|||
of the values that can be specified. |
|||
|
|||
Gets the bounding box for text within a range in a text object. |
|||
The returned values are meaningful only if the Text has both |
|||
STATE_VISIBLE and STATE_SHOWING. |
|||
--> |
|||
<method name="GetRangeExtents"> |
|||
<arg direction="in" name="startOffset" type="i"/> |
|||
<arg direction="in" name="endOffset" type="i"/> |
|||
<arg direction="in" name="coordType" type="u"/> |
|||
<arg direction="out" name="x" type="i"/> |
|||
<arg direction="out" name="y" type="i"/> |
|||
<arg direction="out" name="width" type="i"/> |
|||
<arg direction="out" name="height" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetBoundedRanges: |
|||
|
|||
@x: the 'starting' x coordinate of the bounding box. |
|||
|
|||
@y: the 'starting' y coordinate of the bounding box. |
|||
|
|||
@width: the x extent of the bounding box. |
|||
|
|||
@height: the y extent of the bounding box. |
|||
|
|||
@coordType: Specifies the coordinate system that should be used to |
|||
interpret @x and @y. See the org.a11y.Atspi.Component documentation |
|||
for a description of the values that can be specified. |
|||
|
|||
@xClipType: Indicates how to treat characters that intersect the |
|||
bounding box's x extents. |
|||
|
|||
@yClipType: Indicates how to treat characters that intersect the |
|||
bounding box's y extents. |
|||
|
|||
@xClipType and yClipType can have the following values: |
|||
- 0 (ATSPI_TEXT_CLIP_NONE): No characters/glyphs are omitted. |
|||
- 1 (ATSPI_TEXT_CLIP_MIN): Characters/glyphs clipped by the minimum |
|||
coordinate are omitted. |
|||
- 2 (ATSPI_TEXT_CLIP_MAX): Characters/glyphs which intersect the |
|||
maximum coordinate are omitted. |
|||
- 3 (ATSPI_TEXT_CLIP_BOTH): Only glyphs falling entirely within the |
|||
region bounded by min and max are retained. |
|||
|
|||
Gets the ranges of text from an Text object which lie within the |
|||
bounds defined by (@x, @y) and (@x+@width, @y+@height). |
|||
|
|||
Returns an array of structures with the following elements: |
|||
- The starting character offset of the range. |
|||
- The ending character offset of the range. |
|||
- The contents of the range. |
|||
- A variant. Currently unused (if this specification is revisited, |
|||
then this should probably be removed). |
|||
--> |
|||
<method name="GetBoundedRanges"> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="in" name="width" type="i"/> |
|||
<arg direction="in" name="height" type="i"/> |
|||
<arg direction="in" name="coordType" type="u"/> |
|||
<arg direction="in" name="xClipType" type="u"/> |
|||
<arg direction="in" name="yClipType" type="u"/> |
|||
<arg direction="out" type="a(iisv)"> |
|||
<av:TypeDefinition> |
|||
<av:List> |
|||
<av:Struct Type="AtSpiTextRange"/> |
|||
</av:List> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetAttributeRun: |
|||
|
|||
@offset: an integer indicating the offset from which the attribute |
|||
search is based. |
|||
|
|||
@includeDefaults: a #bool that, when set as false, indicates the |
|||
call should only return those attributes which are explicitly set |
|||
on the current attribute run, omitting any attributes which are |
|||
inherited from the default values. |
|||
|
|||
Gets a set of attributes applied to a range of text from a text |
|||
object, optionally including its 'default' attributes. |
|||
|
|||
Returns a dictionary of key/value pairs specifying the |
|||
attributes. |
|||
--> |
|||
<method name="GetAttributeRun"> |
|||
<arg direction="in" name="offset" type="i"/> |
|||
<arg direction="in" name="includeDefaults" type="b"/> |
|||
<arg direction="out" name="attributes" type="a{ss}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiAttributeSet"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
<arg direction="out" name="startOffset" type="i"/> |
|||
<arg direction="out" name="endOffset" type="i"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
GetDefaultAttributeSet: |
|||
|
|||
Returns the default attributes for the text object. |
|||
--> |
|||
<method name="GetDefaultAttributeSet"> |
|||
<arg direction="out" type="a{ss}"> |
|||
<av:TypeDefinition> |
|||
<av:Dictionary Type="AtSpiAttributeSet"/> |
|||
</av:TypeDefinition> |
|||
</arg> |
|||
</method> |
|||
|
|||
<!-- |
|||
ScrollSubstringTo: |
|||
|
|||
@startOffset: a integer indicating the start of the desired text |
|||
range. |
|||
|
|||
@endOffset: a integer indicating the first character past the |
|||
desired range. |
|||
|
|||
@type: Indicates where the object should be placed on the screen, |
|||
as follows: |
|||
- 0 (ATSPI_SCROLL_TOP_LEFT): Scroll the object to the top left corner |
|||
of the window. |
|||
- 1 (ATSPI_SCROLL_BOTTOM_RIGHT): Scroll the object to the bottom right |
|||
corner of the window. |
|||
- 2 (ATSPI_SCROLL_TOP_EDGE): Scroll the object to the top edge of |
|||
the window. |
|||
- 3 (ATSPI_SCROLL_BOTTOM_EDGE): Scroll the object to the bottom edge |
|||
of the window. |
|||
- 4 (ATSPI_SCROLL_LEFT_EDGE: Scroll the object to the left edge of the |
|||
window. |
|||
- 5 (ATSPI_SCROLL_RIGHT_EDGE): Scroll the object to the right edge of |
|||
the window. |
|||
- 6 (ATSPI_SCROLL_ANYWHERE): Scroll the object to application-dependent |
|||
position on the window. |
|||
|
|||
Scrolls whatever container of the Text text range so it becomes |
|||
visible on the screen. |
|||
|
|||
Returns: true if successful, false otherwise. |
|||
--> |
|||
<method name="ScrollSubstringTo"> |
|||
<arg direction="in" name="startOffset" type="i"/> |
|||
<arg direction="in" name="endOffset" type="i"/> |
|||
<arg direction="in" name="type" type="u"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
<!-- |
|||
ScrollSubstringToPoint: |
|||
|
|||
@startOffset: an integer indicating the start of the desired text |
|||
range. |
|||
|
|||
@endOffset: an integer indicating the first character past the |
|||
desired range. |
|||
|
|||
@coordType: a CoordType indicating whether the coordinates are relative to |
|||
the screen, to the window, or to the parent object. See the |
|||
documentation for org.a11y.Atspi.Component for a description of |
|||
the allowed values. |
|||
|
|||
@x: the x coordinate of the point to reach |
|||
|
|||
@y: the y coordinate of the point to reach |
|||
|
|||
Scrolls whatever container of the text range so it becomes |
|||
visible on the screen at a given position. |
|||
|
|||
Returns: true if successful, false otherwise. |
|||
--> |
|||
<method name="ScrollSubstringToPoint"> |
|||
<arg direction="in" name="startOffset" type="i"/> |
|||
<arg direction="in" name="endOffset" type="i"/> |
|||
<arg direction="in" name="coordType" type="u"/> |
|||
<arg direction="in" name="x" type="i"/> |
|||
<arg direction="in" name="y" type="i"/> |
|||
<arg direction="out" type="b"/> |
|||
</method> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,158 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<Types xmlns="http://avaloniaui.net/dbus/1.0"> |
|||
<Struct Name="AtSpiObjectReference"> |
|||
<Property Name="Service"/> |
|||
<Property Name="Path"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiAction"> |
|||
<Property Name="LocalizedName"/> |
|||
<Property Name="LocalizedDescription"/> |
|||
<Property Name="KeyBinding"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiAccessibleCacheItem"> |
|||
<Property Name="Path" Type="AtSpiObjectReference"/> |
|||
<Property Name="Application" Type="AtSpiObjectReference"/> |
|||
<Property Name="Parent" Type="AtSpiObjectReference"/> |
|||
<Property Name="IndexInParent"/> |
|||
<Property Name="ChildCount"/> |
|||
<Property Name="SupportedInterfaces"> |
|||
<List/> |
|||
</Property> |
|||
<Property Name="Name"/> |
|||
<Property Name="Role"/> |
|||
<Property Name="Description"/> |
|||
<Property Name="State"> |
|||
<List/> |
|||
</Property> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiEventListener"> |
|||
<Property Name="ListenerAddress"/> |
|||
<Property Name="EventName"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiRelationEntry"> |
|||
<Property Name="RelationType"/> |
|||
<Property Name="Targets"> |
|||
<List> |
|||
<Struct Type="AtSpiObjectReference"/> |
|||
</List> |
|||
</Property> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiTextRange"> |
|||
<Property Name="StartOffset"/> |
|||
<Property Name="EndOffset"/> |
|||
<Property Name="Contents"/> |
|||
<Property Name="Value"/> |
|||
</Struct> |
|||
|
|||
<Dictionary Name="AtSpiAttributeSet"> |
|||
<Key Name="Key"/> |
|||
<Value Name="Value"/> |
|||
</Dictionary> |
|||
|
|||
<Dictionary Name="AtSpiEventProperties"> |
|||
<Key Name="Key"/> |
|||
<Value Name="Value"/> |
|||
</Dictionary> |
|||
|
|||
<Struct Name="AtSpiDeviceEvent"> |
|||
<Property Name="Type"/> |
|||
<Property Name="Id"/> |
|||
<Property Name="HardwareCode"/> |
|||
<Property Name="Modifiers"> |
|||
<BitFlags Type="AtSpiModifierMask"/> |
|||
</Property> |
|||
<Property Name="Timestamp"/> |
|||
<Property Name="Text"/> |
|||
<Property Name="IsText"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiMatchRule"> |
|||
<Property Name="States"> |
|||
<List/> |
|||
</Property> |
|||
<Property Name="StateMatchType"/> |
|||
<Property Name="Attributes" Type="AtSpiAttributeSet"/> |
|||
<Property Name="AttributeMatchType"/> |
|||
<Property Name="Roles"> |
|||
<List/> |
|||
</Property> |
|||
<Property Name="RoleMatchType"/> |
|||
<Property Name="Interfaces"> |
|||
<List/> |
|||
</Property> |
|||
<Property Name="InterfaceMatchType"/> |
|||
<Property Name="Invert"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiKeyDefinition"> |
|||
<Property Name="KeyCode"/> |
|||
<Property Name="KeySym"/> |
|||
<Property Name="KeyString"/> |
|||
<Property Name="Unused"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiEventMode"> |
|||
<Property Name="Synchronous"/> |
|||
<Property Name="Preemptive"/> |
|||
<Property Name="Global"/> |
|||
</Struct> |
|||
|
|||
<BitFlags Name="AtSpiEventTypeMask"> |
|||
<BitFlag Name="KeyPress" Value="1"/> |
|||
<BitFlag Name="KeyRelease" Value="2"/> |
|||
</BitFlags> |
|||
|
|||
<BitFlags Name="AtSpiModifierMask"> |
|||
<BitFlag Name="Shift" Value="1"/> |
|||
<BitFlag Name="Lock" Value="2"/> |
|||
<BitFlag Name="Control" Value="4"/> |
|||
<BitFlag Name="Mod1" Value="8"/> |
|||
<BitFlag Name="Mod2" Value="16"/> |
|||
<BitFlag Name="Mod3" Value="32"/> |
|||
<BitFlag Name="Mod4" Value="64"/> |
|||
<BitFlag Name="Mod5" Value="128"/> |
|||
</BitFlags> |
|||
|
|||
<Struct Name="AtSpiKeystrokeListener"> |
|||
<Property Name="BusName"/> |
|||
<Property Name="Path"/> |
|||
<Property Name="Type"/> |
|||
<Property Name="Types"> |
|||
<BitFlags Type="AtSpiEventTypeMask"/> |
|||
</Property> |
|||
<Property Name="Keys"> |
|||
<List> |
|||
<Struct Type="AtSpiKeyDefinition"/> |
|||
</List> |
|||
</Property> |
|||
<Property Name="Mask"> |
|||
<BitFlags Type="AtSpiModifierMask"/> |
|||
</Property> |
|||
<Property Name="Mode" Type="AtSpiEventMode"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiTextSelection"> |
|||
<Property Name="StartObject" Type="AtSpiObjectReference"/> |
|||
<Property Name="StartOffset"/> |
|||
<Property Name="EndObject" Type="AtSpiObjectReference"/> |
|||
<Property Name="EndOffset"/> |
|||
<Property Name="IsStartActive"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiRect"> |
|||
<Property Name="X"/> |
|||
<Property Name="Y"/> |
|||
<Property Name="Width"/> |
|||
<Property Name="Height"/> |
|||
</Struct> |
|||
|
|||
<Struct Name="AtSpiPoint"> |
|||
<Property Name="X"/> |
|||
<Property Name="Y"/> |
|||
</Struct> |
|||
</Types> |
|||
@ -0,0 +1,47 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<!-- |
|||
org.a11y.atspi.Value: |
|||
@short_description: An interface supporting a one-dimensional scalar |
|||
to be modified, or which reflects its value. |
|||
--> |
|||
<interface name="org.a11y.atspi.Value"> |
|||
|
|||
<!-- |
|||
Version: The version of this interface. |
|||
|
|||
This property is incremented by one every time a new method, signal, or property |
|||
is added to this interface. |
|||
--> |
|||
<property name="version" type="u" access="read"/> |
|||
|
|||
<!-- |
|||
MinimumValue: The minimum allowed value. |
|||
--> |
|||
<property name="MinimumValue" type="d" access="read"/> |
|||
|
|||
<!-- |
|||
MaximumValue: The maximum allowed value. |
|||
--> |
|||
<property name="MaximumValue" type="d" access="read"/> |
|||
|
|||
<!-- |
|||
MinimumIncrement: The minimum increment by which a value can be |
|||
adjusted. |
|||
--> |
|||
<property name="MinimumIncrement" type="d" access="read"/> |
|||
|
|||
<!-- |
|||
CurrentValue: The current value. |
|||
--> |
|||
<property name="CurrentValue" type="d" access="readwrite"/> |
|||
|
|||
<!-- |
|||
Text: a human readable text alternative associated with the value, |
|||
if available. |
|||
--> |
|||
<property name="Text" type="s" access="read"/> |
|||
|
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,9 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<node xmlns:av="http://avaloniaui.net/dbus/1.0"> |
|||
<av:ImportTypes>Types.xml</av:ImportTypes> |
|||
<interface name="org.a11y.Bus"> |
|||
<method name="GetAddress"> |
|||
<arg direction="out" type="s"/> |
|||
</method> |
|||
</interface> |
|||
</node> |
|||
@ -0,0 +1,152 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI Accessible interface for an AutomationPeer-backed node.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiAccessibleHandler(AtSpiServer server, AtSpiNode node) : IOrgA11yAtspiAccessible |
|||
{ |
|||
public uint Version => AccessibleVersion; |
|||
|
|||
public string Name => AtSpiNode.GetAccessibleName(node.Peer); |
|||
|
|||
public string Description => node.Peer.GetHelpText(); |
|||
|
|||
public AtSpiObjectReference Parent |
|||
{ |
|||
get |
|||
{ |
|||
// For window nodes, return the ApplicationAtSpiNode as parent
|
|||
if (node is RootAtSpiNode { AppRoot: { } appRoot }) |
|||
return new AtSpiObjectReference( |
|||
server.UniqueName, new DBusObjectPath(appRoot.Path)); |
|||
|
|||
return server.GetReference(node.Parent); |
|||
} |
|||
} |
|||
|
|||
public int ChildCount => node.EnsureChildren().Count; |
|||
|
|||
public string Locale => ResolveLocale(); |
|||
|
|||
public string AccessibleId => node.Peer.GetAutomationId() ?? string.Empty; |
|||
|
|||
public string HelpText => node.Peer.GetHelpText(); |
|||
|
|||
public ValueTask<AtSpiObjectReference> GetChildAtIndexAsync(int index) |
|||
{ |
|||
var children = node.EnsureChildren(); |
|||
if (index < 0 || index >= children.Count) |
|||
return ValueTask.FromResult(server.GetNullReference()); |
|||
|
|||
return ValueTask.FromResult(server.GetReference(children[index])); |
|||
} |
|||
|
|||
public ValueTask<List<AtSpiObjectReference>> GetChildrenAsync() |
|||
{ |
|||
var children = node.EnsureChildren(); |
|||
var refs = new List<AtSpiObjectReference>(children.Count); |
|||
foreach (var child in children) |
|||
{ |
|||
refs.Add(server.GetReference(child)); |
|||
} |
|||
|
|||
return ValueTask.FromResult(refs); |
|||
} |
|||
|
|||
public ValueTask<int> GetIndexInParentAsync() |
|||
{ |
|||
var parent = node.Parent; |
|||
if (parent is null) |
|||
return ValueTask.FromResult(-1); |
|||
|
|||
var siblings = parent.EnsureChildren(); |
|||
for (var i = 0; i < siblings.Count; i++) |
|||
{ |
|||
if (ReferenceEquals(siblings[i], node)) |
|||
return ValueTask.FromResult(i); |
|||
} |
|||
|
|||
return ValueTask.FromResult(-1); |
|||
} |
|||
|
|||
public ValueTask<List<AtSpiRelationEntry>> GetRelationSetAsync() |
|||
{ |
|||
var relations = new List<AtSpiRelationEntry>(); |
|||
|
|||
var labeledBy = node.Peer.GetLabeledBy(); |
|||
if (labeledBy is not null) |
|||
{ |
|||
var labelNode = server.TryGetAttachedNode(labeledBy); |
|||
if (labelNode is not null) |
|||
{ |
|||
// Relation type 2 = LABELLED_BY
|
|||
relations.Add(new AtSpiRelationEntry(2, [server.GetReference(labelNode)])); |
|||
} |
|||
} |
|||
|
|||
return ValueTask.FromResult(relations); |
|||
} |
|||
|
|||
public ValueTask<uint> GetRoleAsync() |
|||
{ |
|||
var role = AtSpiNode.ToAtSpiRole(node.Peer.GetAutomationControlType(), node.Peer); |
|||
return ValueTask.FromResult((uint)role); |
|||
} |
|||
|
|||
public ValueTask<string> GetRoleNameAsync() |
|||
{ |
|||
var role = AtSpiNode.ToAtSpiRole(node.Peer.GetAutomationControlType(), node.Peer); |
|||
return ValueTask.FromResult(AtSpiNode.ToAtSpiRoleName(role)); |
|||
} |
|||
|
|||
public ValueTask<string> GetLocalizedRoleNameAsync() |
|||
{ |
|||
var role = AtSpiNode.ToAtSpiRole(node.Peer.GetAutomationControlType(), node.Peer); |
|||
return ValueTask.FromResult(AtSpiNode.ToAtSpiRoleName(role)); |
|||
} |
|||
|
|||
public ValueTask<List<uint>> GetStateAsync() |
|||
{ |
|||
return ValueTask.FromResult(node.ComputeStates()); |
|||
} |
|||
|
|||
public ValueTask<AtSpiAttributeSet> GetAttributesAsync() |
|||
{ |
|||
var attrs = new AtSpiAttributeSet { ["toolkit"] = "Avalonia" }; |
|||
|
|||
var name = node.Peer.GetName(); |
|||
if (!string.IsNullOrEmpty(name)) |
|||
attrs["explicit-name"] = "true"; |
|||
|
|||
var acceleratorKey = node.Peer.GetAcceleratorKey(); |
|||
if (!string.IsNullOrEmpty(acceleratorKey)) |
|||
attrs["accelerator-key"] = acceleratorKey; |
|||
|
|||
var accessKey = node.Peer.GetAccessKey(); |
|||
if (!string.IsNullOrEmpty(accessKey)) |
|||
attrs["access-key"] = accessKey; |
|||
|
|||
return ValueTask.FromResult(attrs); |
|||
} |
|||
|
|||
public ValueTask<AtSpiObjectReference> GetApplicationAsync() |
|||
{ |
|||
return ValueTask.FromResult(server.GetRootReference()); |
|||
} |
|||
|
|||
public ValueTask<List<string>> GetInterfacesAsync() |
|||
{ |
|||
var interfaces = node.GetSupportedInterfaces(); |
|||
var sorted = interfaces.OrderBy(static i => i, StringComparer.Ordinal).ToList(); |
|||
return ValueTask.FromResult(sorted); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,172 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Automation; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI Action interface (invoke, toggle, expand/collapse, scroll).
|
|||
/// </summary>
|
|||
internal sealed class AtSpiActionHandler : IOrgA11yAtspiAction |
|||
{ |
|||
private readonly AtSpiNode _node; |
|||
private readonly List<ActionEntry> _actions; |
|||
|
|||
public AtSpiActionHandler(AtSpiServer server, AtSpiNode node) |
|||
{ |
|||
_ = server; |
|||
_node = node; |
|||
_actions = BuildActionList(); |
|||
} |
|||
|
|||
public uint Version => ActionVersion; |
|||
|
|||
public int NActions => _actions.Count; |
|||
|
|||
public ValueTask<string> GetDescriptionAsync(int index) |
|||
{ |
|||
if (index >= 0 && index < _actions.Count) |
|||
return ValueTask.FromResult(_actions[index].Description); |
|||
return ValueTask.FromResult(string.Empty); |
|||
} |
|||
|
|||
public ValueTask<string> GetNameAsync(int index) |
|||
{ |
|||
if (index >= 0 && index < _actions.Count) |
|||
return ValueTask.FromResult(_actions[index].ActionName); |
|||
return ValueTask.FromResult(string.Empty); |
|||
} |
|||
|
|||
public ValueTask<string> GetLocalizedNameAsync(int index) |
|||
{ |
|||
if (index >= 0 && index < _actions.Count) |
|||
return ValueTask.FromResult(_actions[index].LocalizedName); |
|||
return ValueTask.FromResult(string.Empty); |
|||
} |
|||
|
|||
public ValueTask<string> GetKeyBindingAsync(int index) |
|||
{ |
|||
if (index >= 0 && index < _actions.Count) |
|||
return ValueTask.FromResult(_actions[index].KeyBinding); |
|||
return ValueTask.FromResult(string.Empty); |
|||
} |
|||
|
|||
public ValueTask<List<AtSpiAction>> GetActionsAsync() |
|||
{ |
|||
var result = new List<AtSpiAction>(_actions.Count); |
|||
result.AddRange(_actions.Select(entry => new AtSpiAction(entry.LocalizedName, entry.Description, entry.KeyBinding))); |
|||
return ValueTask.FromResult(result); |
|||
} |
|||
|
|||
public ValueTask<bool> DoActionAsync(int index) |
|||
{ |
|||
if (index < 0 || index >= _actions.Count) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var action = _actions[index]; |
|||
ExecuteAction(action.ActionName); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
private void ExecuteAction(string actionName) |
|||
{ |
|||
switch (actionName) |
|||
{ |
|||
case "click": |
|||
_node.Peer.GetProvider<IInvokeProvider>()?.Invoke(); |
|||
break; |
|||
case "toggle": |
|||
_node.Peer.GetProvider<IToggleProvider>()?.Toggle(); |
|||
break; |
|||
case "expand or collapse": |
|||
if (_node.Peer.GetProvider<IExpandCollapseProvider>() is { } expandCollapseAction) |
|||
{ |
|||
if (expandCollapseAction.ExpandCollapseState == ExpandCollapseState.Collapsed) |
|||
expandCollapseAction.Expand(); |
|||
else |
|||
expandCollapseAction.Collapse(); |
|||
} |
|||
break; |
|||
case "scroll up": |
|||
_node.Peer.GetProvider<IScrollProvider>()?.Scroll( |
|||
ScrollAmount.NoAmount, ScrollAmount.SmallDecrement); |
|||
break; |
|||
case "scroll down": |
|||
_node.Peer.GetProvider<IScrollProvider>()?.Scroll( |
|||
ScrollAmount.NoAmount, ScrollAmount.SmallIncrement); |
|||
break; |
|||
case "scroll left": |
|||
_node.Peer.GetProvider<IScrollProvider>()?.Scroll( |
|||
ScrollAmount.SmallDecrement, ScrollAmount.NoAmount); |
|||
break; |
|||
case "scroll right": |
|||
_node.Peer.GetProvider<IScrollProvider>()?.Scroll( |
|||
ScrollAmount.SmallIncrement, ScrollAmount.NoAmount); |
|||
break; |
|||
// Provisional: activate selectable items (TabItem, ListBoxItem) via Action.
|
|||
case "select": |
|||
_node.Peer.GetProvider<ISelectionItemProvider>()?.Select(); |
|||
break; |
|||
} |
|||
} |
|||
// TODO: Proper mapping of ActionList keybindings to AutomationPeers.
|
|||
private List<ActionEntry> BuildActionList() |
|||
{ |
|||
var actions = new List<ActionEntry>(); |
|||
|
|||
if (_node.Peer.GetProvider<IInvokeProvider>() is not null) |
|||
{ |
|||
var acceleratorKey = _node.Peer.GetAcceleratorKey() ?? string.Empty; |
|||
actions.Add(new ActionEntry("click", "Click", "Performs the default action", acceleratorKey)); |
|||
} |
|||
|
|||
if (_node.Peer.GetProvider<IToggleProvider>() is not null) |
|||
actions.Add(new ActionEntry("toggle", "Toggle", "Toggles the control state", string.Empty)); |
|||
|
|||
if (_node.Peer.GetProvider<IExpandCollapseProvider>() is not null) |
|||
actions.Add(new ActionEntry("expand or collapse", "Expand or Collapse", "Expands or collapses the control", string.Empty)); |
|||
|
|||
if (_node.Peer.GetProvider<IScrollProvider>() is { } scroll) |
|||
{ |
|||
if (scroll.VerticallyScrollable) |
|||
{ |
|||
actions.Add(new ActionEntry("scroll up", "Scroll Up", "Scrolls the view up", string.Empty)); |
|||
actions.Add(new ActionEntry("scroll down", "Scroll Down", "Scrolls the view down", string.Empty)); |
|||
} |
|||
|
|||
if (scroll.HorizontallyScrollable) |
|||
{ |
|||
actions.Add(new ActionEntry("scroll left", "Scroll Left", "Scrolls the view left", string.Empty)); |
|||
actions.Add(new ActionEntry("scroll right", "Scroll Right", "Scrolls the view right", string.Empty)); |
|||
} |
|||
} |
|||
|
|||
if (_node.Peer.GetProvider<ISelectionItemProvider>() is not null |
|||
&& _node.Peer.GetProvider<IInvokeProvider>() is null) |
|||
{ |
|||
actions.Add(new ActionEntry("select", "Select", "Selects this item", string.Empty)); |
|||
} |
|||
|
|||
return actions; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Describes a single AT-SPI action exposed by a node.
|
|||
/// </summary>
|
|||
private readonly struct ActionEntry( |
|||
string actionName, |
|||
string localizedName, |
|||
string description, |
|||
string keyBinding) |
|||
{ |
|||
public string ActionName { get; } = actionName; |
|||
public string LocalizedName { get; } = localizedName; |
|||
public string Description { get; } = description; |
|||
public string KeyBinding { get; } = keyBinding; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,272 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI Collection interface for match-based node queries.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiCollectionHandler(AtSpiServer server, AtSpiNode node) : IOrgA11yAtspiCollection |
|||
{ |
|||
private enum MatchType |
|||
{ |
|||
Invalid = 0, |
|||
All = 1, |
|||
Any = 2, |
|||
None = 3, |
|||
Empty = 4, |
|||
} |
|||
|
|||
public uint Version => CollectionVersion; |
|||
|
|||
public ValueTask<List<AtSpiObjectReference>> GetMatchesAsync( |
|||
AtSpiMatchRule rule, uint sortby, int count, bool traverse) |
|||
{ |
|||
var results = new List<AtSpiObjectReference>(); |
|||
CollectMatches(node, rule, count, traverse, results, skipSelf: true); |
|||
return ValueTask.FromResult(results); |
|||
} |
|||
|
|||
public ValueTask<List<AtSpiObjectReference>> GetMatchesToAsync( |
|||
DBusObjectPath currentObject, AtSpiMatchRule rule, uint sortby, uint tree, |
|||
bool limitScope, int count, bool traverse) |
|||
{ |
|||
// GetMatchesTo: find matches after currentObject in tree order
|
|||
var results = new List<AtSpiObjectReference>(); |
|||
var found = false; |
|||
CollectMatchesOrdered(node, rule, count, traverse, results, |
|||
currentObject.ToString(), ref found, after: true); |
|||
return ValueTask.FromResult(results); |
|||
} |
|||
|
|||
public ValueTask<List<AtSpiObjectReference>> GetMatchesFromAsync( |
|||
DBusObjectPath currentObject, AtSpiMatchRule rule, uint sortby, uint tree, |
|||
int count, bool traverse) |
|||
{ |
|||
// GetMatchesFrom: find matches before currentObject in tree order
|
|||
var results = new List<AtSpiObjectReference>(); |
|||
var found = false; |
|||
CollectMatchesOrdered(node, rule, count, traverse, results, |
|||
currentObject.ToString(), ref found, after: false); |
|||
return ValueTask.FromResult(results); |
|||
} |
|||
|
|||
public ValueTask<AtSpiObjectReference> GetActiveDescendantAsync() |
|||
{ |
|||
// Not implemented in most toolkits
|
|||
return ValueTask.FromResult(server.GetNullReference()); |
|||
} |
|||
|
|||
private void CollectMatches( |
|||
AtSpiNode parent, AtSpiMatchRule rule, int count, bool traverse, |
|||
List<AtSpiObjectReference> results, bool skipSelf) |
|||
{ |
|||
if (count > 0 && results.Count >= count) |
|||
return; |
|||
|
|||
if (!skipSelf && MatchesRule(parent, rule)) |
|||
{ |
|||
results.Add(server.GetReference(parent)); |
|||
if (count > 0 && results.Count >= count) |
|||
return; |
|||
} |
|||
|
|||
var children = parent.EnsureChildren(); |
|||
foreach (var childNode in children) |
|||
{ |
|||
if (MatchesRule(childNode, rule)) |
|||
{ |
|||
results.Add(server.GetReference(childNode)); |
|||
if (count > 0 && results.Count >= count) |
|||
return; |
|||
} |
|||
|
|||
if (traverse) |
|||
CollectMatches(childNode, rule, count, traverse, results, skipSelf: true); |
|||
} |
|||
} |
|||
|
|||
private void CollectMatchesOrdered( |
|||
AtSpiNode parent, AtSpiMatchRule rule, int count, bool traverse, |
|||
List<AtSpiObjectReference> results, string targetPath, ref bool pastTarget, |
|||
bool after) |
|||
{ |
|||
if (count > 0 && results.Count >= count) |
|||
return; |
|||
|
|||
var children = parent.EnsureChildren(); |
|||
foreach (var childNode in children) |
|||
{ |
|||
if (count > 0 && results.Count >= count) |
|||
return; |
|||
|
|||
if (string.Equals(childNode.Path, targetPath, StringComparison.Ordinal)) |
|||
{ |
|||
pastTarget = true; |
|||
if (traverse) |
|||
CollectMatchesOrdered(childNode, rule, count, traverse, results, |
|||
targetPath, ref pastTarget, after); |
|||
continue; |
|||
} |
|||
|
|||
var shouldInclude = after ? pastTarget : !pastTarget; |
|||
if (shouldInclude && MatchesRule(childNode, rule)) |
|||
{ |
|||
results.Add(server.GetReference(childNode)); |
|||
if (count > 0 && results.Count >= count) |
|||
return; |
|||
} |
|||
|
|||
if (traverse) |
|||
CollectMatchesOrdered(childNode, rule, count, traverse, results, |
|||
targetPath, ref pastTarget, after); |
|||
} |
|||
} |
|||
|
|||
private static bool MatchesRule(AtSpiNode node, AtSpiMatchRule rule) |
|||
{ |
|||
var match = MatchesStates(node, rule.States, (MatchType)rule.StateMatchType) |
|||
&& MatchesRoles(node, rule.Roles, (MatchType)rule.RoleMatchType) |
|||
&& MatchesInterfaces(node, rule.Interfaces, (MatchType)rule.InterfaceMatchType) |
|||
&& MatchesAttributes(node, rule.Attributes, (MatchType)rule.AttributeMatchType); |
|||
|
|||
return rule.Invert ? !match : match; |
|||
} |
|||
|
|||
private static bool MatchesStates(AtSpiNode node, List<int> ruleStates, MatchType matchType) |
|||
{ |
|||
if (matchType is MatchType.Invalid or MatchType.Empty) |
|||
return matchType != MatchType.Empty || IsEmptyBitSet(ruleStates); |
|||
|
|||
if (IsEmptyBitSet(ruleStates)) |
|||
return true; |
|||
|
|||
var nodeStates = node.ComputeStates(); |
|||
var nodeLow = nodeStates.Count > 0 ? nodeStates[0] : 0u; |
|||
var nodeHigh = nodeStates.Count > 1 ? nodeStates[1] : 0u; |
|||
var ruleLow = ruleStates.Count > 0 ? (uint)ruleStates[0] : 0u; |
|||
var ruleHigh = ruleStates.Count > 1 ? (uint)ruleStates[1] : 0u; |
|||
|
|||
return matchType switch |
|||
{ |
|||
MatchType.All => (nodeLow & ruleLow) == ruleLow && (nodeHigh & ruleHigh) == ruleHigh, |
|||
MatchType.Any => (nodeLow & ruleLow) != 0 || (nodeHigh & ruleHigh) != 0, |
|||
MatchType.None => (nodeLow & ruleLow) == 0 && (nodeHigh & ruleHigh) == 0, |
|||
_ => true, |
|||
}; |
|||
} |
|||
|
|||
private static bool MatchesRoles(AtSpiNode node, List<int> ruleRoles, MatchType matchType) |
|||
{ |
|||
if (matchType is MatchType.Invalid or MatchType.Empty) |
|||
return matchType != MatchType.Empty || IsEmptyBitSet(ruleRoles); |
|||
|
|||
if (IsEmptyBitSet(ruleRoles)) |
|||
return true; |
|||
|
|||
var role = (uint)AtSpiNode.ToAtSpiRole(node.Peer.GetAutomationControlType(), node.Peer); |
|||
var bucket = (int)(role / 32); |
|||
var bit = (int)(role % 32); |
|||
var isSet = bucket < ruleRoles.Count && ((uint)ruleRoles[bucket] & (1u << bit)) != 0; |
|||
|
|||
return matchType switch |
|||
{ |
|||
MatchType.All or MatchType.Any => isSet, |
|||
MatchType.None => !isSet, |
|||
_ => true, |
|||
}; |
|||
} |
|||
|
|||
private static bool MatchesInterfaces(AtSpiNode node, List<string> ruleInterfaces, MatchType matchType) |
|||
{ |
|||
if (matchType is MatchType.Invalid or MatchType.Empty) |
|||
return matchType != MatchType.Empty || ruleInterfaces.Count == 0; |
|||
|
|||
if (ruleInterfaces.Count == 0) |
|||
return true; |
|||
|
|||
var nodeInterfaces = node.GetSupportedInterfaces(); |
|||
|
|||
return matchType switch |
|||
{ |
|||
MatchType.All => AllInterfacesPresent(nodeInterfaces, ruleInterfaces), |
|||
MatchType.Any => AnyInterfacePresent(nodeInterfaces, ruleInterfaces), |
|||
MatchType.None => !AnyInterfacePresent(nodeInterfaces, ruleInterfaces), |
|||
_ => true, |
|||
}; |
|||
} |
|||
|
|||
private static bool AllInterfacesPresent(HashSet<string> nodeInterfaces, List<string> required) |
|||
{ |
|||
return required.All(iface => nodeInterfaces.Contains(ResolveInterfaceName(iface))); |
|||
} |
|||
|
|||
private static bool AnyInterfacePresent(HashSet<string> nodeInterfaces, List<string> required) |
|||
{ |
|||
return required.Any(iface => nodeInterfaces.Contains(ResolveInterfaceName(iface))); |
|||
} |
|||
|
|||
private static string ResolveInterfaceName(string name) |
|||
{ |
|||
// ATs may pass short names like "Action" or full names like "org.a11y.atspi.Action"
|
|||
return name.Contains('.') ? name : $"org.a11y.atspi.{name}"; |
|||
} |
|||
|
|||
private static bool MatchesAttributes(AtSpiNode node, Dictionary<string, string>? ruleAttrs, MatchType matchType) |
|||
{ |
|||
if (matchType is MatchType.Invalid or MatchType.Empty) |
|||
return matchType != MatchType.Empty || (ruleAttrs == null || ruleAttrs.Count == 0); |
|||
|
|||
if (ruleAttrs == null || ruleAttrs.Count == 0) |
|||
return true; |
|||
|
|||
// Build node attributes (same as AccessibleHandler.GetAttributesAsync)
|
|||
var nodeAttrs = new Dictionary<string, string>(StringComparer.Ordinal) { ["toolkit"] = "Avalonia" }; |
|||
var name = node.Peer.GetName(); |
|||
if (!string.IsNullOrEmpty(name)) |
|||
nodeAttrs["explicit-name"] = "true"; |
|||
|
|||
return matchType switch |
|||
{ |
|||
MatchType.All => AllAttributesMatch(nodeAttrs, ruleAttrs), |
|||
MatchType.Any => AnyAttributeMatches(nodeAttrs, ruleAttrs), |
|||
MatchType.None => !AnyAttributeMatches(nodeAttrs, ruleAttrs), |
|||
_ => true, |
|||
}; |
|||
} |
|||
|
|||
private static bool AllAttributesMatch(Dictionary<string, string> nodeAttrs, Dictionary<string, string> required) |
|||
{ |
|||
foreach (var kv in required) |
|||
{ |
|||
if (!nodeAttrs.TryGetValue(kv.Key, out var value) || |
|||
!string.Equals(value, kv.Value, StringComparison.Ordinal)) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
private static bool AnyAttributeMatches(Dictionary<string, string> nodeAttrs, Dictionary<string, string> required) |
|||
{ |
|||
foreach (var kv in required) |
|||
{ |
|||
if (nodeAttrs.TryGetValue(kv.Key, out var value) && |
|||
string.Equals(value, kv.Value, StringComparison.Ordinal)) |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private static bool IsEmptyBitSet(List<int>? values) |
|||
{ |
|||
if (values == null || values.Count == 0) |
|||
return true; |
|||
return values.All(v => v == 0); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
using System.Threading.Tasks; |
|||
using Avalonia; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using Avalonia.Platform; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI Component interface (geometry and focus).
|
|||
/// </summary>
|
|||
internal sealed class AtSpiComponentHandler(AtSpiServer server, AtSpiNode node) : IOrgA11yAtspiComponent |
|||
{ |
|||
|
|||
public uint Version => ComponentVersion; |
|||
|
|||
public ValueTask<bool> ContainsAsync(int x, int y, uint coordType) |
|||
{ |
|||
var rect = AtSpiCoordinateHelper.GetScreenExtents(node); |
|||
var point = TranslateToScreen(x, y, coordType); |
|||
var contains = rect.ContainsExclusive(new Point(point.x, point.y)); |
|||
return ValueTask.FromResult(contains); |
|||
} |
|||
|
|||
public ValueTask<AtSpiObjectReference> GetAccessibleAtPointAsync(int x, int y, uint coordType) |
|||
{ |
|||
var rect = AtSpiCoordinateHelper.GetScreenExtents(node); |
|||
var point = TranslateToScreen(x, y, coordType); |
|||
if (rect.ContainsExclusive(new Point(point.x, point.y))) |
|||
{ |
|||
return ValueTask.FromResult(server.GetReference(node)); |
|||
} |
|||
|
|||
return ValueTask.FromResult(server.GetNullReference()); |
|||
} |
|||
|
|||
public ValueTask<AtSpiRect> GetExtentsAsync(uint coordType) |
|||
{ |
|||
var rect = AtSpiCoordinateHelper.GetScreenExtents(node); |
|||
var translated = AtSpiCoordinateHelper.TranslateRect(node, rect, coordType); |
|||
return ValueTask.FromResult(new AtSpiRect( |
|||
(int)translated.X, (int)translated.Y, |
|||
(int)translated.Width, (int)translated.Height)); |
|||
} |
|||
|
|||
public ValueTask<(int X, int Y)> GetPositionAsync(uint coordType) |
|||
{ |
|||
var rect = AtSpiCoordinateHelper.GetScreenExtents(node); |
|||
var translated = AtSpiCoordinateHelper.TranslateRect(node, rect, coordType); |
|||
return ValueTask.FromResult(((int)translated.X, (int)translated.Y)); |
|||
} |
|||
|
|||
public ValueTask<(int Width, int Height)> GetSizeAsync() |
|||
{ |
|||
var rect = AtSpiCoordinateHelper.GetScreenExtents(node); |
|||
return ValueTask.FromResult(((int)rect.Width, (int)rect.Height)); |
|||
} |
|||
|
|||
public ValueTask<uint> GetLayerAsync() |
|||
{ |
|||
var controlType = node.Peer.GetAutomationControlType(); |
|||
var layer = controlType == Automation.Peers.AutomationControlType.Window |
|||
? WindowLayer |
|||
: WidgetLayer; |
|||
return ValueTask.FromResult(layer); |
|||
} |
|||
|
|||
public ValueTask<short> GetMDIZOrderAsync() => ValueTask.FromResult((short)-1); |
|||
|
|||
public ValueTask<bool> GrabFocusAsync() |
|||
{ |
|||
node.Peer.SetFocus(); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask<double> GetAlphaAsync() => ValueTask.FromResult(1.0); |
|||
|
|||
public ValueTask<bool> SetExtentsAsync(int x, int y, int width, int height, uint coordType) |
|||
{ |
|||
// Only support moving (not resizing) for now
|
|||
return SetPositionAsync(x, y, coordType); |
|||
} |
|||
|
|||
public ValueTask<bool> SetPositionAsync(int x, int y, uint coordType) |
|||
{ |
|||
if (node.Peer.GetProvider<IRootProvider>() is not { PlatformImpl: IWindowImpl windowImpl }) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var screenPos = TranslateToScreen(x, y, coordType); |
|||
windowImpl.Move(new PixelPoint(screenPos.x, screenPos.y)); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask<bool> SetSizeAsync(int width, int height) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
public ValueTask<bool> ScrollToAsync(uint type) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
public ValueTask<bool> ScrollToPointAsync(uint coordType, int x, int y) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
private (int x, int y) TranslateToScreen(int x, int y, uint coordType) |
|||
{ |
|||
var ct = (AtSpiCoordType)coordType; |
|||
|
|||
switch (ct) |
|||
{ |
|||
case AtSpiCoordType.Screen: |
|||
return (x, y); |
|||
case AtSpiCoordType.Window: |
|||
{ |
|||
var windowRect = AtSpiCoordinateHelper.GetWindowRect(node); |
|||
return (x + (int)windowRect.X, y + (int)windowRect.Y); |
|||
} |
|||
case AtSpiCoordType.Parent: |
|||
{ |
|||
var parentRect = AtSpiCoordinateHelper.GetParentScreenRect(node); |
|||
return (x + (int)parentRect.X, y + (int)parentRect.Y); |
|||
} |
|||
default: |
|||
return (x, y); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Coordinate translation utilities between screen, window, and parent frames.
|
|||
/// </summary>
|
|||
internal static class AtSpiCoordinateHelper |
|||
{ |
|||
public static Rect GetScreenExtents(AtSpiNode node) |
|||
{ |
|||
var bounds = node.Peer.GetBoundingRectangle(); |
|||
if (node is RootAtSpiNode rootNode) |
|||
return rootNode.ToScreen(bounds); |
|||
|
|||
var root = node.Peer.GetVisualRoot(); |
|||
|
|||
if (root is null) |
|||
return bounds; |
|||
|
|||
if (node.Server.TryGetAttachedNode(root) is RootAtSpiNode rootNode2) |
|||
return rootNode2.ToScreen(bounds); |
|||
|
|||
return bounds; |
|||
} |
|||
|
|||
public static Rect TranslateRect(AtSpiNode node, Rect screenRect, uint coordType) |
|||
{ |
|||
var ct = (AtSpiCoordType)coordType; |
|||
|
|||
switch (ct) |
|||
{ |
|||
case AtSpiCoordType.Screen: |
|||
return screenRect; |
|||
case AtSpiCoordType.Window: |
|||
{ |
|||
var windowRect = GetWindowRect(node); |
|||
return new Rect( |
|||
screenRect.X - windowRect.X, |
|||
screenRect.Y - windowRect.Y, |
|||
screenRect.Width, |
|||
screenRect.Height); |
|||
} |
|||
case AtSpiCoordType.Parent: |
|||
{ |
|||
var parentRect = GetParentScreenRect(node); |
|||
return new Rect( |
|||
screenRect.X - parentRect.X, |
|||
screenRect.Y - parentRect.Y, |
|||
screenRect.Width, |
|||
screenRect.Height); |
|||
} |
|||
default: |
|||
return screenRect; |
|||
} |
|||
} |
|||
|
|||
public static Rect GetWindowRect(AtSpiNode node) |
|||
{ |
|||
var root = node.Peer.GetVisualRoot(); |
|||
if (root is null) return default; |
|||
|
|||
if (node.Server.TryGetAttachedNode(root) is RootAtSpiNode rootNode) |
|||
return rootNode.ToScreen(root.GetBoundingRectangle()); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
public static Rect GetParentScreenRect(AtSpiNode node) |
|||
{ |
|||
var parent = node.Peer.GetParent(); |
|||
if (parent is null) |
|||
return default; |
|||
var bounds = parent.GetBoundingRectangle(); |
|||
var root = parent.GetVisualRoot(); |
|||
if (root is null) |
|||
return bounds; |
|||
var rootNode = node.Server.TryGetAttachedNode(root) as RootAtSpiNode; |
|||
return rootNode?.ToScreen(bounds) ?? bounds; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI EditableText interface for writable text content.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiEditableTextHandler(AtSpiNode node) : IOrgA11yAtspiEditableText |
|||
{ |
|||
public uint Version => EditableTextVersion; |
|||
|
|||
public ValueTask<bool> SetTextContentsAsync(string newContents) |
|||
{ |
|||
if (node.Peer.GetProvider<IValueProvider>() is not { IsReadOnly: false } provider) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
provider.SetValue(newContents); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask<bool> InsertTextAsync(int position, string text, int length) |
|||
{ |
|||
if (node.Peer.GetProvider<IValueProvider>() is not { IsReadOnly: false } provider) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var current = provider.Value ?? string.Empty; |
|||
position = Math.Max(0, Math.Min(position, current.Length)); |
|||
var toInsert = length >= 0 && length < text.Length ? text.Substring(0, length) : text; |
|||
var newValue = current.Insert(position, toInsert); |
|||
provider.SetValue(newValue); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask CopyTextAsync(int startPos, int endPos) |
|||
{ |
|||
// Clipboard operations not supported via IValueProvider
|
|||
return ValueTask.CompletedTask; |
|||
} |
|||
|
|||
public ValueTask<bool> CutTextAsync(int startPos, int endPos) |
|||
{ |
|||
// Clipboard operations not supported via IValueProvider
|
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
public ValueTask<bool> DeleteTextAsync(int startPos, int endPos) |
|||
{ |
|||
if (node.Peer.GetProvider<IValueProvider>() is not { IsReadOnly: false } provider) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var current = provider.Value ?? string.Empty; |
|||
startPos = Math.Max(0, Math.Min(startPos, current.Length)); |
|||
endPos = Math.Max(startPos, Math.Min(endPos, current.Length)); |
|||
|
|||
if (startPos >= endPos) |
|||
return ValueTask.FromResult(false); |
|||
var newValue = current.Remove(startPos, endPos - startPos); |
|||
provider.SetValue(newValue); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask<bool> PasteTextAsync(int position) |
|||
{ |
|||
// Clipboard operations not supported via IValueProvider
|
|||
return ValueTask.FromResult(false); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Emits AT-SPI Event.Object signals (children-changed, state-changed, property-change).
|
|||
/// </summary>
|
|||
internal sealed class AtSpiEventObjectHandler(AtSpiServer server, string path) : IOrgA11yAtspiEventObject |
|||
{ |
|||
public uint Version => EventObjectVersion; |
|||
|
|||
public void EmitChildrenChangedSignal(string operation, int indexInParent, DBusVariant child) |
|||
{ |
|||
EmitSignal("ChildrenChanged", operation, indexInParent, 0, child, EmptyProperties()); |
|||
} |
|||
|
|||
public void EmitPropertyChangeSignal(string propertyName, DBusVariant value) |
|||
{ |
|||
EmitSignal("PropertyChange", propertyName, 0, 0, value, EmptyProperties()); |
|||
} |
|||
|
|||
public void EmitStateChangedSignal(string stateName, int detail1, DBusVariant value) |
|||
{ |
|||
EmitSignal("StateChanged", stateName, detail1, 0, value, EmptyProperties()); |
|||
} |
|||
|
|||
public void EmitSelectionChangedSignal() |
|||
{ |
|||
EmitSignal("SelectionChanged", string.Empty, 0, 0, new DBusVariant(0), EmptyProperties()); |
|||
} |
|||
|
|||
public void EmitBoundsChangedSignal() |
|||
{ |
|||
EmitSignal("BoundsChanged", string.Empty, 0, 0, new DBusVariant(0), EmptyProperties()); |
|||
} |
|||
|
|||
private void EmitSignal(string member, params object[] body) |
|||
{ |
|||
if (!server.HasEventListeners) |
|||
return; |
|||
|
|||
var connection = server.A11yConnection; |
|||
if (connection is null) |
|||
return; |
|||
|
|||
var message = DBusMessage.CreateSignal( |
|||
(DBusObjectPath)path, |
|||
IfaceEventObject, |
|||
member, |
|||
body); |
|||
|
|||
_ = connection.SendMessageAsync(message); |
|||
} |
|||
|
|||
private static Dictionary<string, DBusVariant> EmptyProperties() |
|||
{ |
|||
return new Dictionary<string, DBusVariant>(StringComparer.Ordinal); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Emits AT-SPI Event.Window signals (activate, deactivate).
|
|||
/// </summary>
|
|||
internal sealed class AtSpiEventWindowHandler(AtSpiServer server, string path) : IOrgA11yAtspiEventWindow |
|||
{ |
|||
public void EmitActivateSignal() |
|||
{ |
|||
EmitSignal("Activate", string.Empty, 0, 0, new DBusVariant("0"), EmptyProperties()); |
|||
} |
|||
|
|||
public void EmitDeactivateSignal() |
|||
{ |
|||
EmitSignal("Deactivate", string.Empty, 0, 0, new DBusVariant("0"), EmptyProperties()); |
|||
} |
|||
|
|||
private void EmitSignal(string member, params object[] body) |
|||
{ |
|||
if (!server.HasEventListeners) |
|||
return; |
|||
|
|||
var connection = server.A11yConnection; |
|||
if (connection is null) |
|||
return; |
|||
|
|||
var message = DBusMessage.CreateSignal( |
|||
(DBusObjectPath)path, |
|||
IfaceEventWindow, |
|||
member, |
|||
body); |
|||
|
|||
_ = connection.SendMessageAsync(message); |
|||
} |
|||
|
|||
private static Dictionary<string, DBusVariant> EmptyProperties() |
|||
{ |
|||
return new Dictionary<string, DBusVariant>(StringComparer.Ordinal); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
using System.Threading.Tasks; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI Image interface for image controls.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiImageHandler : IOrgA11yAtspiImage |
|||
{ |
|||
private readonly AtSpiNode _node; |
|||
|
|||
public AtSpiImageHandler(AtSpiServer server, AtSpiNode node) |
|||
{ |
|||
_ = server; |
|||
_node = node; |
|||
} |
|||
|
|||
public uint Version => ImageVersion; |
|||
|
|||
public string ImageDescription => _node.Peer.GetHelpText(); |
|||
|
|||
public string ImageLocale => ResolveLocale(); |
|||
|
|||
public ValueTask<AtSpiRect> GetImageExtentsAsync(uint coordType) |
|||
{ |
|||
var rect = AtSpiCoordinateHelper.GetScreenExtents(_node); |
|||
var translated = AtSpiCoordinateHelper.TranslateRect(_node, rect, coordType); |
|||
return ValueTask.FromResult(new AtSpiRect( |
|||
(int)translated.X, (int)translated.Y, |
|||
(int)translated.Width, (int)translated.Height)); |
|||
} |
|||
|
|||
public ValueTask<(int X, int Y)> GetImagePositionAsync(uint coordType) |
|||
{ |
|||
var rect = AtSpiCoordinateHelper.GetScreenExtents(_node); |
|||
var translated = AtSpiCoordinateHelper.TranslateRect(_node, rect, coordType); |
|||
return ValueTask.FromResult(((int)translated.X, (int)translated.Y)); |
|||
} |
|||
|
|||
public ValueTask<(int Width, int Height)> GetImageSizeAsync() |
|||
{ |
|||
var rect = AtSpiCoordinateHelper.GetScreenExtents(_node); |
|||
return ValueTask.FromResult(((int)rect.Width, (int)rect.Height)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.DBus; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI Selection interface for list-like containers.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiSelectionHandler(AtSpiServer server, AtSpiNode node) : IOrgA11yAtspiSelection |
|||
{ |
|||
public uint Version => SelectionVersion; |
|||
|
|||
public int NSelectedChildren |
|||
{ |
|||
get |
|||
{ |
|||
var provider = node.Peer.GetProvider<ISelectionProvider>(); |
|||
return provider?.GetSelection().Count ?? 0; |
|||
} |
|||
} |
|||
|
|||
public ValueTask<AtSpiObjectReference> GetSelectedChildAsync(int selectedChildIndex) |
|||
{ |
|||
node.EnsureChildren(); |
|||
|
|||
var provider = node.Peer.GetProvider<ISelectionProvider>(); |
|||
if (provider is null) |
|||
return ValueTask.FromResult(server.GetNullReference()); |
|||
|
|||
var selection = provider.GetSelection(); |
|||
if (selectedChildIndex < 0 || selectedChildIndex >= selection.Count) |
|||
return ValueTask.FromResult(server.GetNullReference()); |
|||
|
|||
var selectedPeer = selection[selectedChildIndex]; |
|||
var childNode = server.TryGetAttachedNode(selectedPeer); |
|||
if (childNode is null || !ReferenceEquals(childNode.Parent, node)) |
|||
return ValueTask.FromResult(server.GetNullReference()); |
|||
|
|||
return ValueTask.FromResult(server.GetReference(childNode)); |
|||
} |
|||
|
|||
public ValueTask<bool> SelectChildAsync(int childIndex) |
|||
{ |
|||
var children = node.Peer.GetChildren(); |
|||
if (childIndex < 0 || childIndex >= children.Count) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var childPeer = children[childIndex]; |
|||
if (childPeer.GetProvider<ISelectionItemProvider>() is not { } selectionItem) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
selectionItem.Select(); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask<bool> DeselectSelectedChildAsync(int selectedChildIndex) |
|||
{ |
|||
var provider = node.Peer.GetProvider<ISelectionProvider>(); |
|||
if (provider is null) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var selection = provider.GetSelection(); |
|||
if (selectedChildIndex < 0 || selectedChildIndex >= selection.Count) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var selectedPeer = selection[selectedChildIndex]; |
|||
if (selectedPeer.GetProvider<ISelectionItemProvider>() is not { } selectionItem) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
selectionItem.RemoveFromSelection(); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask<bool> IsChildSelectedAsync(int childIndex) |
|||
{ |
|||
var children = node.Peer.GetChildren(); |
|||
if (childIndex < 0 || childIndex >= children.Count) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var childPeer = children[childIndex]; |
|||
if (childPeer.GetProvider<ISelectionItemProvider>() is not { } selectionItem) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
return ValueTask.FromResult(selectionItem.IsSelected); |
|||
} |
|||
|
|||
public ValueTask<bool> SelectAllAsync() |
|||
{ |
|||
var provider = node.Peer.GetProvider<ISelectionProvider>(); |
|||
if (provider is null || !provider.CanSelectMultiple) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var children = node.Peer.GetChildren(); |
|||
foreach (var child in children) |
|||
{ |
|||
if (child.GetProvider<ISelectionItemProvider>() is { } selectionItem) |
|||
selectionItem.AddToSelection(); |
|||
} |
|||
|
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask<bool> ClearSelectionAsync() |
|||
{ |
|||
var provider = node.Peer.GetProvider<ISelectionProvider>(); |
|||
if (provider is null) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var selection = provider.GetSelection(); |
|||
foreach (var selectedPeer in selection) |
|||
{ |
|||
if (selectedPeer.GetProvider<ISelectionItemProvider>() is { } selectionItem) |
|||
selectionItem.RemoveFromSelection(); |
|||
} |
|||
|
|||
return ValueTask.FromResult(true); |
|||
} |
|||
|
|||
public ValueTask<bool> DeselectChildAsync(int childIndex) |
|||
{ |
|||
var children = node.Peer.GetChildren(); |
|||
if (childIndex < 0 || childIndex >= children.Count) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
var childPeer = children[childIndex]; |
|||
if (childPeer.GetProvider<ISelectionItemProvider>() is not { } selectionItem) |
|||
return ValueTask.FromResult(false); |
|||
|
|||
selectionItem.RemoveFromSelection(); |
|||
return ValueTask.FromResult(true); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,296 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.Controls.Utils; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI Text interface for read-only text content.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiTextHandler(AtSpiNode node) : IOrgA11yAtspiText |
|||
{ |
|||
private enum TextGranularity : uint |
|||
{ |
|||
Char = 0, |
|||
Word = 1, |
|||
Sentence = 2, |
|||
Line = 3, |
|||
Paragraph = 4, |
|||
} |
|||
|
|||
private enum TextBoundaryType : uint |
|||
{ |
|||
Char = 0, |
|||
WordStart = 1, |
|||
WordEnd = 2, |
|||
SentenceStart = 3, |
|||
SentenceEnd = 4, |
|||
LineStart = 5, |
|||
LineEnd = 6, |
|||
} |
|||
|
|||
public uint Version => TextVersion; |
|||
|
|||
public int CharacterCount => GetText().Length; |
|||
|
|||
public int CaretOffset => 0; |
|||
|
|||
public ValueTask<(string Text, int StartOffset, int EndOffset)> GetStringAtOffsetAsync( |
|||
int offset, uint granularity) |
|||
{ |
|||
var text = GetText(); |
|||
if (text.Length == 0) |
|||
return ValueTask.FromResult((string.Empty, 0, 0)); |
|||
|
|||
offset = Math.Max(0, Math.Min(offset, text.Length - 1)); |
|||
|
|||
var g = (TextGranularity)granularity; |
|||
|
|||
// For CHAR granularity, return single character
|
|||
if (g == TextGranularity.Char) |
|||
{ |
|||
var ch = text.Substring(offset, 1); |
|||
return ValueTask.FromResult((ch, offset, offset + 1)); |
|||
} |
|||
|
|||
// For WORD granularity, find word boundaries
|
|||
if (g == TextGranularity.Word) |
|||
{ |
|||
var start = StringUtils.PreviousWord(text, offset + 1); |
|||
if (start >= text.Length || !StringUtils.IsStartOfWord(text, start)) |
|||
return ValueTask.FromResult((string.Empty, 0, 0)); |
|||
|
|||
var end = Math.Min(StringUtils.NextWord(text, start), text.Length); |
|||
if (end <= start) |
|||
return ValueTask.FromResult((string.Empty, 0, 0)); |
|||
|
|||
return ValueTask.FromResult((text.Substring(start, end - start), start, end)); |
|||
} |
|||
|
|||
// For SENTENCE, LINE, PARAGRAPH - return full text
|
|||
return ValueTask.FromResult((text, 0, text.Length)); |
|||
} |
|||
|
|||
public ValueTask<string> GetTextAsync(int startOffset, int endOffset) |
|||
{ |
|||
var text = GetText(); |
|||
if (text.Length == 0) |
|||
return ValueTask.FromResult(string.Empty); |
|||
|
|||
startOffset = Math.Max(0, startOffset); |
|||
if (endOffset < 0 || endOffset > text.Length) |
|||
endOffset = text.Length; |
|||
|
|||
if (startOffset >= endOffset) |
|||
return ValueTask.FromResult(string.Empty); |
|||
|
|||
return ValueTask.FromResult(text.Substring(startOffset, endOffset - startOffset)); |
|||
} |
|||
|
|||
public ValueTask<bool> SetCaretOffsetAsync(int offset) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
public ValueTask<(string Text, int StartOffset, int EndOffset)> GetTextBeforeOffsetAsync( |
|||
int offset, uint type) |
|||
{ |
|||
// TODO: This method is a bit sketchy. Might need to wired up to
|
|||
// our text handling logic in core.
|
|||
|
|||
var text = GetText(); |
|||
if (offset <= 0 || text.Length == 0) |
|||
return ValueTask.FromResult((string.Empty, 0, 0)); |
|||
|
|||
offset = Math.Min(offset, text.Length); |
|||
|
|||
var bt = (TextBoundaryType)type; |
|||
|
|||
// CHAR boundary
|
|||
if (bt == TextBoundaryType.Char) |
|||
{ |
|||
var charOffset = offset - 1; |
|||
return ValueTask.FromResult((text.Substring(charOffset, 1), charOffset, charOffset + 1)); |
|||
} |
|||
|
|||
// WORD_START or WORD_END boundary
|
|||
if (bt is TextBoundaryType.WordStart or TextBoundaryType.WordEnd) |
|||
{ |
|||
var end = offset; |
|||
var start = StringUtils.PreviousWord(text, end); |
|||
if (start >= end) |
|||
start = StringUtils.PreviousWord(text, start); |
|||
|
|||
if (start < 0 || start >= text.Length || !StringUtils.IsStartOfWord(text, start)) |
|||
return ValueTask.FromResult((string.Empty, 0, 0)); |
|||
|
|||
end = Math.Min(StringUtils.NextWord(text, start), end); |
|||
if (end <= start) |
|||
return ValueTask.FromResult((string.Empty, 0, 0)); |
|||
|
|||
return ValueTask.FromResult((text.Substring(start, end - start), start, end)); |
|||
} |
|||
|
|||
// SENTENCE/LINE/PARAGRAPH - return all text before offset
|
|||
var result = text.Substring(0, offset); |
|||
return ValueTask.FromResult((result, 0, offset)); |
|||
} |
|||
|
|||
public ValueTask<(string Text, int StartOffset, int EndOffset)> GetTextAtOffsetAsync( |
|||
int offset, uint type) |
|||
{ |
|||
return GetStringAtOffsetAsync(offset, type); |
|||
} |
|||
|
|||
public ValueTask<(string Text, int StartOffset, int EndOffset)> GetTextAfterOffsetAsync( |
|||
int offset, uint type) |
|||
{ |
|||
var text = GetText(); |
|||
if (offset >= text.Length - 1 || text.Length == 0) |
|||
return ValueTask.FromResult((string.Empty, text.Length, text.Length)); |
|||
|
|||
var bt = (TextBoundaryType)type; |
|||
|
|||
// CHAR boundary
|
|||
if (bt == TextBoundaryType.Char) |
|||
{ |
|||
var charOffset = offset + 1; |
|||
if (charOffset >= text.Length) |
|||
return ValueTask.FromResult((string.Empty, text.Length, text.Length)); |
|||
return ValueTask.FromResult((text.Substring(charOffset, 1), charOffset, charOffset + 1)); |
|||
} |
|||
|
|||
// WORD_START or WORD_END boundary
|
|||
if (bt is TextBoundaryType.WordStart or TextBoundaryType.WordEnd) |
|||
{ |
|||
var start = offset + 1; |
|||
|
|||
while (start < text.Length && |
|||
StringUtils.IsEndOfWord(text, start) && |
|||
!StringUtils.IsStartOfWord(text, start)) |
|||
{ |
|||
start++; |
|||
} |
|||
|
|||
if (start >= text.Length) |
|||
return ValueTask.FromResult((string.Empty, text.Length, text.Length)); |
|||
|
|||
var end = Math.Min(StringUtils.NextWord(text, start), text.Length); |
|||
if (end <= start) |
|||
return ValueTask.FromResult((string.Empty, text.Length, text.Length)); |
|||
|
|||
return ValueTask.FromResult((text.Substring(start, end - start), start, end)); |
|||
} |
|||
|
|||
// SENTENCE/LINE/PARAGRAPH - return all text after offset
|
|||
var afterOffset = Math.Max(0, offset + 1); |
|||
var result = text.Substring(afterOffset); |
|||
return ValueTask.FromResult((result, afterOffset, text.Length)); |
|||
} |
|||
|
|||
public ValueTask<int> GetCharacterAtOffsetAsync(int offset) |
|||
{ |
|||
var text = GetText(); |
|||
if (offset < 0 || offset >= text.Length) |
|||
return ValueTask.FromResult(unchecked((int)0xFFFFFFFF)); |
|||
|
|||
return ValueTask.FromResult((int)text[offset]); |
|||
} |
|||
|
|||
public ValueTask<string> GetAttributeValueAsync(int offset, string attributeName) |
|||
{ |
|||
return ValueTask.FromResult(string.Empty); |
|||
} |
|||
|
|||
public ValueTask<(AtSpiAttributeSet Attributes, int StartOffset, int EndOffset)> GetAttributesAsync(int offset) |
|||
{ |
|||
var text = GetText(); |
|||
return ValueTask.FromResult((new AtSpiAttributeSet(), 0, text.Length)); |
|||
} |
|||
|
|||
public ValueTask<AtSpiAttributeSet> GetDefaultAttributesAsync() |
|||
{ |
|||
return ValueTask.FromResult(new AtSpiAttributeSet()); |
|||
} |
|||
|
|||
public ValueTask<(int X, int Y, int Width, int Height)> GetCharacterExtentsAsync( |
|||
int offset, uint coordType) |
|||
{ |
|||
return ValueTask.FromResult((0, 0, 0, 0)); |
|||
} |
|||
|
|||
public ValueTask<int> GetOffsetAtPointAsync(int x, int y, uint coordType) |
|||
{ |
|||
return ValueTask.FromResult(-1); |
|||
} |
|||
|
|||
public ValueTask<int> GetNSelectionsAsync() |
|||
{ |
|||
return ValueTask.FromResult(0); |
|||
} |
|||
|
|||
public ValueTask<(int StartOffset, int EndOffset)> GetSelectionAsync(int selectionNum) |
|||
{ |
|||
return ValueTask.FromResult((0, 0)); |
|||
} |
|||
|
|||
public ValueTask<bool> AddSelectionAsync(int startOffset, int endOffset) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
public ValueTask<bool> RemoveSelectionAsync(int selectionNum) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
public ValueTask<bool> SetSelectionAsync(int selectionNum, int startOffset, int endOffset) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
public ValueTask<(int X, int Y, int Width, int Height)> GetRangeExtentsAsync( |
|||
int startOffset, int endOffset, uint coordType) |
|||
{ |
|||
return ValueTask.FromResult((0, 0, 0, 0)); |
|||
} |
|||
|
|||
public ValueTask<List<AtSpiTextRange>> GetBoundedRangesAsync( |
|||
int x, int y, int width, int height, uint coordType, uint xClipType, uint yClipType) |
|||
{ |
|||
return ValueTask.FromResult(new List<AtSpiTextRange>()); |
|||
} |
|||
|
|||
public ValueTask<(AtSpiAttributeSet Attributes, int StartOffset, int EndOffset)> GetAttributeRunAsync( |
|||
int offset, bool includeDefaults) |
|||
{ |
|||
var text = GetText(); |
|||
return ValueTask.FromResult((new AtSpiAttributeSet(), 0, text.Length)); |
|||
} |
|||
|
|||
public ValueTask<AtSpiAttributeSet> GetDefaultAttributeSetAsync() |
|||
{ |
|||
return ValueTask.FromResult(new AtSpiAttributeSet()); |
|||
} |
|||
|
|||
public ValueTask<bool> ScrollSubstringToAsync(int startOffset, int endOffset, uint type) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
public ValueTask<bool> ScrollSubstringToPointAsync( |
|||
int startOffset, int endOffset, uint coordType, int x, int y) |
|||
{ |
|||
return ValueTask.FromResult(false); |
|||
} |
|||
|
|||
private string GetText() |
|||
{ |
|||
return node.Peer.GetProvider<IValueProvider>()?.Value ?? string.Empty; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.FreeDesktop.AtSpi.DBusXml; |
|||
using static Avalonia.FreeDesktop.AtSpi.AtSpiConstants; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi.Handlers |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the AT-SPI Value interface for range-based controls.
|
|||
/// </summary>
|
|||
internal sealed class AtSpiValueHandler : IOrgA11yAtspiValue |
|||
{ |
|||
private readonly AtSpiNode _node; |
|||
|
|||
public AtSpiValueHandler(AtSpiServer server, AtSpiNode node) |
|||
{ |
|||
_ = server; |
|||
_node = node; |
|||
} |
|||
|
|||
public uint Version => ValueVersion; |
|||
|
|||
public double MinimumValue => |
|||
_node.Peer.GetProvider<IRangeValueProvider>() is { } p ? p.Minimum : 0; |
|||
|
|||
public double MaximumValue => |
|||
_node.Peer.GetProvider<IRangeValueProvider>() is { } p ? p.Maximum : 0; |
|||
|
|||
public double MinimumIncrement => |
|||
_node.Peer.GetProvider<IRangeValueProvider>() is { } p ? p.SmallChange : 0; |
|||
|
|||
public string Text => string.Empty; |
|||
|
|||
public double CurrentValue |
|||
{ |
|||
get => _node.Peer.GetProvider<IRangeValueProvider>() is { } p ? p.Value : 0; |
|||
set |
|||
{ |
|||
if (_node.Peer.GetProvider<IRangeValueProvider>() is not { } p) |
|||
return; |
|||
var clamped = Math.Max(p.Minimum, Math.Min(p.Maximum, value)); |
|||
p.SetValue(clamped); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
using System; |
|||
using Avalonia.Automation.Peers; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.FreeDesktop.AtSpi |
|||
{ |
|||
/// <summary>
|
|||
/// AT-SPI node for a top-level window with coordinate translation support.
|
|||
/// </summary>
|
|||
internal sealed class RootAtSpiNode : AtSpiNode |
|||
{ |
|||
public RootAtSpiNode(AutomationPeer peer, AtSpiServer server) |
|||
: base(peer, server) |
|||
{ |
|||
RootProvider = peer.GetProvider<IRootProvider>() ?? throw new InvalidOperationException( |
|||
"Attempt to create RootAtSpiNode from peer which does not implement IRootProvider."); |
|||
RootProvider.FocusChanged += OnRootFocusChanged; |
|||
|
|||
if (WindowImpl is not { } impl) |
|||
return; |
|||
|
|||
impl.Activated += OnWindowActivated; |
|||
impl.Deactivated += OnWindowDeactivated; |
|||
} |
|||
|
|||
public IRootProvider RootProvider { get; } |
|||
public IWindowBaseImpl? WindowImpl => RootProvider.PlatformImpl as IWindowBaseImpl; |
|||
public ApplicationAtSpiNode? AppRoot { get; set; } |
|||
|
|||
public Rect ToScreen(Rect rect) |
|||
{ |
|||
if (WindowImpl is null) |
|||
return default; |
|||
return new PixelRect( |
|||
WindowImpl.PointToScreen(rect.TopLeft), |
|||
WindowImpl.PointToScreen(rect.BottomRight)) |
|||
.ToRect(1); |
|||
} |
|||
|
|||
public Point PointToClient(PixelPoint point) |
|||
{ |
|||
if (WindowImpl is null) |
|||
return default; |
|||
return WindowImpl.PointToClient(point); |
|||
} |
|||
|
|||
private void OnRootFocusChanged(object? sender, EventArgs e) |
|||
{ |
|||
var focused = RootProvider.GetFocus(); |
|||
var focusedNode = Server.TryGetAttachedNode(focused); |
|||
if (focusedNode is null) |
|||
{ |
|||
// Focus can shift before children are queried;
|
|||
// refresh visible root children lazily.
|
|||
EnsureChildren(); |
|||
focusedNode = Server.TryGetAttachedNode(focused); |
|||
} |
|||
Server.EmitFocusChange(focusedNode); |
|||
} |
|||
|
|||
private void OnWindowActivated() |
|||
{ |
|||
Server.EmitWindowActivationChange(this, true); |
|||
} |
|||
|
|||
private void OnWindowDeactivated() |
|||
{ |
|||
Server.EmitWindowActivationChange(this, false); |
|||
} |
|||
|
|||
public override void Detach() |
|||
{ |
|||
if (_detached) |
|||
return; |
|||
|
|||
RootProvider.FocusChanged -= OnRootFocusChanged; |
|||
|
|||
if (WindowImpl is { } impl) |
|||
{ |
|||
impl.Activated -= OnWindowActivated; |
|||
impl.Deactivated -= OnWindowDeactivated; |
|||
} |
|||
|
|||
base.Detach(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,173 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Automation.Peers; |
|||
using Avalonia.Controls; |
|||
using Avalonia.FreeDesktop.AtSpi; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Threading; |
|||
|
|||
namespace Avalonia.X11 |
|||
{ |
|||
internal sealed class X11AtSpiAccessibility |
|||
{ |
|||
private readonly AvaloniaX11Platform _platform; |
|||
private readonly List<X11Window> _trackedWindows = new(); |
|||
|
|||
private AtSpiAccessibilityWatcher? _watcher; |
|||
private AtSpiServer? _server; |
|||
private bool _serverStartedUnconditionally; |
|||
|
|||
internal X11AtSpiAccessibility(AvaloniaX11Platform platform) |
|||
{ |
|||
_platform = platform; |
|||
} |
|||
|
|||
internal AtSpiServer? Server => _server; |
|||
|
|||
internal void Initialize() |
|||
{ |
|||
_watcher = new AtSpiAccessibilityWatcher(); |
|||
_ = InitializeAsync(); |
|||
} |
|||
|
|||
internal void TrackWindow(X11Window window) => _trackedWindows.Add(window); |
|||
internal void UntrackWindow(X11Window window) => _trackedWindows.Remove(window); |
|||
|
|||
private async Task InitializeAsync() |
|||
{ |
|||
try |
|||
{ |
|||
await WaitForUiThreadSettleAsync(); |
|||
|
|||
// Path A: try unconditional connection first (GTK4 approach).
|
|||
// This avoids delaying startup on watcher/session-bus property calls.
|
|||
if (await TryStartServerAsync()) |
|||
{ |
|||
_serverStartedUnconditionally = true; |
|||
return; |
|||
} |
|||
|
|||
// Path A failed - fall back to watcher-driven enablement.
|
|||
await _watcher!.InitAsync(); |
|||
_watcher.IsEnabledChanged += OnAccessibilityEnabledChanged; |
|||
if (_watcher.IsEnabled) |
|||
await EnableAccessibilityAsync(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Warning, LogArea.X11Platform)? |
|||
.Log(_platform, "AT-SPI initialization failed and will be disabled: {Exception}", e); |
|||
} |
|||
} |
|||
|
|||
private async Task WaitForUiThreadSettleAsync() |
|||
{ |
|||
try |
|||
{ |
|||
// Wait until UI work is drained to context-idle so AT-SPI handlers
|
|||
// are responsive when clients start querying immediately after embed.
|
|||
var settle = Dispatcher.UIThread |
|||
.InvokeAsync(() => { }, DispatcherPriority.ContextIdle) |
|||
.GetTask(); |
|||
|
|||
// Keep startup bounded in case the UI thread never reaches idle
|
|||
// (e.g., continuous high-priority work).
|
|||
await settle.WaitAsync(TimeSpan.FromMilliseconds(100)); |
|||
} |
|||
catch (TimeoutException e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.X11Platform)? |
|||
.Log(_platform, "AT-SPI startup wait timed out before UI thread reached idle: {Exception}", e); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.X11Platform)? |
|||
.Log(_platform, "AT-SPI startup wait failed, continuing without idle settle: {Exception}", e); |
|||
} |
|||
} |
|||
|
|||
private async void OnAccessibilityEnabledChanged(object? sender, bool enabled) |
|||
{ |
|||
try |
|||
{ |
|||
if (enabled) |
|||
{ |
|||
await EnableAccessibilityAsync(); |
|||
} |
|||
else if (!_serverStartedUnconditionally) |
|||
{ |
|||
// Only tear down if server wasn't started unconditionally.
|
|||
// When started unconditionally, event listener tracking handles suppression.
|
|||
await DisableAccessibilityAsync(); |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Warning, LogArea.X11Platform)? |
|||
.Log(_platform, "AT-SPI dynamic enable/disable toggle failed: {Exception}", e); |
|||
} |
|||
} |
|||
|
|||
private async Task<bool> TryStartServerAsync() |
|||
{ |
|||
if (_server is not null) |
|||
return true; |
|||
|
|||
try |
|||
{ |
|||
var server = new AtSpiServer(); |
|||
await server.StartAsync(); |
|||
_server = server; |
|||
|
|||
// Register any already-tracked windows.
|
|||
foreach (var window in _trackedWindows) |
|||
{ |
|||
var peer = TryGetWindowPeer(window); |
|||
if (peer is not null) |
|||
server.AddWindow(peer); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Warning, LogArea.X11Platform)? |
|||
.Log(_platform, "AT-SPI server startup attempt failed: {Exception}", e); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private static AutomationPeer? TryGetWindowPeer(X11Window window) |
|||
{ |
|||
try |
|||
{ |
|||
if (window.InputRoot.RootElement is Control control) |
|||
return ControlAutomationPeer.CreatePeerForElement(control); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
// Window can be tracked before input root is available.
|
|||
Logger.TryGet(LogEventLevel.Debug, LogArea.X11Platform)? |
|||
.Log(window, "AT-SPI could not resolve window automation peer yet: {Exception}", e); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
private async Task EnableAccessibilityAsync() |
|||
{ |
|||
await TryStartServerAsync(); |
|||
} |
|||
|
|||
private async Task DisableAccessibilityAsync() |
|||
{ |
|||
if (_server is null) |
|||
return; |
|||
|
|||
var server = _server; |
|||
_server = null; |
|||
await server.DisposeAsync(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<LangVersion>latest</LangVersion> |
|||
<Nullable>enable</Nullable> |
|||
<IncludeBuildOutput>false</IncludeBuildOutput> |
|||
<DebugType>embedded</DebugType> |
|||
<IncludeSymbols>false</IncludeSymbols> |
|||
<EnableNETAnalyzers>false</EnableNETAnalyzers> |
|||
<AvDBusSourceGenPath>../../../external/Avalonia.DBus/src/Avalonia.DBus.SourceGen</AvDBusSourceGenPath> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Compile Include="$(AvDBusSourceGenPath)/**/*.cs" |
|||
Exclude="$(AvDBusSourceGenPath)/obj/**/*.cs" |
|||
LinkBase="Avalonia.DBus.SourceGen" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Include="$(OutputPath)/$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="../../../build/AnalyzerProject.targets" /> |
|||
</Project> |
|||
@ -0,0 +1,6 @@ |
|||
<Project> |
|||
<ItemGroup> |
|||
<CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="DBusGeneratorMode" /> |
|||
<CompilerVisibleProperty Include="AvDBusInternal" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
Loading…
Reference in new issue