Skip to content

Commit c2b3b23

Browse files
Do not fail if config is not present in /etc
1 parent 2cd8d45 commit c2b3b23

2 files changed

Lines changed: 139 additions & 95 deletions

File tree

src/config.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use std::fs::read_to_string;
2+
use anyhow::Error;
3+
use cairo::FontFace;
4+
use crate::FunctionLayer;
5+
use crate::fonts::{FontConfig, Pattern};
6+
use freetype::Library as FtLibrary;
7+
use input_linux::Key;
8+
use nix::errno::Errno;
9+
use nix::poll::{PollFd, PollFlags};
10+
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify, WatchDescriptor};
11+
use serde::Deserialize;
12+
13+
const USER_CFG_PATH: &'static str = "/etc/tiny-dfr/config.toml";
14+
15+
pub struct Config {
16+
pub show_button_outlines: bool,
17+
pub enable_pixel_shift: bool,
18+
pub font_face: FontFace,
19+
}
20+
21+
#[derive(Deserialize)]
22+
#[serde(rename_all = "PascalCase")]
23+
struct ConfigProxy {
24+
media_layer_default: Option<bool>,
25+
show_button_outlines: Option<bool>,
26+
enable_pixel_shift: Option<bool>,
27+
font_template: Option<String>,
28+
primary_layer_keys: Option<Vec<ButtonConfig>>,
29+
media_layer_keys: Option<Vec<ButtonConfig>>
30+
}
31+
32+
#[derive(Deserialize)]
33+
#[serde(rename_all = "PascalCase")]
34+
pub struct ButtonConfig {
35+
#[serde(alias = "Svg")]
36+
pub icon: Option<String>,
37+
pub text: Option<String>,
38+
pub action: Key
39+
}
40+
41+
fn load_font(name: &str) -> FontFace {
42+
let fontconfig = FontConfig::new();
43+
let mut pattern = Pattern::new(name);
44+
fontconfig.perform_substitutions(&mut pattern);
45+
let pat_match = fontconfig.match_pattern(&pattern);
46+
let file_name = pat_match.get_file_name();
47+
let file_idx = pat_match.get_font_index();
48+
let ft_library = FtLibrary::init().unwrap();
49+
let face = ft_library.new_face(file_name, file_idx).unwrap();
50+
FontFace::create_from_ft(&face).unwrap()
51+
}
52+
53+
fn load_config() -> (Config, [FunctionLayer; 2]) {
54+
let mut base = toml::from_str::<ConfigProxy>(&read_to_string("/usr/share/tiny-dfr/config.toml").unwrap()).unwrap();
55+
let user = read_to_string(USER_CFG_PATH).map_err::<Error, _>(|e| e.into())
56+
.and_then(|r| Ok(toml::from_str::<ConfigProxy>(&r)?));
57+
if let Ok(user) = user {
58+
base.media_layer_default = user.media_layer_default.or(base.media_layer_default);
59+
base.show_button_outlines = user.show_button_outlines.or(base.show_button_outlines);
60+
base.enable_pixel_shift = user.enable_pixel_shift.or(base.enable_pixel_shift);
61+
base.font_template = user.font_template.or(base.font_template);
62+
base.media_layer_keys = user.media_layer_keys.or(base.media_layer_keys);
63+
base.primary_layer_keys = user.primary_layer_keys.or(base.primary_layer_keys);
64+
};
65+
let media_layer = FunctionLayer::with_config(base.media_layer_keys.unwrap());
66+
let fkey_layer = FunctionLayer::with_config(base.primary_layer_keys.unwrap());
67+
let layers = if base.media_layer_default.unwrap(){ [media_layer, fkey_layer] } else { [fkey_layer, media_layer] };
68+
let cfg = Config {
69+
show_button_outlines: base.show_button_outlines.unwrap(),
70+
enable_pixel_shift: base.enable_pixel_shift.unwrap(),
71+
font_face: load_font(&base.font_template.unwrap()),
72+
};
73+
(cfg, layers)
74+
}
75+
76+
pub struct ConfigManager {
77+
inotify_fd: Inotify,
78+
watch_desc: Option<WatchDescriptor>
79+
}
80+
81+
fn arm_inotify(inotify_fd: &Inotify) -> Option<WatchDescriptor> {
82+
let flags = AddWatchFlags::IN_MOVED_TO | AddWatchFlags::IN_CLOSE | AddWatchFlags::IN_ONESHOT;
83+
match inotify_fd.add_watch(USER_CFG_PATH, flags) {
84+
Ok(wd) => Some(wd),
85+
Err(Errno::ENOENT) => None,
86+
e => Some(e.unwrap())
87+
}
88+
}
89+
90+
impl ConfigManager {
91+
pub fn new() -> ConfigManager {
92+
let inotify_fd = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
93+
let watch_desc = arm_inotify(&inotify_fd);
94+
ConfigManager {
95+
inotify_fd, watch_desc
96+
}
97+
}
98+
pub fn load_config(&self) -> (Config, [FunctionLayer; 2]) {
99+
load_config()
100+
}
101+
pub fn update_config(&mut self, cfg: &mut Config, layers: &mut [FunctionLayer; 2]) -> bool {
102+
if self.watch_desc.is_none() {
103+
self.watch_desc = arm_inotify(&self.inotify_fd);
104+
return false;
105+
}
106+
let evts = match self.inotify_fd.read_events() {
107+
Ok(e) => e,
108+
Err(Errno::EAGAIN) => Vec::new(),
109+
r => r.unwrap(),
110+
};
111+
let mut ret = false;
112+
for evt in evts {
113+
if evt.wd != self.watch_desc.unwrap() {
114+
continue
115+
}
116+
let parts = load_config();
117+
*cfg = parts.0;
118+
*layers = parts.1;
119+
ret = true;
120+
self.watch_desc = arm_inotify(&self.inotify_fd);
121+
}
122+
ret
123+
}
124+
pub fn pollfd(&self) -> PollFd {
125+
PollFd::new(&self.inotify_fd, PollFlags::POLLIN)
126+
}
127+
}

