1
0
Fork 0
forked from wry/wry

config: add documentation

This commit is contained in:
Julian Orth 2022-05-16 18:21:56 +02:00
parent 6916f03e94
commit fe80440f38
36 changed files with 620 additions and 199 deletions

View file

@ -7,14 +7,17 @@ use {
ipc::{ClientMessage, InitMessage, Response, ServerMessage},
logging, Config, ConfigEntry, ConfigEntryGen, VERSION,
},
drm::{
exec::Command,
input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat},
keyboard::Keymap,
logging::LogLevel,
theme::{colors::Colorable, sized::Resizable, Color},
timer::Timer,
video::{
connector_type::{ConnectorType, CON_UNKNOWN},
Connector, DrmDevice, Mode,
},
input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat},
keyboard::keymap::Keymap,
theme::{colors::Colorable, sized::Resizable, Color},
Axis, Command, Direction, LogLevel, ModifiedKeySym, PciId, Timer, Workspace,
Axis, Direction, ModifiedKeySym, PciId, Workspace,
},
std::{
cell::{Cell, RefCell},

View file

@ -1,10 +1,12 @@
use {
crate::{
drm::{connector_type::ConnectorType, Connector, DrmDevice},
input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat},
keyboard::{keymap::Keymap, mods::Modifiers, syms::KeySym},
keyboard::{mods::Modifiers, syms::KeySym, Keymap},
logging::LogLevel,
theme::{colors::Colorable, sized::Resizable, Color},
Axis, Direction, LogLevel, PciId, Timer, Workspace,
timer::Timer,
video::{connector_type::ConnectorType, Connector, DrmDevice},
Axis, Direction, PciId, Workspace,
},
bincode::{BorrowDecode, Decode, Encode},
std::time::Duration,

View file

@ -1,5 +1,5 @@
use {
crate::LogLevel,
crate::logging::LogLevel,
log::{Level, LevelFilter, Log, Metadata, Record},
};

View file

@ -1,5 +1,11 @@
//! Tools to configure the compositor in embedded environments.
use crate::input::InputDevice;
/// Grab the input device.
///
/// This usually only works if the compositor is running as an application under X. It will
/// probably not work under XWayland.
pub fn grab_input_device(kb: InputDevice, grab: bool) {
get!().grab(kb, grab);
}

50
jay-config/src/exec.rs Normal file
View file

@ -0,0 +1,50 @@
//! Tools for spawning programs.
use std::collections::HashMap;
/// Sets an environment variable.
///
/// This does not affect the compositor itself but only programs spawned by the compositor.
pub fn set_env(key: &str, val: &str) {
get!().set_env(key, val);
}
/// A command to be spawned.
pub struct Command {
pub(crate) prog: String,
pub(crate) args: Vec<String>,
pub(crate) env: HashMap<String, String>,
}
impl Command {
/// Creates a new command to be spawned.
///
/// `prog` should be the path to the program being spawned. If `prog` does not contain
/// a `/`, then it will be searched in `PATH` similar to how a shell would do it.
///
/// The first argument passed to `prog`, `argv[0]`, is `prog` itself.
pub fn new(prog: &str) -> Self {
Self {
prog: prog.to_string(),
args: vec![],
env: Default::default(),
}
}
/// Adds an argument to be passed to the command.
pub fn arg(&mut self, arg: &str) -> &mut Self {
self.args.push(arg.to_string());
self
}
/// Sets an environment variable for this command only.
pub fn env(&mut self, key: &str, val: &str) -> &mut Self {
self.env.insert(key.to_string(), val.to_string());
self
}
/// Executes the command.
pub fn spawn(&self) {
get!().spawn(self);
}
}

View file

@ -1,48 +1,90 @@
//! Tools for configuring input devices.
pub mod acceleration;
pub mod capability;
use {
crate::{
input::{acceleration::AccelProfile, capability::Capability},
Axis, Direction, Keymap, ModifiedKeySym, Workspace,
keyboard::Keymap,
Axis, Direction, ModifiedKeySym, Workspace,
},
bincode::{Decode, Encode},
};
/// An input device.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct InputDevice(pub u64);
impl InputDevice {
/// Assigns the input device to a seat.
pub fn set_seat(self, seat: Seat) {
get!().set_seat(self, seat)
}
/// Returns whether the device has the specified capability.
pub fn has_capability(self, cap: Capability) -> bool {
get!(false).has_capability(self, cap)
}
/// Sets the device to be left handed.
///
/// This has the effect of swapping the left and right mouse button. See the libinput
/// documentation for more details.
pub fn set_left_handed(self, left_handed: bool) {
get!().set_left_handed(self, left_handed);
}
/// Sets the acceleration profile of the device.
///
/// This corresponds to the libinput setting of the same name.
pub fn set_accel_profile(self, profile: AccelProfile) {
get!().set_accel_profile(self, profile);
}
/// Sets the acceleration speed of the device.
///
/// This corresponds to the libinput setting of the same name.
pub fn set_accel_speed(self, speed: f64) {
get!().set_accel_speed(self, speed);
}
/// Sets the transformation matrix of the device.
///
/// This is not a libinput setting but a setting of the compositor. It currently affects
/// relative mouse motions in that the matrix is applied to the motion. To reduce the mouse
/// speed to 35%, use the following matrix:
///
/// ```text
/// [
/// [0.35, 1.0],
/// [1.0, 0.35],
/// ]
/// ```
///
/// This might give you better results than using `set_accel_profile` and `set_accel_speed`.
pub fn set_transform_matrix(self, matrix: [[f64; 2]; 2]) {
get!().set_transform_matrix(self, matrix);
}
/// Returns the name of the device.
pub fn name(self) -> String {
get!(String::new()).device_name(self)
}
}
/// A seat.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Seat(pub u64);
impl Seat {
pub const INVALID: Self = Self(0);
/// Returns whether the seat is invalid.
pub fn is_invalid(self) -> bool {
self == Self::INVALID
}
#[doc(hidden)]
pub fn raw(self) -> u64 {
self.0
@ -53,143 +95,164 @@ impl Seat {
Self(raw)
}
/// Creates a compositor-wide hotkey.
///
/// The closure is invoked when the user presses the last key of the modified keysym.
/// Note that the keysym is calculated without modifiers applied. To perform an action
/// when `SHIFT+k` is pressed, use `SHIFT | SYM_k` not `SHIFT | SYM_K`.
pub fn bind<T: Into<ModifiedKeySym>, F: Fn() + 'static>(self, mod_sym: T, f: F) {
get!().bind(self, mod_sym, f)
}
/// Unbinds a hotkey.
pub fn unbind<T: Into<ModifiedKeySym>>(self, mod_sym: T) {
get!().unbind(self, mod_sym)
}
/// Moves the keyboard focus of the seat in the specified direction.
pub fn focus(self, direction: Direction) {
get!().focus(self, direction)
}
/// Moves the focused window in the specified direction.
pub fn move_(self, direction: Direction) {
get!().move_(self, direction)
}
/// Sets the keymap of the seat.
pub fn set_keymap(self, keymap: Keymap) {
get!().seat_set_keymap(self, keymap)
}
/// Returns the repeat rate of the seat.
///
/// The returned tuple is `(rate, delay)` where `rate` is the number of times keys repeat per second
/// and `delay` is the time after the button press after which keys start repeating.
pub fn repeat_rate(self) -> (i32, i32) {
let mut res = (25, 250);
(|| res = get!().seat_get_repeat_rate(self))();
res
get!((25, 250)).seat_get_repeat_rate(self)
}
/// Sets the repeat rate of the seat.
pub fn set_repeat_rate(self, rate: i32, delay: i32) {
get!().seat_set_repeat_rate(self, rate, delay)
}
/// Returns whether the parent-container of the currently focused window is in mono-mode.
pub fn mono(self) -> bool {
get!(false).mono(self)
}
/// Sets whether the parent-container of the currently focused window is in mono-mode.
pub fn set_mono(self, mono: bool) {
get!().set_mono(self, mono)
}
/// Toggles whether the parent-container of the currently focused window is in mono-mode.
pub fn toggle_mono(self) {
self.set_mono(!self.mono());
}
/// Returns the split axis of the parent-container of the currently focused window.
pub fn split(self) -> Axis {
get!(Axis::Horizontal).split(self)
}
/// Sets the split axis of the parent-container of the currently focused window.
pub fn set_split(self, axis: Axis) {
get!().set_split(self, axis)
}
/// Toggles the split axis of the parent-container of the currently focused window.
pub fn toggle_split(self) {
self.set_split(self.split().other());
}
/// Returns the input devices assigned to this seat.
pub fn input_devices(self) -> Vec<InputDevice> {
get!().get_input_devices(Some(self))
}
/// Creates a new container with the specified split in place of the currently focused window.
pub fn create_split(self, axis: Axis) {
get!().create_split(self, axis);
}
/// Focuses the parent node of the currently focused window.
pub fn focus_parent(self) {
get!().focus_parent(self);
}
/// Requests the currently focused window to be closed.
pub fn close(self) {
get!().close(self);
}
/// Returns whether the currently focused window is floating.
pub fn get_floating(self) -> bool {
get!().get_floating(self)
}
/// Sets whether the currently focused window is floating.
pub fn set_floating(self, floating: bool) {
get!().set_floating(self, floating);
}
/// Toggles whether the currently focused window is floating.
pub fn toggle_floating(self) {
get!().toggle_floating(self);
}
/// Shows the workspace and sets the keyboard focus of the seat to that workspace.
///
/// If the workspace doesn't currently exist, it is created on the output that contains the
/// seat's cursor.
pub fn show_workspace(self, workspace: Workspace) {
get!().show_workspace(self, workspace)
}
/// Moves the currently focused window to the workspace.
pub fn set_workspace(self, workspace: Workspace) {
get!().set_workspace(self, workspace)
}
/// Toggles whether the currently focused window is fullscreen.
pub fn toggle_fullscreen(self) {
let c = get!();
c.set_fullscreen(self, !c.get_fullscreen(self));
}
/// Returns whether the currently focused window is fullscreen.
pub fn fullscreen(self) -> bool {
get!(false).get_fullscreen(self)
}
/// Sets whether the currently focused window is fullscreen.
pub fn set_fullscreen(self, fullscreen: bool) {
get!().set_fullscreen(self, fullscreen)
}
}
/// Returns all seats.
pub fn get_seats() -> Vec<Seat> {
let mut res = vec![];
(|| res = get!().seats())();
res
get!().seats()
}
/// Returns all input devices.
pub fn input_devices() -> Vec<InputDevice> {
get!().get_input_devices(None)
}
pub fn remove_all_seats() {}
/// Returns or creates a seat.
///
/// Seats are identified by their name. If no seat with the name exists, a new seat will be created.
pub fn get_seat(name: &str) -> Seat {
let mut res = Seat(0);
(|| res = get!().get_seat(name))();
res
get!(Seat(0)).get_seat(name)
}
/// Sets a closure to run when a new seat has been created.
pub fn on_new_seat<F: Fn(Seat) + 'static>(f: F) {
get!().on_new_seat(f)
}
/// Sets a closure to run when a new input device has been added.
pub fn on_new_input_device<F: Fn(InputDevice) + 'static>(f: F) {
get!().on_new_input_device(f)
}
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Seat(pub u64);
impl Seat {
pub const INVALID: Self = Self(0);
pub fn is_invalid(self) -> bool {
self == Self::INVALID
}
}

