Update dependencies, allow changing the screen brightness dynamically
This commit is contained in:
parent
e2f4aac438
commit
95f34add08
22 changed files with 1021 additions and 844 deletions
1586
Cargo.lock
generated
1586
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
44
Cargo.toml
44
Cargo.toml
|
@ -4,36 +4,36 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.5.0"
|
||||
clap = { version = "4.4.12", features = ["derive"] }
|
||||
color-eyre = "0.6.2"
|
||||
cosmic-text = "0.10.0"
|
||||
derive_more = "0.99.17"
|
||||
parse-display = "0.9.0"
|
||||
loupedeck_serial = { path = "./crates/loupedeck_serial" }
|
||||
deckster_shared = { path = "./crates/deckster_shared" }
|
||||
bytes = "1.6.0"
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
cosmic-text = "0.11.2"
|
||||
derive_more = { version = "1.0.0-beta.6", features = ["deref", "from", "into"] }
|
||||
parse-display = "0.9.1"
|
||||
encode_unicode = "1.0.0"
|
||||
enum-map = "3.0.0-beta.2"
|
||||
enum-ordinalize = "4.3.0"
|
||||
env_logger = "0.11.0"
|
||||
env_logger = "0.11.3"
|
||||
humantime-serde = "1.1.1"
|
||||
log = "0.4.20"
|
||||
loupedeck_serial = { path = "./crates/loupedeck_serial" }
|
||||
deckster_shared = { path = "./crates/deckster_shared" }
|
||||
regex = "1.10.2"
|
||||
resvg = "0.37.0"
|
||||
log = "0.4.21"
|
||||
regex = "1.10.5"
|
||||
resvg = "0.42.0"
|
||||
rgb = "0.8.37"
|
||||
serde = { version = "1.0.193", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.113"
|
||||
serde = { version = "1.0.203", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.117"
|
||||
serde_regex = "1.1.0"
|
||||
serde_with = "3.4.0"
|
||||
thiserror = "1.0.52"
|
||||
tiny-skia = "0.11.3"
|
||||
tokio = { version = "1.35.1", features = ["macros", "parking_lot", "rt", "rt-multi-thread", "sync", "process", "io-util"] }
|
||||
toml = "0.8.8"
|
||||
walkdir = "2.4.0"
|
||||
serde_with = "3.8.1"
|
||||
thiserror = "1.0.61"
|
||||
tiny-skia = "0.11.4"
|
||||
tokio = { version = "1.38.0", features = ["macros", "parking_lot", "rt", "rt-multi-thread", "sync", "process", "io-util"] }
|
||||
toml = "0.8.14"
|
||||
walkdir = "2.5.0"
|
||||
once_cell = "1.19.0"
|
||||
is_executable = "1.0.1"
|
||||
rumqttc = "0.23.0"
|
||||
itertools = "0.12.1"
|
||||
rumqttc = "0.24.0"
|
||||
itertools = "0.13.0"
|
||||
flume = "0.11.0"
|
||||
nanoid = "0.4.0"
|
||||
|
||||
|
|
15
README.md
15
README.md
|
@ -1,17 +1,20 @@
|
|||
# Deckster
|
||||
|
||||
## To do
|
||||
- Make the CLI of handlers more useful
|
||||
- Make the `playerctl` handler independent of… playerctl. Use the [`mpris` crate](https://lib.rs/crates/mpris) directly instead.
|
||||
- Implement scrolling
|
||||
- Move loupedeck_serial and pa_volume_interface out of this repository.
|
||||
- Publish libraries to crates.io
|
||||
- Move handlers to their own repositories
|
||||
- Update dependencies
|
||||
- Make the CLI of handlers more useful
|
||||
- Make the `playerctl` handler independent of… playerctl. Use the [`mpris` crate](https://lib.rs/crates/mpris) directly instead.
|
||||
|
||||
## Contributing
|
||||
|
||||
### Terminology
|
||||
- `handler runner`: Node that is running handlers.
|
||||
- `handler host`: A `handler runner` that is not the `coordinator`.
|
||||
- `coordinator`: Node to which the Loupedeck device is physically connected. Always a `handler runner`.
|
||||
- `coordinator`: Node to which the Loupedeck device is physically connected. Can be a `handler runner`.
|
||||
|
||||
|
||||
### The different types of `unwrap`
|
||||
- `expect("<reason>")`: The author thinks that unwrapping will never fail because of `<reason>`.
|
||||
|
@ -19,7 +22,9 @@
|
|||
- `unwrap_todo()`: The author has not yet thought about how to handle this value being `None` or `Err`.
|
||||
They will replace this unwrapping with `expect("<reason>")`, `unwrap()`, or proper error handling later.
|
||||
|
||||
## Attribution
|
||||
|
||||
## Credits
|
||||
|
||||
[foxxyz’s `loupedeck` library for JavaScript](https://github.com/foxxyz/loupedeck)
|
||||
(licensed under the [MIT license](https://github.com/foxxyz/loupedeck/blob/e41e5d920130d9ef651e47173c68450b9c832b96/LICENSE))
|
||||
was used as a reference for and inspired the design of `loupedeck_serial`.
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
deckster_shared = { path = "../deckster_shared" }
|
||||
thiserror = "1.0.56"
|
||||
thiserror = "1.0.61"
|
||||
im = "15.1.0"
|
||||
serde = { version = "1.0.196", default-features = false }
|
||||
serde_json = "1.0.113"
|
||||
serde = { version = "1.0.203", default-features = false }
|
||||
serde_json = "1.0.117"
|
||||
|
|
|
@ -4,11 +4,11 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.195", features = ["derive", "rc"] }
|
||||
serde_with = "3.4.0"
|
||||
thiserror = "1.0.56"
|
||||
derive_more = "0.99.17"
|
||||
serde = { version = "1.0.203", features = ["derive", "rc"] }
|
||||
serde_with = "3.8.1"
|
||||
thiserror = "1.0.61"
|
||||
derive_more = { version = "1.0.0-beta.6", features = ["from", "into", "deref"] }
|
||||
rgb = "0.8.37"
|
||||
enum-ordinalize = "4.3.0"
|
||||
enum-map = "3.0.0-beta.2"
|
||||
parse-display = "0.8.2"
|
||||
parse-display = "0.9.1"
|
||||
|
|
|
@ -7,8 +7,8 @@ edition = "2021"
|
|||
serialport = "4.3.0"
|
||||
enum-ordinalize = "4.3.0"
|
||||
enumset = "1.1.3"
|
||||
bytes = "1.5.0"
|
||||
thiserror = "1.0.52"
|
||||
bytes = "1.6.0"
|
||||
thiserror = "1.0.61"
|
||||
rgb = "0.8.37"
|
||||
flume = "0.11.0"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
|
@ -43,7 +43,7 @@ pub enum VibrationPattern {
|
|||
pub(crate) enum LoupedeckCommand {
|
||||
RequestSerialNumber,
|
||||
RequestFirmwareVersion,
|
||||
SetBrightness(f32),
|
||||
SetBrightness(u8),
|
||||
SetButtonColor {
|
||||
button: LoupedeckButton,
|
||||
color: RGB8,
|
||||
|
|
|
@ -103,7 +103,10 @@ impl LoupedeckDevice {
|
|||
self.events_receiver.clone()
|
||||
}
|
||||
|
||||
pub fn set_brightness(&self, value: f32) {
|
||||
/// Sets the screen brightness.
|
||||
///
|
||||
/// `value` must be in 0..10. Higher values are clamped to 10.
|
||||
pub fn set_brightness(&self, value: u8) {
|
||||
self.commands_sender.send(LoupedeckCommand::SetBrightness(value)).unwrap();
|
||||
}
|
||||
|
||||
|
|
|
@ -263,10 +263,7 @@ pub(crate) fn write_messages_worker(mut port: Box<dyn SerialPort>, receiver: flu
|
|||
let result = match command {
|
||||
LoupedeckCommand::RequestSerialNumber => send(0x03, Bytes::new()),
|
||||
LoupedeckCommand::RequestFirmwareVersion => send(0x07, Bytes::new()),
|
||||
LoupedeckCommand::SetBrightness(value) => {
|
||||
let raw_value = (value.clamp(0f32, 1f32) * 10.0) as u8;
|
||||
send(0x09, Bytes::copy_from_slice(&[raw_value]))
|
||||
}
|
||||
LoupedeckCommand::SetBrightness(value) => send(0x09, Bytes::copy_from_slice(&[value])),
|
||||
LoupedeckCommand::SetButtonColor { button, color } => send(0x02, Bytes::copy_from_slice(&[button.ordinal(), color.r, color.g, color.b])),
|
||||
LoupedeckCommand::ReplaceFramebufferArea {
|
||||
display_id,
|
||||
|
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
flume = "0.11.0"
|
||||
im = "15.1.0"
|
||||
tokio = { version = "1.35.1", default-features = false, features = ["sync"] }
|
||||
tokio = { version = "1.38.0", default-features = false, features = ["sync"] }
|
||||
libpulse-binding = "2.28.1"
|
||||
log = "0.4.21"
|
||||
arc-swap = "1.7.1"
|
|
@ -1,5 +1,5 @@
|
|||
use arc_swap::ArcSwap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Instant;
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
deckster_mode = { path = "../../crates/deckster_mode" }
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
color-eyre = "0.6.2"
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
env_logger = "0.11.1"
|
||||
log = "0.4.20"
|
||||
tokio = { version = "1.35.1", features = ["macros", "parking_lot", "rt", "process", "sync"] }
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.21"
|
||||
tokio = { version = "1.38.0", features = ["macros", "parking_lot", "rt", "process", "sync"] }
|
|
@ -5,17 +5,17 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
deckster_mode = { path = "../../crates/deckster_mode" }
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
color-eyre = "0.6.2"
|
||||
env_logger = "0.11.1"
|
||||
log = "0.4.20"
|
||||
tokio = { version = "1.35.1", features = ["macros", "parking_lot", "rt", "sync"] }
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
serde_json = "1.0.114"
|
||||
reqwest = { version = "0.11.24", default-features = false, features = ["rustls-tls-native-roots"] }
|
||||
url = { version = "2.5.0", features = ["serde"] }
|
||||
tokio-tungstenite = { version = "0.21.0", features = ["rustls-tls-native-roots"] }
|
||||
tokio-stream = "0.1.14"
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.21"
|
||||
tokio = { version = "1.38.0", features = ["macros", "parking_lot", "rt", "sync"] }
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde_json = "1.0.117"
|
||||
reqwest = { version = "0.12.4", default-features = false, features = ["rustls-tls-native-roots"] }
|
||||
url = { version = "2.5.1", features = ["serde"] }
|
||||
tokio-tungstenite = { version = "0.23.1", features = ["rustls-tls-native-roots"] }
|
||||
tokio-stream = "0.1.15"
|
||||
futures-util = "0.3.30"
|
||||
parse-display = "0.9.0"
|
||||
serde_with = "3.6.1"
|
||||
parse-display = "0.9.1"
|
||||
serde_with = "3.8.1"
|
|
@ -6,12 +6,12 @@ edition = "2021"
|
|||
[dependencies]
|
||||
deckster_mode = { path = "../../crates/deckster_mode" }
|
||||
pa_volume_interface = { path = "../../crates/pa_volume_interface" }
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
color-eyre = "0.6.2"
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde_regex = "1.1.0"
|
||||
regex = "1.10.3"
|
||||
parse-display = "0.8.2"
|
||||
env_logger = "0.11.1"
|
||||
log = "0.4.20"
|
||||
tokio = { version = "1.35.1", features = ["macros", "parking_lot", "rt-multi-thread", "sync"] }
|
||||
regex = "1.10.5"
|
||||
parse-display = "0.9.1"
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.21"
|
||||
tokio = { version = "1.38.0", features = ["macros", "parking_lot", "rt-multi-thread", "sync"] }
|
|
@ -5,10 +5,10 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
deckster_mode = { path = "../../crates/deckster_mode" }
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
color-eyre = "0.6.2"
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
env_logger = "0.11.1"
|
||||
log = "0.4.20"
|
||||
tokio = { version = "1.35.1", features = ["macros", "parking_lot", "rt-multi-thread", "sync"] }
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.21"
|
||||
tokio = { version = "1.38.0", features = ["macros", "parking_lot", "rt-multi-thread", "sync"] }
|
||||
once_cell = "1.19.0"
|
|
@ -5,10 +5,10 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
deckster_mode = { path = "../../crates/deckster_mode" }
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
color-eyre = "0.6.2"
|
||||
env_logger = "0.11.1"
|
||||
log = "0.4.20"
|
||||
tokio = { version = "1.35.1", features = ["macros", "parking_lot", "rt", "sync", "time"] }
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.21"
|
||||
tokio = { version = "1.38.0", features = ["macros", "parking_lot", "rt", "sync", "time"] }
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
humantime-serde = "1.1.1"
|
|
@ -190,7 +190,7 @@ pub mod labels {
|
|||
let font_size = if text.utf8chars().count() == 1 { 40.0 } else { 11.0 };
|
||||
self.buffer.set_metrics(&mut self.font_system, Metrics::new(font_size, font_size));
|
||||
|
||||
self.buffer.shape_until_scroll(&mut self.font_system);
|
||||
self.buffer.shape_until_scroll(&mut self.font_system, false);
|
||||
|
||||
self.buffer.draw(
|
||||
&mut self.font_system,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::cell::RefCell;
|
||||
use std::ops::Add;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -10,7 +11,7 @@ use tokio::sync::broadcast;
|
|||
use deckster_shared::handler_communication::{HandlerCommand, HandlerEvent, KeyEvent, KeyTouchEventKind, KnobEvent, VibrationPattern};
|
||||
use deckster_shared::path::{KeyPath, KeyPosition, KnobPath, KnobPosition};
|
||||
use deckster_shared::state::{Key, Knob};
|
||||
use loupedeck_serial::characteristics::{LoupedeckDeviceKeyGridCharacteristics, LoupedeckDisplayRect, LoupedeckKnob};
|
||||
use loupedeck_serial::characteristics::{LoupedeckButton, LoupedeckDeviceKeyGridCharacteristics, LoupedeckDisplayRect, LoupedeckKnob};
|
||||
use loupedeck_serial::commands::VibrationPattern as LSVibrationPattern;
|
||||
use loupedeck_serial::device::LoupedeckDevice;
|
||||
use loupedeck_serial::events::{LoupedeckEvent, RotationDirection};
|
||||
|
@ -23,6 +24,7 @@ use crate::model::coordinator_config::Config;
|
|||
use crate::model::position::ButtonPosition;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum CoordinatorCommand {
|
||||
SetActivePages { key_page_id: String, knob_page_id: String },
|
||||
SetRemoteHostIsActive { host_id: Box<str>, is_active: bool },
|
||||
|
@ -120,6 +122,10 @@ fn handle_event(context: &mut IoWorkerContext, event: LoupedeckEvent) -> bool {
|
|||
let position = ButtonPosition::of(&button);
|
||||
let button_config = &context.config.buttons[position];
|
||||
|
||||
if button == LoupedeckButton::N0 {
|
||||
context.state.is_0_button_held = true
|
||||
}
|
||||
|
||||
context
|
||||
.coordinator_commands_sender
|
||||
.send(CoordinatorCommand::SetActivePages {
|
||||
|
@ -128,6 +134,11 @@ fn handle_event(context: &mut IoWorkerContext, event: LoupedeckEvent) -> bool {
|
|||
})
|
||||
.unwrap()
|
||||
}
|
||||
LoupedeckEvent::ButtonUp { button } => {
|
||||
if button == LoupedeckButton::N0 {
|
||||
context.state.is_0_button_held = false
|
||||
}
|
||||
}
|
||||
LoupedeckEvent::Touch { x, y, is_end, touch_id } => {
|
||||
let characteristics = context.device.characteristics();
|
||||
let display = characteristics.get_display_at_coordinates(x, y);
|
||||
|
@ -183,6 +194,16 @@ fn handle_event(context: &mut IoWorkerContext, event: LoupedeckEvent) -> bool {
|
|||
}
|
||||
}
|
||||
LoupedeckEvent::KnobRotate { knob, direction } => {
|
||||
if knob == LoupedeckKnob::LeftBottom && context.state.is_0_button_held {
|
||||
context.state.screen_brightness = (context.state.screen_brightness as i8)
|
||||
.add(match direction {
|
||||
RotationDirection::Clockwise => 1,
|
||||
RotationDirection::Counterclockwise => -1,
|
||||
})
|
||||
.clamp(0, 10) as u8;
|
||||
|
||||
context.device.set_brightness(context.state.screen_brightness);
|
||||
} else {
|
||||
let position: KnobPosition = get_position_of_loupedeck_knob(knob);
|
||||
|
||||
send_knob_event(
|
||||
|
@ -198,6 +219,7 @@ fn handle_event(context: &mut IoWorkerContext, event: LoupedeckEvent) -> bool {
|
|||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
LoupedeckEvent::KnobDown { knob } => {
|
||||
let position: KnobPosition = get_position_of_loupedeck_knob(knob);
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ pub async fn start(config_directory: &Path, config: Config) -> Result<()> {
|
|||
let device = available_device.connect().wrap_err("Connecting to the device failed.")?;
|
||||
log::info!("Connected.");
|
||||
|
||||
device.set_brightness(0.5);
|
||||
device.set_brightness(config.initial.screen_brightness);
|
||||
device.vibrate(VibrationPattern::RiseFall);
|
||||
|
||||
let io_worker_context = IoWorkerContext::create(config_directory, Arc::clone(&config), device, coordinator_commands_sender, events_sender);
|
||||
|
|
|
@ -16,6 +16,8 @@ pub struct State {
|
|||
pub active_remote_handler_host_ids: HashSet<Box<str>>,
|
||||
pub key_pages_by_id: HashMap<String, KeyPage>,
|
||||
pub knob_pages_by_id: HashMap<String, KnobPage>,
|
||||
pub is_0_button_held: bool,
|
||||
pub screen_brightness: u8,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
@ -77,6 +79,8 @@ impl State {
|
|||
},
|
||||
key_pages_by_id,
|
||||
knob_pages_by_id,
|
||||
is_0_button_held: false,
|
||||
screen_brightness: config.initial.screen_brightness,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,8 @@ use std::sync::Arc;
|
|||
|
||||
use color_eyre::eyre::{eyre, ContextCompat, WrapErr};
|
||||
use color_eyre::Result;
|
||||
use resvg::usvg::fontdb;
|
||||
use resvg::usvg::tiny_skia_path::IntSize;
|
||||
use resvg::usvg::{TextRendering, TreeParsing, TreeTextToPath};
|
||||
use resvg::usvg::{fontdb, TextRendering};
|
||||
use tiny_skia::{BlendMode, FilterQuality, Pixmap, PixmapPaint, Transform};
|
||||
|
||||
use deckster_shared::icon_descriptor::{IconDescriptor, IconDescriptorSource};
|
||||
|
@ -23,7 +22,7 @@ pub struct IconManager {
|
|||
config_directory: PathBuf,
|
||||
icon_packs_by_id: Arc<HashMap<String, IconPack>>,
|
||||
dpi: f32,
|
||||
fonts_db: fontdb::Database,
|
||||
fonts_db: Arc<fontdb::Database>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -41,11 +40,10 @@ impl IconManager {
|
|||
config_directory,
|
||||
icon_packs_by_id,
|
||||
dpi,
|
||||
fonts_db,
|
||||
fonts_db: Arc::new(fonts_db),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add caching
|
||||
fn load_icon(&self, descriptor: &IconDescriptor) -> Result<LoadedIcon> {
|
||||
let icon_pack = if let IconDescriptorSource::IconPack { pack_id, .. } = &descriptor.source {
|
||||
Some(
|
||||
|
@ -113,7 +111,7 @@ impl IconManager {
|
|||
fn read_image_and_get_scale(
|
||||
config_directory: &Path,
|
||||
dpi: f32,
|
||||
fonts_db: &fontdb::Database,
|
||||
fonts_db: &Arc<fontdb::Database>,
|
||||
source: &IconDescriptorSource,
|
||||
icon_pack: Option<&IconPack>,
|
||||
preferred_scale: f32,
|
||||
|
@ -148,34 +146,29 @@ fn read_image_and_get_scale(
|
|||
})
|
||||
}
|
||||
|
||||
fn read_image_from_svg(path: &Path, dpi: f32, font_db: &fontdb::Database, scale: f32) -> Result<Pixmap> {
|
||||
fn read_image_from_svg(path: &Path, dpi: f32, font_db: &Arc<fontdb::Database>, scale: f32) -> Result<Pixmap> {
|
||||
let raw_data = std::fs::read(path)?;
|
||||
|
||||
let tree = {
|
||||
let mut tree = resvg::usvg::Tree::from_data(
|
||||
let tree = resvg::usvg::Tree::from_data(
|
||||
&raw_data,
|
||||
&resvg::usvg::Options {
|
||||
dpi,
|
||||
font_family: "Inter".to_owned(),
|
||||
font_size: 11.0,
|
||||
text_rendering: TextRendering::OptimizeLegibility,
|
||||
fontdb: Arc::clone(font_db),
|
||||
..resvg::usvg::Options::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
tree.convert_text(font_db);
|
||||
|
||||
resvg::Tree::from_usvg(&tree)
|
||||
};
|
||||
|
||||
let size = tree.size.to_int_size();
|
||||
let size = tree.size().to_int_size();
|
||||
let mut pixmap = Pixmap::new(
|
||||
max(1, (size.width() as f32 * scale).ceil() as u32),
|
||||
max(1, (size.height() as f32 * scale).ceil() as u32),
|
||||
)
|
||||
.expect("width and height can never be zero.");
|
||||
|
||||
tree.render(Transform::from_scale(scale, scale), &mut pixmap.as_mut());
|
||||
resvg::render(&tree, Transform::from_scale(scale, scale), &mut pixmap.as_mut());
|
||||
|
||||
Ok(pixmap)
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ pub struct ButtonConfig {
|
|||
pub struct InitialConfig {
|
||||
pub key_page: String,
|
||||
pub knob_page: String,
|
||||
pub screen_brightness: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
|
||||
|
@ -99,6 +100,10 @@ impl Config {
|
|||
)));
|
||||
}
|
||||
|
||||
if self.initial.screen_brightness > 10 {
|
||||
return Err(eyre!("The maximum screen brightness is 10."));
|
||||
}
|
||||
|
||||
for (position, button) in &self.buttons {
|
||||
if let Some(key_page) = &button.key_page {
|
||||
if !self.key_pages_by_id.contains_key(key_page) {
|
||||
|
|
Loading…
Add table
Reference in a new issue