src/main.rs

Lines changed: 12 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
fs::{File, OpenOptions, read_to_string},
2+
fs::{File, OpenOptions},
33
os::{
44
fd::{AsRawFd, AsFd},
55
unix::{io::OwnedFd, fs::OpenOptionsExt}
@@ -9,10 +9,10 @@ use std::{
99
cmp::min,
1010
panic::{self, AssertUnwindSafe}
1111
};
12-
use cairo::{ImageSurface, Format, Context, Surface, Rectangle, FontFace, Antialias};
12+
use cairo::{ImageSurface, Format, Context, Surface, Rectangle, Antialias};
1313
use rsvg::{Loader, CairoRenderer, SvgHandle};
1414
use drm::control::ClipRect;
15-
use anyhow::{Error, Result};
15+
use anyhow::Result;
1616
use input::{
1717
Libinput, LibinputInterface, Device as InputDevice,
1818
event::{
@@ -26,25 +26,21 @@ use input_linux::{uinput::UInputHandle, EventKind, Key, SynchronizeKind};
2626
use input_linux_sys::{uinput_setup, input_id, timeval, input_event};
2727
use nix::{
2828
poll::{poll, PollFd, PollFlags},
29-
sys::{
30-
signal::{Signal, SigSet},
31-
inotify::{AddWatchFlags, InitFlags, Inotify, WatchDescriptor}
32-
},
33-
errno::Errno
29+
sys::signal::{Signal, SigSet},
3430
};
3531
use privdrop::PrivDrop;
36-
use serde::Deserialize;
37-
use freetype::Library as FtLibrary;
3832

3933
mod backlight;
4034
mod display;
4135
mod pixel_shift;
4236
mod fonts;
37+
mod config;
4338

4439
use backlight::BacklightManager;
4540
use display::DrmBackend;
4641
use pixel_shift::{PixelShiftManager, PIXEL_SHIFT_WIDTH_PX};
47-
use fonts::{FontConfig, Pattern};
42+
use config::{ButtonConfig, Config};
43+
use crate::config::ConfigManager;
4844

4945
const DFR_WIDTH: i32 = 2008;
5046
const DFR_HEIGHT: i32 = 60;
@@ -54,33 +50,6 @@ const BUTTON_COLOR_INACTIVE: f64 = 0.200;
5450
const BUTTON_COLOR_ACTIVE: f64 = 0.400;
5551
const ICON_SIZE: i32 = 48;
5652
const TIMEOUT_MS: i32 = 10 * 1000;
57-
const USER_CFG_PATH: &'static str = "/etc/tiny-dfr/config.toml";
58-
59-
#[derive(Deserialize)]
60-
#[serde(rename_all = "PascalCase")]
61-
struct ConfigProxy {
62-
media_layer_default: Option<bool>,
63-
show_button_outlines: Option<bool>,
64-
enable_pixel_shift: Option<bool>,
65-
font_template: Option<String>,
66-
primary_layer_keys: Option<Vec<ButtonConfig>>,
67-
media_layer_keys: Option<Vec<ButtonConfig>>
68-
}
69-
70-
#[derive(Deserialize)]
71-
#[serde(rename_all = "PascalCase")]
72-
struct ButtonConfig {
73-
#[serde(alias = "Svg")]
74-
icon: Option<String>,
75-
text: Option<String>,
76-
action: Key
77-
}
78-
79-
struct Config {
80-
show_button_outlines: bool,
81-
enable_pixel_shift: bool,
82-
font_face: FontFace,
83-
}
8453

8554
enum ButtonImage {
8655
Text(String),
@@ -184,7 +153,7 @@ impl Button {
184153
}
185154

186155
#[derive(Default)]
187-
struct FunctionLayer {
156+
pub struct FunctionLayer {
188157
buttons: Vec<Button>
189158
}
190159

@@ -338,41 +307,6 @@ fn toggle_key<F>(uinput: &mut UInputHandle<F>, code: Key, value: i32) where F: A
338307
emit(uinput, EventKind::Synchronize, SynchronizeKind::Report as u16, 0);
339308
}
340309

341-
fn load_font(name: &str) -> FontFace {
342-
let fontconfig = FontConfig::new();
343-
let mut pattern = Pattern::new(name);
344-
fontconfig.perform_substitutions(&mut pattern);
345-
let pat_match = fontconfig.match_pattern(&pattern);
346-
let file_name = pat_match.get_file_name();
347-
let file_idx = pat_match.get_font_index();
348-
let ft_library = FtLibrary::init().unwrap();
349-
let face = ft_library.new_face(file_name, file_idx).unwrap();
350-
FontFace::create_from_ft(&face).unwrap()
351-
}
352-
353-
fn load_config() -> (Config, [FunctionLayer; 2]) {
354-
let mut base = toml::from_str::<ConfigProxy>(&read_to_string("/usr/share/tiny-dfr/config.toml").unwrap()).unwrap();
355-
let user = read_to_string(USER_CFG_PATH).map_err::<Error, _>(|e| e.into())
356-
.and_then(|r| Ok(toml::from_str::<ConfigProxy>(&r)?));
357-
if let Ok(user) = user {
358-
base.media_layer_default = user.media_layer_default.or(base.media_layer_default);
359-
base.show_button_outlines = user.show_button_outlines.or(base.show_button_outlines);
360-
base.enable_pixel_shift = user.enable_pixel_shift.or(base.enable_pixel_shift);
361-
base.font_template = user.font_template.or(base.font_template);
362-
base.media_layer_keys = user.media_layer_keys.or(base.media_layer_keys);
363-
base.primary_layer_keys = user.primary_layer_keys.or(base.primary_layer_keys);
364-
};
365-
let media_layer = FunctionLayer::with_config(base.media_layer_keys.unwrap());
366-
let fkey_layer = FunctionLayer::with_config(base.primary_layer_keys.unwrap());
367-
let layers = if base.media_layer_default.unwrap(){ [media_layer, fkey_layer] } else { [fkey_layer, media_layer] };
368-
let cfg = Config {
369-
show_button_outlines: base.show_button_outlines.unwrap(),
370-
enable_pixel_shift: base.enable_pixel_shift.unwrap(),
371-
font_face: load_font(&base.font_template.unwrap()),
372-
};
373-
(cfg, layers)
374-
}
375-
376310
fn main() {
377311
let mut drm = DrmBackend::open_card().unwrap();
378312
let _ = panic::catch_unwind(AssertUnwindSafe(|| {
@@ -400,15 +334,11 @@ fn main() {
400334
sigset.wait().unwrap();
401335
}
402336

403-
fn arm_inotify(inotify_fd: &Inotify) -> WatchDescriptor {
404-
let flags = AddWatchFlags::IN_MOVED_TO | AddWatchFlags::IN_CLOSE | AddWatchFlags::IN_ONESHOT;
405-
inotify_fd.add_watch(USER_CFG_PATH, flags).unwrap()
406-
}
407-
408337
fn real_main(drm: &mut DrmBackend) {
409338
let mut uinput = UInputHandle::new(OpenOptions::new().write(true).open("/dev/uinput").unwrap());
410339
let mut backlight = BacklightManager::new();
411-
let (mut cfg, mut layers) = load_config();
340+
let mut cfg_mgr = ConfigManager::new();
341+
let (mut cfg, mut layers) = cfg_mgr.load_config();
412342
let mut pixel_shift = PixelShiftManager::new();
413343

414344
// drop privileges to input and video group
@@ -438,9 +368,6 @@ fn real_main(drm: &mut DrmBackend) {
438368
uinput.set_keybit(button.action).unwrap();
439369
}
440370
}
441-
let inotify_fd = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
442-
let mut cfg_watch_desc = arm_inotify(&inotify_fd);
443-
let pollfd_notify = PollFd::new(&inotify_fd, PollFlags::POLLIN);
444371
let mut dev_name_c = [0 as c_char; 80];
445372
let dev_name = "Dynamic Function Row Virtual Input Device".as_bytes();
446373
for i in 0..dev_name.len() {
@@ -461,19 +388,9 @@ fn real_main(drm: &mut DrmBackend) {
461388
let mut digitizer: Option<InputDevice> = None;
462389
let mut touches = HashMap::new();
463390
loop {
464-
let evts = match inotify_fd.read_events() {
465-
Ok(e) => e,
466-
Err(Errno::EAGAIN) => Vec::new(),
467-
r => r.unwrap(),
468-
};
469-
for evt in evts {
470-
if evt.wd != cfg_watch_desc {
471-
continue
472-
}
473-
(cfg, layers) = load_config();
391+
if cfg_mgr.update_config(&mut cfg, &mut layers) {
474392
active_layer = 0;
475393
needs_complete_redraw = true;
476-
cfg_watch_desc = arm_inotify(&inotify_fd);
477394
}
478395

479396
let mut next_timeout_ms = TIMEOUT_MS;
@@ -498,7 +415,7 @@ fn real_main(drm: &mut DrmBackend) {
498415
needs_complete_redraw = false;
499416
}
500417

501-
poll(&mut [pollfd_tb, pollfd_main, pollfd_notify], next_timeout_ms).unwrap();
418+
poll(&mut [pollfd_tb, pollfd_main, cfg_mgr.pollfd()], next_timeout_ms).unwrap();
502419
input_tb.dispatch().unwrap();
503420
input_main.dispatch().unwrap();
504421
for event in &mut input_tb.clone().chain(input_main.clone()) {

0 commit comments

Comments
 (0)