chore(accessibility): use settings subscription for color filter
This commit is contained in:
parent
cc2e4f77a0
commit
be6b799b51
5 changed files with 13 additions and 250 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -1792,19 +1792,23 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-settings-subscriptions"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/cosmic-settings-subscriptions#650f0bc1dbfdce2e541c104674257d1621b2de4c"
|
||||
source = "git+https://github.com/pop-os/cosmic-settings-subscriptions#6c36b82d5f7264f3b06ca83e1b2d01c9f979d104"
|
||||
dependencies = [
|
||||
"bluez-zbus",
|
||||
"cosmic-dbus-a11y",
|
||||
"cosmic-dbus-networkmanager",
|
||||
"cosmic-protocols",
|
||||
"futures",
|
||||
"iced_futures",
|
||||
"itertools 0.14.0",
|
||||
"libpulse-binding",
|
||||
"log",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pipewire",
|
||||
"rustix 1.0.5",
|
||||
"secure-string",
|
||||
"smithay-client-toolkit",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ pwhash = "1"
|
|||
[dependencies.cosmic-settings-subscriptions]
|
||||
git = "https://github.com/pop-os/cosmic-settings-subscriptions"
|
||||
#TODO: only select features as needed
|
||||
features = ["accessibility", "network_manager", "pipewire", "pulse", "bluetooth"]
|
||||
features = ["cosmic_a11y_manager", "accessibility", "network_manager", "pipewire", "pulse", "bluetooth"]
|
||||
optional = true
|
||||
|
||||
[dependencies.icu]
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use cosmic_settings_page::{
|
|||
use slotmap::SlotMap;
|
||||
use tracing::error;
|
||||
|
||||
use super::{AccessibilityEvent, AccessibilityRequest, wayland};
|
||||
use super::{AccessibilityEvent, AccessibilityRequest, cosmic_a11y_manager as wayland};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Page {
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ use cosmic_settings_page::{
|
|||
section::{self, Section},
|
||||
};
|
||||
use cosmic_settings_subscriptions::accessibility::{self, DBusRequest, DBusUpdate};
|
||||
use cosmic_settings_subscriptions::cosmic_a11y_manager;
|
||||
use num_traits::FromPrimitive;
|
||||
use slotmap::SlotMap;
|
||||
|
||||
pub mod magnifier;
|
||||
mod wayland;
|
||||
pub use cosmic_a11y_manager::{AccessibilityEvent, AccessibilityRequest, ColorFilter};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
pub use wayland::{AccessibilityEvent, AccessibilityRequest, ColorFilter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Page {
|
||||
|
|
@ -32,7 +32,7 @@ pub struct Page {
|
|||
screen_filter_selections: Vec<String>,
|
||||
|
||||
wayland_available: Option<u32>,
|
||||
wayland_thread: Option<wayland::Sender>,
|
||||
wayland_thread: Option<cosmic_a11y_manager::Sender>,
|
||||
theme: Box<cosmic::cosmic_theme::Theme>,
|
||||
high_contrast: Option<bool>,
|
||||
daemon_config: CosmicSettingsDaemonConfig,
|
||||
|
|
@ -74,7 +74,7 @@ impl Default for Page {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
Event(wayland::AccessibilityEvent),
|
||||
Event(cosmic_a11y_manager::AccessibilityEvent),
|
||||
ProtocolUnavailable,
|
||||
Return,
|
||||
HighContrast(bool),
|
||||
|
|
@ -110,7 +110,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
|
||||
fn on_enter(&mut self) -> cosmic::Task<crate::pages::Message> {
|
||||
if self.wayland_thread.is_none() {
|
||||
match wayland::spawn_wayland_connection() {
|
||||
match cosmic_a11y_manager::spawn_wayland_connection() {
|
||||
Ok((tx, mut rx)) => {
|
||||
self.wayland_thread = Some(tx);
|
||||
|
||||
|
|
@ -385,7 +385,7 @@ impl Page {
|
|||
if let Some(sender) = self.wayland_thread.as_ref() {
|
||||
let _ = sender.send(AccessibilityRequest::ScreenFilter {
|
||||
inverted,
|
||||
filter: Some(wayland::ColorFilter::Unknown),
|
||||
filter: Some(cosmic_a11y_manager::ColorFilter::Unknown),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,241 +0,0 @@
|
|||
use cosmic_protocols::a11y::v1::client::cosmic_a11y_manager_v1;
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use sctk::{
|
||||
reexports::{
|
||||
calloop::{self, LoopSignal, channel},
|
||||
calloop_wayland_source::WaylandSource,
|
||||
client::{
|
||||
ConnectError, Connection, Dispatch, Proxy, WEnum,
|
||||
globals::{GlobalListContents, registry_queue_init},
|
||||
protocol::wl_registry,
|
||||
},
|
||||
},
|
||||
registry::RegistryState,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AccessibilityEvent {
|
||||
Bound(u32),
|
||||
Magnifier(bool),
|
||||
ScreenFilter {
|
||||
inverted: bool,
|
||||
filter: Option<ColorFilter>,
|
||||
},
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
pub enum ColorFilter {
|
||||
Greyscale,
|
||||
Deuteranopia,
|
||||
Protanopia,
|
||||
Tritanopia,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Default for ColorFilter {
|
||||
fn default() -> Self {
|
||||
ColorFilter::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AccessibilityRequest {
|
||||
Magnifier(bool),
|
||||
ScreenFilter {
|
||||
inverted: bool,
|
||||
filter: Option<ColorFilter>,
|
||||
},
|
||||
}
|
||||
|
||||
pub type Sender = calloop::channel::Sender<AccessibilityRequest>;
|
||||
|
||||
pub fn spawn_wayland_connection() -> Result<
|
||||
(
|
||||
channel::Sender<AccessibilityRequest>,
|
||||
mpsc::Receiver<AccessibilityEvent>,
|
||||
),
|
||||
ConnectError,
|
||||
> {
|
||||
let (event_tx, event_rx) = mpsc::channel(10);
|
||||
let (request_tx, request_rx) = channel::channel();
|
||||
let conn = Connection::connect_to_env()?;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if let Err(err) = wayland_thread(conn, event_tx.clone(), request_rx) {
|
||||
tracing::warn!("Accessibility protocol wayland thread crashed: {}", err);
|
||||
let _ = event_tx.blocking_send(AccessibilityEvent::Closed);
|
||||
}
|
||||
});
|
||||
|
||||
Ok((request_tx, event_rx))
|
||||
}
|
||||
|
||||
fn wayland_thread(
|
||||
conn: Connection,
|
||||
tx: mpsc::Sender<AccessibilityEvent>,
|
||||
rx: channel::Channel<AccessibilityRequest>,
|
||||
) -> anyhow::Result<()> {
|
||||
struct State {
|
||||
loop_signal: LoopSignal,
|
||||
tx: mpsc::Sender<AccessibilityEvent>,
|
||||
global: cosmic_a11y_manager_v1::CosmicA11yManagerV1,
|
||||
|
||||
magnifier: bool,
|
||||
screen_inverted: bool,
|
||||
screen_filter: Option<ColorFilter>,
|
||||
}
|
||||
|
||||
impl Dispatch<cosmic_a11y_manager_v1::CosmicA11yManagerV1, ()> for State {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_proxy: &cosmic_a11y_manager_v1::CosmicA11yManagerV1,
|
||||
event: <cosmic_a11y_manager_v1::CosmicA11yManagerV1 as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
cosmic_a11y_manager_v1::Event::Magnifier { active } => {
|
||||
let magnifier = active
|
||||
.into_result()
|
||||
.unwrap_or(cosmic_a11y_manager_v1::ActiveState::Disabled)
|
||||
== cosmic_a11y_manager_v1::ActiveState::Enabled;
|
||||
if magnifier != state.magnifier {
|
||||
if state
|
||||
.tx
|
||||
.blocking_send(AccessibilityEvent::Magnifier(magnifier))
|
||||
.is_err()
|
||||
{
|
||||
state.loop_signal.stop();
|
||||
state.loop_signal.wakeup();
|
||||
};
|
||||
state.magnifier = magnifier;
|
||||
}
|
||||
}
|
||||
cosmic_a11y_manager_v1::Event::ScreenFilter { inverted, filter } => {
|
||||
let inverted = inverted
|
||||
.into_result()
|
||||
.unwrap_or(cosmic_a11y_manager_v1::ActiveState::Disabled)
|
||||
== cosmic_a11y_manager_v1::ActiveState::Enabled;
|
||||
let filter = match filter {
|
||||
WEnum::Value(cosmic_a11y_manager_v1::Filter::Disabled) => None,
|
||||
WEnum::Value(cosmic_a11y_manager_v1::Filter::Greyscale) => {
|
||||
Some(ColorFilter::Greyscale)
|
||||
}
|
||||
WEnum::Value(cosmic_a11y_manager_v1::Filter::DaltonizeProtanopia) => {
|
||||
Some(ColorFilter::Protanopia)
|
||||
}
|
||||
WEnum::Value(cosmic_a11y_manager_v1::Filter::DaltonizeDeuteranopia) => {
|
||||
Some(ColorFilter::Deuteranopia)
|
||||
}
|
||||
WEnum::Value(cosmic_a11y_manager_v1::Filter::DaltonizeTritanopia) => {
|
||||
Some(ColorFilter::Tritanopia)
|
||||
}
|
||||
WEnum::Value(_) | WEnum::Unknown(_) => Some(ColorFilter::Unknown),
|
||||
};
|
||||
|
||||
if inverted != state.screen_inverted || filter != state.screen_filter {
|
||||
if state
|
||||
.tx
|
||||
.blocking_send(AccessibilityEvent::ScreenFilter { inverted, filter })
|
||||
.is_err()
|
||||
{
|
||||
state.loop_signal.stop();
|
||||
state.loop_signal.wakeup();
|
||||
};
|
||||
state.screen_inverted = inverted;
|
||||
state.screen_filter = filter;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &wl_registry::WlRegistry,
|
||||
_event: <wl_registry::WlRegistry as Proxy>::Event,
|
||||
_data: &GlobalListContents,
|
||||
_conn: &Connection,
|
||||
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
||||
) {
|
||||
// We don't care about any dynamic globals
|
||||
}
|
||||
}
|
||||
|
||||
let mut event_loop = calloop::EventLoop::<State>::try_new().unwrap();
|
||||
|
||||
let loop_handle = event_loop.handle();
|
||||
let (globals, event_queue) = registry_queue_init(&conn).unwrap();
|
||||
let qhandle = event_queue.handle();
|
||||
|
||||
WaylandSource::new(conn, event_queue)
|
||||
.insert(loop_handle.clone())
|
||||
.map_err(|err| err.error)?;
|
||||
|
||||
let registry_state = RegistryState::new(&globals);
|
||||
let Ok(global) = registry_state.bind_one::<cosmic_a11y_manager_v1::CosmicA11yManagerV1, _, _>(
|
||||
&qhandle,
|
||||
1..=2,
|
||||
(),
|
||||
) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let _ = tx.blocking_send(AccessibilityEvent::Bound(global.version()));
|
||||
|
||||
loop_handle
|
||||
.insert_source(rx, |request, _, state| match request {
|
||||
channel::Event::Msg(AccessibilityRequest::Magnifier(val)) => {
|
||||
state.global.set_magnifier(if val {
|
||||
cosmic_a11y_manager_v1::ActiveState::Enabled
|
||||
} else {
|
||||
cosmic_a11y_manager_v1::ActiveState::Disabled
|
||||
});
|
||||
}
|
||||
channel::Event::Msg(AccessibilityRequest::ScreenFilter { inverted, filter }) => {
|
||||
state.global.set_screen_filter(
|
||||
if inverted {
|
||||
cosmic_a11y_manager_v1::ActiveState::Enabled
|
||||
} else {
|
||||
cosmic_a11y_manager_v1::ActiveState::Disabled
|
||||
},
|
||||
match filter {
|
||||
None => cosmic_a11y_manager_v1::Filter::Disabled,
|
||||
Some(ColorFilter::Greyscale) => cosmic_a11y_manager_v1::Filter::Greyscale,
|
||||
Some(ColorFilter::Protanopia) => {
|
||||
cosmic_a11y_manager_v1::Filter::DaltonizeProtanopia
|
||||
}
|
||||
Some(ColorFilter::Deuteranopia) => {
|
||||
cosmic_a11y_manager_v1::Filter::DaltonizeDeuteranopia
|
||||
}
|
||||
Some(ColorFilter::Tritanopia) => {
|
||||
cosmic_a11y_manager_v1::Filter::DaltonizeTritanopia
|
||||
}
|
||||
Some(ColorFilter::Unknown) => cosmic_a11y_manager_v1::Filter::Unknown,
|
||||
},
|
||||
);
|
||||
}
|
||||
channel::Event::Closed => {
|
||||
state.loop_signal.stop();
|
||||
state.loop_signal.wakeup();
|
||||
}
|
||||
})
|
||||
.map_err(|err| err.error)?;
|
||||
|
||||
let mut state = State {
|
||||
loop_signal: event_loop.get_signal(),
|
||||
tx,
|
||||
global,
|
||||
|
||||
magnifier: false,
|
||||
screen_inverted: false,
|
||||
screen_filter: None,
|
||||
};
|
||||
|
||||
event_loop.run(None, &mut state, |_| {})?;
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue