Skip to content

Commit 69be7a1

Browse files
Configurable layers
1 parent 7d26335 commit 69be7a1

4 files changed

Lines changed: 99 additions & 48 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ drm = "0.10.0"
1616
anyhow = "1"
1717
input = "0.8"
1818
libc = "0.2"
19-
input-linux = "0.6"
19+
input-linux = { version = "0.6", features = ["serde"] }
2020
input-linux-sys = "0.8"
2121
nix = { version = "0.27", features = ["poll"] }
2222
privdrop = "0.5.3"

share/tiny-dfr/config.toml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,49 @@ EnablePixelShift = false
2323
# section "Font Names"
2424
# https://www.freedesktop.org/software/fontconfig/fontconfig-user.html
2525
FontTemplate = ":bold"
26+
27+
# This key defines the contents of the primary layer
28+
# (the one with F{number} keys)
29+
# You can change the individual buttons, add, or remove them
30+
# Any number of keys that is greater than 0 is allowed
31+
# however rendering will start to break around 24 keys
32+
PrimaryLayerKeys = [
33+
# Action defines the key code to send when the button is pressed
34+
# Text defines the button label
35+
# Svg specifies the icon to be used for the button.
36+
# Do not include the .svg extension in the file name.
37+
# Svgs are looked up in /etc/tiny-dfr first and then in /usr/share/tiny-dfr
38+
# Only one of Text or Svg is allowed,
39+
# if both are present, the behavior is undefined.
40+
# For the list of supported key codes see
41+
# https://docs.rs/input-linux/latest/input_linux/enum.Key.html
42+
{ Text = "F1", Action = "F1" },
43+
{ Text = "F2", Action = "F2" },
44+
{ Text = "F3", Action = "F3" },
45+
{ Text = "F4", Action = "F4" },
46+
{ Text = "F5", Action = "F5" },
47+
{ Text = "F6", Action = "F6" },
48+
{ Text = "F7", Action = "F7" },
49+
{ Text = "F8", Action = "F8" },
50+
{ Text = "F9", Action = "F9" },
51+
{ Text = "F10", Action = "F10" },
52+
{ Text = "F11", Action = "F11" },
53+
{ Text = "F12", Action = "F12" }
54+
]
55+
56+
# This key defines the contents of the media key layer
57+
MediaLayerKeys = [
58+
{ Svg = "brightness_low", Action = "BrightnessDown" },
59+
{ Svg = "brightness_high", Action = "BrightnessUp" },
60+
{ Svg = "mic_off", Action = "MicMute" },
61+
{ Svg = "search", Action = "Search" },
62+
{ Svg = "backlight_low", Action = "IllumDown" },
63+
{ Svg = "backlight_high", Action = "IllumUp" },
64+
{ Svg = "fast_rewind", Action = "PreviousSong" },
65+
{ Svg = "play_pause", Action = "PlayPause" },
66+
{ Svg = "fast_forward", Action = "NextSong" },
67+
{ Svg = "volume_off", Action = "Mute" },
68+
{ Svg = "volume_down", Action = "VolumeDown" },
69+
{ Svg = "volume_up", Action = "VolumeUp" }
70+
]
71+

src/main.rs

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use std::{
66
},
77
path::Path,
88
collections::HashMap,
9-
cmp::min
9+
cmp::min,
10+
mem
1011
};
1112
use std::os::fd::AsFd;
1213
use cairo::{ImageSurface, Format, Context, Surface, Rectangle, FontFace};
@@ -55,18 +56,28 @@ struct ConfigProxy {
5556
media_layer_default: Option<bool>,
5657
show_button_outlines: Option<bool>,
5758
enable_pixel_shift: Option<bool>,
58-
font_template: Option<String>
59+
font_template: Option<String>,
60+
primary_layer_keys: Option<Vec<ButtonConfig>>,
61+
media_layer_keys: Option<Vec<ButtonConfig>>
62+
}
63+
64+
#[derive(Deserialize)]
65+
#[serde(rename_all = "PascalCase")]
66+
struct ButtonConfig {
67+
svg: Option<String>,
68+
text: Option<String>,
69+
action: Key
5970
}
6071