View file

@ -1,7 +1,14 @@
//! Constants determining the acceleration profile of a device.
//!
//! See the libinput documentation for details.
use bincode::{Decode, Encode};
/// The acceleration profile of a device.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct AccelProfile(pub u32);
/// A flat acceleration profile.
pub const ACCEL_PROFILE_FLAT: AccelProfile = AccelProfile(1 << 0);
/// An adaptive acceleration profile.
pub const ACCEL_PROFILE_ADAPTIVE: AccelProfile = AccelProfile(1 << 1);

View file

@ -1,5 +1,10 @@
//! Constants specifying the capabilities of an input device.
//!
//! See the libinput documentation for the meanings of these constants.
use bincode::{Decode, Encode};
/// A capability of an input device.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Capability(pub u32);

View file

@ -1,16 +0,0 @@
use bincode::{Decode, Encode};
#[derive(Encode, Decode, Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Keymap(pub u64);
impl Keymap {
pub const INVALID: Self = Self(0);
pub fn is_invalid(self) -> bool {
self == Self::INVALID
}
}
pub fn parse_keymap(keymap: &str) -> Keymap {
get!(Keymap::INVALID).parse_keymap(keymap)
}

View file

@ -1,13 +1,15 @@
//! Tools affecting the keyboard behavior.
use {
crate::keyboard::{mods::Modifiers, syms::KeySym},
bincode::{Decode, Encode},
std::ops::{BitOr, BitOrAssign},
};
pub mod keymap;
pub mod mods;
pub mod syms;
/// A keysym with zero or more modifiers
#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ModifiedKeySym {
pub mods: Modifiers,
@ -39,3 +41,62 @@ impl BitOrAssign<Modifiers> for ModifiedKeySym {
self.mods |= rhs;
}
}
/// A keymap.
#[derive(Encode, Decode, Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Keymap(pub u64);
impl Keymap {
/// The invalid keymap.
pub const INVALID: Self = Self(0);
/// Returns whether this keymap is valid.
pub fn is_valid(self) -> bool {
self != Self::INVALID
}
/// Returns whether this keymap is invalid.
pub fn is_invalid(self) -> bool {
self == Self::INVALID
}
}
/// Parses a keymap.
///
/// The returned keymap can later be used to set the keymap of a seat. If the keymap cannot
/// be parsed, returns an invalid keymap. Trying to set a seat's keymap to an invalid keymap
/// has no effect.
///
/// A simple keymap looks as follows:
///
/// ```text
/// xkb_keymap {
/// xkb_keycodes { include "evdev+aliases(qwerty)" };
/// xkb_types { include "complete" };
/// xkb_compat { include "complete" };
/// xkb_symbols { include "pc+inet(evdev)+us(basic)" };
/// };
/// ```
///
/// To use a programmer Dvorak, replace the corresponding line by
///
/// ```text
/// xkb_symbols { include "pc+inet(evdev)+us(dvp)" };
/// ```
///
/// To use a German keymap, replace the corresponding line by
///
/// ```text
/// xkb_symbols { include "pc+inet(evdev)+de(basic)" };
/// ```
///
/// You can also use a completely custom keymap that doesn't use any includes. See the
/// [default][default] Jay keymap for an example.
///
/// General information about the keymap format can be found in the [arch wiki][wiki].
///
/// [default]: https://github.com/mahkoh/jay/tree/master/src/keymap.xkb
/// [wiki]: https://wiki.archlinux.org/title/X_keyboard_extension
pub fn parse_keymap(keymap: &str) -> Keymap {
get!(Keymap::INVALID).parse_keymap(keymap)
}

