Design
Components
Screen readers are complex applications that need to interact with many different systems and technologies, and Odilia is no different. For this reason, the project is split into several crates:
Odilia – The main application
This is the toplevel binary crate, which glues all the smaller crates together. It also handles logging and other application-wide tasks.
atspi – Accessibility Interface
This crate communicates with the Assistive Technology Service Provider Interface, which allows Odilia to access and present the content in applications and the desktop environment.
odilia-cache – A Cache for Accessibile Items on the Desktop
This subsystem handles a cache that makes Odilia very fast. If you want to help in the performance department of Odilia, this is what to look at. Help us make the hashmap easier to index, the references faster to dereference, and the cache items easier to copy.
odilia-common – Common Algorithms, Data Types and Structures
These are miscellaneous pieces of code shared by other Odilia crates. Among them are the code necessary to parse and work with keybindings, types used in structural navigation, and types used to read configuration files.
odilia-input – Input Methods and Events
This crate is responsible for handling input events from devices such as keyboards, mice, and touch screens. It doesn’t implement any input methods, it only allows input methods to be configured.
odilia-tts – Text to Speech
This handles talking to speech-dispatcher for text to speech synthesis, as well as formatting messages to be spoken.
Dependencies and Technologies
Tokio – Asynchronous Runtime
On Unix-like operating systems, IPC is usually handled using Unix domain sockets (UDS). These sockets act very similar to networking sockets: they’re an asynchronous method of communication, where data could arrive at any time.
For this reason, we’ve chosen an asynchronous architecture, which can react to events as they arrive, and can parallelise the sending and receiving of data through cooperative multitasking.
Odilia uses the Tokio runtime to achieve this. A good knowledge of Tokio is one of the fundamental building blocks to understanding, and contributing to, the Odilia codebase, so go take a look at the Tokio documentation.
Tokio integrates with Rust’s built-in async / await syntax and Futures, so if you’re unfamiliar with them, the Rust Async Book is a good place to start.
DBus – Inter-Process Communication
Accessibility naturally requires inter-process communication (IPC), so that assistive technologies like Odilia can get information from, and send events to, the applications they’re presenting to the user. On the Linux desktop, this is handled by DBus.
We use the zbus crate (book) to talk over DBus. It is asynchronous by default, and integrates well with the Tokio async runtime.
Most of the atspi crate was generated using zbus_xmlgen from the at-spi DBus interface XML definitions. Note, however, that zbus_xmlgen is, at present, meant to be run once, and the generated bindings are to be manually tweaked and kept up to date, in order to keep them idiomatic.