6172
struct Config {
62-
media_layer_default: bool,
6373
show_button_outlines: bool,
6474
enable_pixel_shift: bool,
65-
font_face: FontFace
75+
font_face: FontFace,
76+
layers: [FunctionLayer; 2]
6677
}
6778

6879
enum ButtonImage {
69-
Text(&'static str),
80+
Text(String),
7081
Svg(SvgHandle)
7182
}
7283

@@ -78,16 +89,27 @@ struct Button {
7889
}
7990

8091
impl Button {
81-
fn new_text(text: &'static str, action: Key) -> Button {
92+
fn with_config(cfg: ButtonConfig) -> Button {
93+
if let Some(text) = cfg.text {
94+
Button::new_text(text, cfg.action)
95+
} else if let Some(svg) = cfg.svg {
96+
Button::new_svg(&svg, cfg.action)
97+
} else {
98+
panic!("Invalid config, a button must have either Text or Svg")
99+
}
100+
}
101+
fn new_text(text: String, action: Key) -> Button {
82102
Button {
83103
action,
84104
active: false,
85105
changed: false,
86106
image: ButtonImage::Text(text)
87107
}
88108
}
89-
fn new_svg(path: &'static str, action: Key) -> Button {
90-
let svg = Loader::new().read_path(format!("/usr/share/tiny-dfr/{}.svg", path)).unwrap();
109+
fn new_svg(path: &str, action: Key) -> Button {
110+
let svg = Loader::new().read_path(format!("/etc/tiny-dfr/{}.svg", path)).or_else(|_| {
111+
Loader::new().read_path(format!("/usr/share/tiny-dfr/{}.svg", path))
112+
}).unwrap();
91113
Button {
92114
action,
93115
active: false,
@@ -126,11 +148,20 @@ impl Button {
126148
}
127149
}
128150

151+
#[derive(Default)]
129152
struct FunctionLayer {
130153
buttons: Vec<Button>
131154
}
132155

133156
impl FunctionLayer {
157+
fn with_config(cfg: Vec<ButtonConfig>) -> FunctionLayer {
158+
if cfg.is_empty() {
159+
panic!("Invalid configuration, layer has 0 buttons");
160+
}
161+
FunctionLayer {
162+
buttons: cfg.into_iter().map(Button::with_config).collect()
163+
}
164+
}
134165
fn draw(&mut self, config: &Config, surface: &Surface, pixel_shift: (f64, f64), complete_redraw: bool) -> Vec<ClipRect> {
135166
let c = Context::new(&surface).unwrap();
136167
let mut modified_regions = if complete_redraw {
@@ -293,19 +324,24 @@ fn load_config() -> Config {
293324
base.show_button_outlines = user.show_button_outlines.or(base.show_button_outlines);
294325
base.enable_pixel_shift = user.enable_pixel_shift.or(base.enable_pixel_shift);
295326
base.font_template = user.font_template.or(base.font_template);
327+
base.media_layer_keys = user.media_layer_keys.or(base.media_layer_keys);
328+
base.primary_layer_keys = user.primary_layer_keys.or(base.primary_layer_keys);
296329
};
330+
let media_layer = FunctionLayer::with_config(base.media_layer_keys.unwrap());
331+
let fkey_layer = FunctionLayer::with_config(base.primary_layer_keys.unwrap());
332+
let layers = if base.media_layer_default.unwrap(){ [media_layer, fkey_layer] } else { [fkey_layer, media_layer] };
297333
Config {
298-
media_layer_default: base.media_layer_default.unwrap(),
299334
show_button_outlines: base.show_button_outlines.unwrap(),
300335
enable_pixel_shift: base.enable_pixel_shift.unwrap(),
301-
font_face: load_font(&base.font_template.unwrap())
336+
font_face: load_font(&base.font_template.unwrap()),
337+
layers
302338
}
303339
}
304340

305341
fn main() {
306342
let mut uinput = UInputHandle::new(OpenOptions::new().write(true).open("/dev/uinput").unwrap());
307343
let mut backlight = BacklightManager::new();
308-
let config = load_config();
344+
let mut cfg = load_config();
309345
let mut pixel_shift = PixelShiftManager::new();
310346

311347
// drop privileges to input and video group
@@ -319,39 +355,6 @@ fn main() {
319355

320356
let mut surface = ImageSurface::create(Format::ARgb32, DFR_STRIDE, DFR_WIDTH).unwrap();
321357
let mut active_layer = 0;
322-
let fkey_layer = FunctionLayer {
323-
buttons: vec![
324-
Button::new_text("F1", Key::F1),
325-
Button::new_text("F2", Key::F2),
326-
Button::new_text("F3", Key::F3),
327-
Button::new_text("F4", Key::F4),
328-
Button::new_text("F5", Key::F5),
329-
Button::new_text("F6", Key::F6),
330-
Button::new_text("F7", Key::F7),
331-
Button::new_text("F8", Key::F8),
332-
Button::new_text("F9", Key::F9),
333-
Button::new_text("F10", Key::F10),
334-
Button::new_text("F11", Key::F11),
335-
Button::new_text("F12", Key::F12)
336-
]
337-
};
338-
let media_layer = FunctionLayer {
339-
buttons: vec![
340-
Button::new_svg("brightness_low", Key::BrightnessDown),
341-
Button::new_svg("brightness_high", Key::BrightnessUp),
342-
Button::new_svg("mic_off", Key::MicMute),
343-
Button::new_svg("search", Key::Search),
344-
Button::new_svg("backlight_low", Key::IllumDown),
345-
Button::new_svg("backlight_high", Key::IllumUp),
346-
Button::new_svg("fast_rewind", Key::PreviousSong),
347-
Button::new_svg("play_pause", Key::PlayPause),
348-
Button::new_svg("fast_forward", Key::NextSong),
349-
Button::new_svg("volume_off", Key::Mute),
350-
Button::new_svg("volume_down", Key::VolumeDown),
351-
Button::new_svg("volume_up", Key::VolumeUp)
352-
]
353-
};
354-
let mut layers = if config.media_layer_default { [media_layer, fkey_layer] } else { [fkey_layer, media_layer] };
355358
let mut needs_complete_redraw = true;
356359
let mut drm = DrmBackend::open_card().unwrap();
357360
let fb_info = drm.fb_info().unwrap();
@@ -365,6 +368,7 @@ fn main() {
365368
let pollfd_tb = PollFd::new(&fd_tb, PollFlags::POLLIN);
366369
let pollfd_main = PollFd::new(&fd_main, PollFlags::POLLIN);
367370
uinput.set_evbit(EventKind::Key).unwrap();
371+
let mut layers = mem::take(&mut cfg.layers);
368372
for layer in &layers {
369373
for button in &layer.buttons {
370374
uinput.set_keybit(button.action).unwrap();
@@ -392,7 +396,7 @@ fn main() {
392396
loop {
393397
let mut next_timeout_ms = TIMEOUT_MS;
394398

395-
if config.enable_pixel_shift {
399+
if cfg.enable_pixel_shift {
396400
let (pixel_shift_needs_redraw, pixel_shift_next_timeout_ms) = pixel_shift.update();
397401
if pixel_shift_needs_redraw {
398402
needs_complete_redraw = true;
@@ -401,12 +405,12 @@ fn main() {
401405
}
402406

403407
if needs_complete_redraw || layers[active_layer].buttons.iter().any(|b| b.changed) {
404-
let shift = if config.enable_pixel_shift {
408+
let shift = if cfg.enable_pixel_shift {
405409
pixel_shift.get()
406410
} else {
407411
(0.0, 0.0)
408412
};
409-
let clips = layers[active_layer].draw(&config, &surface, shift, needs_complete_redraw);
413+
let clips = layers[active_layer].draw(&cfg, &surface, shift, needs_complete_redraw);
410414
let data = surface.data().unwrap();
411415
drm.map().unwrap().as_mut()[..data.len()].copy_from_slice(&data);
412416
drm.dirty(&clips).unwrap();

0 commit comments

Comments
 (0)