Skip to content

Commit 8d52a5f

Browse files
authored
feat: Support live regions on Unix (#299)
1 parent 05febcb commit 8d52a5f

File tree

4 files changed

+72
-11
lines changed

4 files changed

+72
-11
lines changed

platforms/unix/src/adapter.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
use accesskit::{ActionHandler, NodeId, Rect, Role, TreeUpdate};
2020
use accesskit_consumer::{DetachedNode, FilterResult, Node, Tree, TreeChangeHandler, TreeState};
2121
use async_channel::{Receiver, Sender};
22-
use atspi::{Interface, InterfaceSet, State};
22+
use atspi::{Interface, InterfaceSet, Live, State};
2323
use futures_lite::StreamExt;
2424
use std::sync::{
2525
atomic::{AtomicUsize, Ordering},
@@ -52,6 +52,22 @@ impl AdapterChangeHandler<'_> {
5252
.unwrap();
5353
self.adapter.window_created(adapter_index, node.id());
5454
}
55+
56+
let live = node.live();
57+
if live != Live::None {
58+
if let Some(name) = node.name() {
59+
self.adapter
60+
.events
61+
.send_blocking(Event::Object {
62+
target: ObjectId::Node {
63+
adapter: self.adapter.id,
64+
node: node.id(),
65+
},
66+
event: ObjectEvent::Announcement(name, live),
67+
})
68+
.unwrap();
69+
}
70+
}
5571
}
5672

5773
fn remove_node(&mut self, node: &DetachedNode) {
@@ -409,7 +425,7 @@ impl Adapter {
409425
adapter: self.id,
410426
node: window.id(),
411427
},
412-
name: window.name(),
428+
name: window.name().unwrap_or_default(),
413429
event: WindowEvent::Activated,
414430
})
415431
.unwrap();
@@ -440,7 +456,7 @@ impl Adapter {
440456
adapter: self.id,
441457
node: window.id(),
442458
},
443-
name: window.name(),
459+
name: window.name().unwrap_or_default(),
444460
event: WindowEvent::Deactivated,
445461
})
446462
.unwrap();

platforms/unix/src/atspi/bus.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ impl Bus {
101101
let interface = "org.a11y.atspi.Event.Object";
102102
let signal = match event {
103103
ObjectEvent::ActiveDescendantChanged(_) => "ActiveDescendantChanged",
104+
ObjectEvent::Announcement(_, _) => "Announcement",
104105
ObjectEvent::BoundsChanged(_) => "BoundsChanged",
105106
ObjectEvent::ChildAdded(_, _) | ObjectEvent::ChildRemoved(_) => "ChildrenChanged",
106107
ObjectEvent::PropertyChanged(_) => "PropertyChange",
@@ -123,6 +124,21 @@ impl Bus {
123124
)
124125
.await
125126
}
127+
ObjectEvent::Announcement(message, politeness) => {
128+
self.emit_event(
129+
target,
130+
interface,
131+
signal,
132+
EventBody {
133+
kind: "",
134+
detail1: politeness as i32,
135+
detail2: 0,
136+
any_data: message.into(),
137+
properties,
138+
},
139+
)
140+
.await
141+
}
126142
ObjectEvent::BoundsChanged(bounds) => {
127143
self.emit_event(
128144
target,

platforms/unix/src/atspi/interfaces/events.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// the LICENSE-MIT file), at your option.
55

66
use crate::atspi::{ObjectId, Rect};
7-
use atspi::{Role, State};
7+
use atspi::{Live, Role, State};
88

99
pub(crate) enum Event {
1010
Object {
@@ -29,6 +29,7 @@ pub(crate) enum Property {
2929
#[allow(clippy::enum_variant_names)]
3030
pub(crate) enum ObjectEvent {
3131
ActiveDescendantChanged(ObjectId),
32+
Announcement(String, Live),
3233
BoundsChanged(Rect),
3334
ChildAdded(usize, ObjectId),
3435
ChildRemoved(ObjectId),

platforms/unix/src/node.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ use crate::{
1818
util::WindowBounds,
1919
};
2020
use accesskit::{
21-
Action, ActionData, ActionRequest, Affine, Checked, DefaultActionVerb, NodeId, Point, Rect,
22-
Role,
21+
Action, ActionData, ActionRequest, Affine, Checked, DefaultActionVerb, Live, NodeId, Point,
22+
Rect, Role,
2323
};
2424
use accesskit_consumer::{DetachedNode, FilterResult, Node, NodeState, TreeState};
2525
use async_channel::Sender;
26-
use atspi::{CoordType, Interface, InterfaceSet, Layer, Role as AtspiRole, State, StateSet};
26+
use atspi::{
27+
CoordType, Interface, InterfaceSet, Layer, Live as AtspiLive, Role as AtspiRole, State,
28+
StateSet,
29+
};
2730
use std::{
2831
iter::FusedIterator,
2932
sync::{Arc, RwLock, RwLockReadGuard, Weak},
@@ -56,12 +59,11 @@ impl<'a> NodeWrapper<'a> {
5659
}
5760
}
5861

59-
pub fn name(&self) -> String {
62+
pub fn name(&self) -> Option<String> {
6063
match self {
6164
Self::Node { node, .. } => node.name(),
6265
Self::DetachedNode { node, .. } => node.name(),
6366
}
64-
.unwrap_or_default()
6567
}
6668

6769
pub fn description(&self) -> String {
@@ -412,6 +414,18 @@ impl<'a> NodeWrapper<'a> {
412414
interfaces
413415
}
414416

417+
pub(crate) fn live(&self) -> AtspiLive {
418+
let live = match self {
419+
Self::Node { node, .. } => node.live(),
420+
Self::DetachedNode { node, .. } => node.live(),
421+
};
422+
match live {
423+
Live::Off => AtspiLive::None,
424+
Live::Polite => AtspiLive::Polite,
425+
Live::Assertive => AtspiLive::Assertive,
426+
}
427+
}
428+
415429
fn n_actions(&self) -> i32 {
416430
match self.node_state().default_action_verb() {
417431
Some(_) => 1,
@@ -502,15 +516,29 @@ impl<'a> NodeWrapper<'a> {
502516
let adapter = self.adapter();
503517
let name = self.name();
504518
if name != old.name() {
519+
let name = name.unwrap_or_default();
505520
events
506521
.send_blocking(Event::Object {
507522
target: ObjectId::Node {
508523
adapter,
509524
node: self.id(),
510525
},
511-
event: ObjectEvent::PropertyChanged(Property::Name(name)),
526+
event: ObjectEvent::PropertyChanged(Property::Name(name.clone())),
512527
})
513528
.unwrap();
529+
530+
let live = self.live();
531+
if live != AtspiLive::None {
532+
events
533+
.send_blocking(Event::Object {
534+
target: ObjectId::Node {
535+
adapter,
536+
node: self.id(),
537+
},
538+
event: ObjectEvent::Announcement(name, live),
539+
})
540+
.unwrap();
541+
}
514542
}
515543
let description = self.description();
516544
if description != old.description() {
@@ -707,7 +735,7 @@ impl PlatformNode {
707735
pub fn name(&self) -> fdo::Result<String> {
708736
self.resolve(|node| {
709737
let wrapper = self.node_wrapper(&node);
710-
Ok(wrapper.name())
738+
Ok(wrapper.name().unwrap_or_default())
711739
})
712740
}
713741

0 commit comments

Comments
 (0)