View file

@ -1,24 +1,39 @@
//! Keyboard modifiers
use {
crate::{keyboard::syms::KeySym, ModifiedKeySym},
bincode::{Decode, Encode},
std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign},
};
/// Zero or more keyboard modifiers
#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Default, Hash, Debug)]
pub struct Modifiers(pub u32);
/// The Shift modifier
pub const SHIFT: Modifiers = Modifiers(1 << 0);
/// The CapsLock modifier.
pub const LOCK: Modifiers = Modifiers(1 << 1);
/// The Ctrl modifier.
pub const CTRL: Modifiers = Modifiers(1 << 2);
/// The Mod1 modifier, i.e., Alt.
pub const MOD1: Modifiers = Modifiers(1 << 3);
/// The Mod2 modifier, i.e., NumLock.
pub const MOD2: Modifiers = Modifiers(1 << 4);
/// The Mod3 modifier.
pub const MOD3: Modifiers = Modifiers(1 << 5);
/// The Mod4 modifier, i.e., Logo.
pub const MOD4: Modifiers = Modifiers(1 << 6);
/// The Mod5 modifier.
pub const MOD5: Modifiers = Modifiers(1 << 7);
/// Alias for `LOCK`.
pub const CAPS: Modifiers = LOCK;
/// Alias for `MOD1`.
pub const ALT: Modifiers = MOD1;
/// Alias for `MOD2`.
pub const NUM: Modifiers = MOD2;
/// Alias for `MOD4`.
pub const LOGO: Modifiers = MOD4;
impl BitOr for Modifiers {

View file

@ -1,7 +1,10 @@
//! Keysyms
#![allow(non_upper_case_globals)]
use bincode::{Decode, Encode};
/// A keysym.
#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct KeySym(pub u32);

View file

@ -1,42 +1,69 @@
//! This crate allows you to configure the Jay compositor.
//!
//! A minimal example configuration looks as follows:
//!
//! ```rust
//! use jay_config::config;
//!
//! fn configure() {
//!
//! }
//!
//! config!(configure);
//! ```
//!
//! This configuration will not allow you to interact with the compositor at all nor exit it.
//! To add at least that much functionality, add the following code to `configure`:
//!
//! ```rust
//! use jay_config::{config, quit};
//! use jay_config::input::{get_seat, input_devices, on_new_input_device};
//! use jay_config::keyboard::mods::ALT;
//! use jay_config::keyboard::syms::SYM_q;
//!
//! fn configure() {
//! // Create a seat.
//! let seat = get_seat("default");
//! // Create a key binding to exit the compositor.
//! seat.bind(ALT | SYM_q, || quit());
//! // Assign all current and future input devices to this seat.
//! input_devices().into_iter().for_each(move |d| d.set_seat(seat));
//! on_new_input_device(move |d| d.set_seat(seat));
//! }
//!
//! config!(configure);
//! ```
use {
crate::keyboard::{keymap::Keymap, ModifiedKeySym},
crate::keyboard::ModifiedKeySym,
bincode::{Decode, Encode},
std::{
collections::HashMap,
fmt::{Debug, Display, Formatter},
time::Duration,
},
std::fmt::{Debug, Display, Formatter},
};
#[macro_use]
mod macros;
#[doc(hidden)]
pub mod _private;
pub mod drm;
pub mod embedded;
pub mod exec;
pub mod input;
pub mod keyboard;
pub mod logging;
pub mod status;
pub mod theme;
pub mod timer;
pub mod video;
#[derive(Encode, Decode, Copy, Clone, Debug)]
pub enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}
/// A planar direction.
#[derive(Encode, Decode, Copy, Clone, Debug, Eq, PartialEq)]
pub enum Direction {
Unspecified,
Left,
Down,
Up,
Right,
}
/// A planar axis.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum Axis {
Horizontal,
@ -44,6 +71,7 @@ pub enum Axis {
}
impl Axis {
/// Returns the axis orthogonal to `self`.
pub fn other(self) -> Self {
match self {
Self::Horizontal => Self::Vertical,
@ -52,88 +80,47 @@ impl Axis {
}
}
/// Exits the compositor.
pub fn quit() {
get!().quit()
}
/// Switches to a different VT.
pub fn switch_to_vt(n: u32) {
get!().switch_to_vt(n)
}
pub fn set_env(key: &str, val: &str) {
get!().set_env(key, val);
}
pub struct Command {
prog: String,
args: Vec<String>,
env: HashMap<String, String>,
}
impl Command {
pub fn new(prog: &str) -> Self {
Self {
prog: prog.to_string(),
args: vec![],
env: Default::default(),
}
}
pub fn arg(&mut self, arg: &str) -> &mut Self {
self.args.push(arg.to_string());
self
}
pub fn env(&mut self, key: &str, val: &str) -> &mut Self {
self.env.insert(key.to_string(), val.to_string());
self
}
pub fn spawn(&self) {
get!().spawn(self);
}
}
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Workspace(pub u64);
pub fn get_workspace(name: &str) -> Workspace {
get!(Workspace(0)).get_workspace(name)
}
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Timer(pub u64);
pub fn get_timer(name: &str) -> Timer {
get!(Timer(0)).get_timer(name)
}
impl Timer {
pub fn program(self, initial: Duration, periodic: Option<Duration>) {
get!().program_timer(self, Some(initial), periodic);
}
pub fn cancel(self) {
get!().program_timer(self, None, None);
}
pub fn remove(self) {
get!().remove_timer(self);
}
pub fn on_tick<F: Fn() + 'static>(self, f: F) {
get!().on_timer_tick(self, f);
}
}
/// Reloads the configuration.
///
/// If the configuration cannot be reloaded, this function has no effect.
pub fn reload() {
get!().reload()
}
/// Returns whether this execution of the configuration function is due to a reload.
///
/// This can be used to decide whether the configuration should auto-start programs.
pub fn is_reload() -> bool {
get!(false).is_reload()
}
/// A workspace.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Workspace(pub u64);
/// Returns the workspace with the given name.
///
/// Workspaces are identified by their name. Calling this function alone does not create the
/// workspace if it doesn't already exist.
pub fn get_workspace(name: &str) -> Workspace {
get!(Workspace(0)).get_workspace(name)
}
/// A PCI ID.
///
/// PCI IDs can be used to identify a hardware component. See the Debian [documentation][pci].
///
/// [pci]: https://wiki.debian.org/HowToIdentifyADevice/PCI
#[derive(Encode, Decode, Debug, Copy, Clone, Hash, Eq, PartialEq, Default)]
pub struct PciId {
pub vendor: u32,

16
jay-config/src/logging.rs Normal file
View file

@ -0,0 +1,16 @@
//! Tools for modifying the logging behavior of the compositor.
//!
//! Note that you can use the `log` crate for logging. All invocations of `log::info` etc.
//! automatically log into the compositors log.
use bincode::{Decode, Encode};
/// The log level of the compositor or a log message.
#[derive(Encode, Decode, Copy, Clone, Debug)]
pub enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}

View file

@ -1,3 +1,4 @@
/// Declares the entry point of the configuration.
#[macro_export]
macro_rules! config {
($f:path) => {

View file

@ -1,3 +1,12 @@
//! Knobs for changing the status text.
/// Sets the status text.
///
/// The status text is displayed at the right end of the bar.
///
/// The status text should be specified in [pango][pango] markup language.
///
/// [pango]: https://docs.gtk.org/Pango/pango_markup.html
pub fn set_status(status: &str) {
get!().set_status(status);
}

View file

@ -1,4 +1,4 @@
//! Knobs for changing the look of the compositor.
//! Tools for configuring the look of the compositor.
use bincode::{Decode, Encode};
@ -156,7 +156,7 @@ pub fn reset_font() {
get!().reset_font()
}
/// Tools for customizing the colors of the desktop.
/// Elements of the compositor whose color can be changed.
pub mod colors {
use {
crate::theme::Color,
@ -248,7 +248,7 @@ pub mod colors {
}
}
/// Tools for customizing the sizes of GUI elements.
/// Elements of the compositor whose size can be changed.
pub mod sized {
use bincode::{Decode, Encode};

92
jay-config/src/timer.rs Normal file
View file

@ -0,0 +1,92 @@
//! Timers for one-time or repeated actions.
use {
bincode::{Decode, Encode},
std::time::{Duration, SystemTime, UNIX_EPOCH},
};
/// A timer.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Timer(pub u64);
/// Creates a new timer or returns an existing one.
///
/// Timers are identified by their name and their lifetime is bound by the lifetime of
/// the configuration. Reloading the configuration destroys all existing timers.
///
/// Within the same configuration, calling this function multiple times with the same name
/// will return the same timer.
///
/// Timers can be deleted by calling `remove`. At that point all existing references to
/// the timer become invalid and `get_timer` will return a new timer.
pub fn get_timer(name: &str) -> Timer {
get!(Timer(0)).get_timer(name)
}
impl Timer {
/// Programs the timer to fire once.
pub fn once(self, initial: Duration) {
get!().program_timer(self, Some(initial), None);
}
/// Programs the timer to fire repeatedly.
///
/// `initial` is the period after which the timer expires for the first time.
pub fn repeated(self, initial: Duration, period: Duration) {
get!().program_timer(self, Some(initial), Some(period));
}
/// Cancels the timer.
///
/// The timer remains valid but will never expire. It can be reprogrammed by calling
/// `once` or `repeated`.
pub fn cancel(self) {
get!().program_timer(self, None, None);
}
/// Removes the time.
///
/// This reference to the timer becomes invalid as do all other existing references.
/// A new timer with the same name can be created by calling `get_timer`.
pub fn remove(self) {
get!().remove_timer(self);
}
/// Sets the function to be executed when the timer expires.
pub fn on_tick<F: Fn() + 'static>(self, f: F) {
get!().on_timer_tick(self, f);
}
}
/// Returns the duration until the wall clock is a multiple of `duration`.
///
/// # Example
///
/// Execute a timer every time the wall clock becomes a multiple of 5 seconds:
///
/// ```rust,ignore
/// let period = Duration::from_secs(5);
/// let timer = get_timer("status_timer");
/// timer.repeated(
/// duration_until_wall_clock_is_multiple_of(period),
/// period,
/// );
/// timer.on_tick(|| todo!());
/// ```
pub fn duration_until_wall_clock_is_multiple_of(duration: Duration) -> Duration {
let now = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(n) => n,
_ => return Duration::from_secs(0),
};
let now = now.as_nanos();
let duration = duration.as_nanos();
if duration == 0 {
return Duration::from_secs(0);
}
let nanos = duration - now % duration;
if nanos == duration {
Duration::from_secs(0)
} else {
Duration::from_nanos(nanos as _)
}
}

View file

@ -1,6 +1,8 @@
//! Tools for configuring graphics cards and monitors.
use {
crate::{
drm::connector_type::{
video::connector_type::{
ConnectorType, CON_9PIN_DIN, CON_COMPONENT, CON_COMPOSITE, CON_DISPLAY_PORT, CON_DPI,
CON_DSI, CON_DVIA, CON_DVID, CON_DVII, CON_EDP, CON_EMBEDDED_WINDOW, CON_HDMIA,
CON_HDMIB, CON_LVDS, CON_SPI, CON_SVIDEO, CON_TV, CON_UNKNOWN, CON_USB, CON_VGA,
@ -12,7 +14,14 @@ use {
std::str::FromStr,
};
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
/// The mode of a connector.
///
/// Currently a mode consists of three properties:
///
/// - width in pixels
/// - height in pixels
/// - refresh rate in mhz.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Mode {
pub(crate) width: i32,
pub(crate) height: i32,
@ -20,14 +29,19 @@ pub struct Mode {
}
impl Mode {
/// Returns the width of the mode.
pub fn width(&self) -> i32 {
self.width
}
/// Returns the height of the mode.
pub fn height(&self) -> i32 {
self.height
}
/// Returns the refresh rate of the mode in mhz.
///
/// For a 60hz monitor, this function would return 60_000.
pub fn refresh_rate(&self) -> u32 {
self.refresh_millihz
}
@ -41,14 +55,23 @@ impl Mode {
}
}
/// A connector that is potentially connected to an output device.
///
/// A connector is the part that sticks out of your graphics card. A graphics card usually
/// has many connectors but one few of them are actually connected to a monitor.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Connector(pub u64);
impl Connector {
/// Returns whether this connector existed at the time `get_connector` was called.
///
/// This only implies existence at the time `get_connector` was called. Even if this
/// function returns true, the connector might since have disappeared.
pub fn exists(self) -> bool {
self.0 != 0
}
/// Returns whether the connector is connected to an output device.
pub fn connected(self) -> bool {
if !self.exists() {
return false;
@ -56,6 +79,7 @@ impl Connector {
get!(false).connector_connected(self)
}
/// Returns the connector type.
pub fn ty(self) -> ConnectorType {
if !self.exists() {
return CON_UNKNOWN;
@ -63,6 +87,7 @@ impl Connector {
get!(CON_UNKNOWN).connector_type(self)
}
/// Returns the current mode of the connector.
pub fn mode(self) -> Mode {
if !self.exists() {
return Mode::zeroed();
@ -70,18 +95,34 @@ impl Connector {
get!(Mode::zeroed()).connector_mode(self)
}
/// Returns the width of the current mode of the connector.
///
/// This is a shortcut for `mode().width()`.
pub fn width(self) -> i32 {
self.mode().width
}
/// Returns the height of the current mode of the connector.
///
/// This is a shortcut for `mode().height()`.
pub fn height(self) -> i32 {
self.mode().height
}
/// Returns the refresh rate in mhz of the current mode of the connector.
///
/// This is a shortcut for `mode().refresh_rate()`.
pub fn refresh_rate(self) -> u32 {
self.mode().refresh_millihz
}
/// Sets the position of the connector in the global compositor space.
///
/// `x` and `y` must be non-negative and must not exceed a currently unspecified limit.
/// Any reasonable values for `x` and `y` should work.
///
/// This function allows the connector to overlap with other connectors, however, such
/// configurations are not supported and might result in unexpected behavior.
pub fn set_position(self, x: i32, y: i32) {
if !self.exists() {
log::warn!("set_position called on a connector that does not exist");
@ -91,30 +132,72 @@ impl Connector {
}
}
/// Returns all available DRM devices.
pub fn drm_devices() -> Vec<DrmDevice> {
get!().drm_devices()
}
/// Sets the callback to be called when a new DRM device appears.
pub fn on_new_drm_device<F: Fn(DrmDevice) + 'static>(f: F) {
get!().on_new_drm_device(f)
}
/// Sets the callback to be called when a DRM device is removed.
pub fn on_drm_device_removed<F: Fn(DrmDevice) + 'static>(f: F) {
get!().on_del_drm_device(f)
}
/// Sets the callback to be called when a new connector appears.
pub fn on_new_connector<F: Fn(Connector) + 'static>(f: F) {
get!().on_new_connector(f)
}
/// Sets the callback to be called when a connector becomes connected to an output device.
pub fn on_connector_connected<F: Fn(Connector) + 'static>(f: F) {
get!().on_connector_connected(f)
}
/// Sets the callback to be called when the graphics of the compositor have been initialized.
///
/// This callback is only invoked once during the lifetime of the compositor. This is a good place
/// to auto start graphical applications.
pub fn on_graphics_initialized<F: FnOnce() + 'static>(f: F) {
get!().on_graphics_initialized(f)
}
/// Returns the connector with the given id.
///
/// The linux kernel identifies connectors by a (type, idx) tuple, e.g., `DP-0`.
/// If the connector does not exist at the time this function is called, a sentinel value is
/// returned. This can be checked by calling `exists()` on the returned connector.
///
/// The `id` argument can either be an explicit tuple, e.g. `(CON_DISPLAY_PORT, 0)`, or a string
/// that can be parsed to such a tuple, e.g. `"DP-0"`.
///
/// The following string prefixes exist:
///
/// - `DP`
/// - `eDP`
/// - `HDMI-A`
/// - `HDMI-B`
/// - `EmbeddedWindow` - this is an implementation detail of the compositor and used if it
/// runs as an embedded application.
/// - `VGA`
/// - `DVI-I`
/// - `DVI-D`
/// - `DVI-A`
/// - `Composite`
/// - `SVIDEO`
/// - `LVDS`
/// - `Component`
/// - `DIN`
/// - `TV`
/// - `Virtual`
/// - `DSI`
/// - `DPI`
/// - `Writeback`
/// - `SPI`
/// - `USB`
pub fn get_connector(id: impl ToConnectorId) -> Connector {
let (ty, idx) = match id.to_connector_id() {
Ok(id) => id,
@ -126,6 +209,7 @@ pub fn get_connector(id: impl ToConnectorId) -> Connector {
get!(Connector(0)).get_connector(ty, idx)
}
/// A type that can be converted to a `(ConnectorType, idx)` tuple.
pub trait ToConnectorId {
fn to_connector_id(&self) -> Result<(ConnectorType, u32), String>;
}
@ -172,9 +256,11 @@ impl ToConnectorId for &'_ str {
}
}
/// Module containing all known connector types.
pub mod connector_type {
use bincode::{Decode, Encode};
/// The type of a connector.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct ConnectorType(pub u32);
@ -202,26 +288,44 @@ pub mod connector_type {
pub const CON_EMBEDDED_WINDOW: ConnectorType = ConnectorType(u32::MAX);
}
/// A *Direct Rendering Manager* (DRM) device.
///
/// It's easiest to think of a DRM device as a graphics card.
/// There are also DRM devices that are emulated in software but you are unlikely to encounter
/// those accidentally.
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct DrmDevice(pub u64);
impl DrmDevice {
/// Returns the connectors of this device.
pub fn connectors(self) -> Vec<Connector> {
get!().device_connectors(self)
}
/// Returns the syspath of this device.
///
/// E.g. `/sys/devices/pci0000:00/0000:00:03.1/0000:07:00.0`.
pub fn syspath(self) -> String {
get!().drm_device_syspath(self)
}
/// Returns the vendor of this device.
///
/// E.g. `Advanced Micro Devices, Inc. [AMD/ATI]`.
pub fn vendor(self) -> String {
get!().drm_device_vendor(self)
}
/// Returns the model of this device.
///
/// E.g. `Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570 Armor 8G OC)`.
pub fn model(self) -> String {
get!().drm_device_model(self)
}
/// Returns the PIC ID of this device.
///
/// E.g. `1002:67DF`.
pub fn pci_id(self) -> PciId {
get!().drm_device_pci_id(self)
}