Skip to content

Commit 17907b3

Browse files
committed
improv: Better abstract backend crate with a Backend object
1 parent 7b88c32 commit 17907b3

File tree

5 files changed

+172
-83
lines changed

5 files changed

+172
-83
lines changed

backend/src/daemon/server.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,14 @@ impl<R: Read, W: Write> DaemonServer<R, W> {
6161
}
6262
};
6363

64-
let self_ = Self {
64+
Ok(Self {
6565
hidapi: RefCell::new(hidapi),
6666
running: Cell::new(true),
6767
read: BufReader::new(read),
6868
write,
6969
boards: RefCell::new(boards),
7070
board_ids: RefCell::new(board_ids),
71-
};
72-
73-
self_.refresh()?;
74-
75-
Ok(self_)
71+
})
7672
}
7773

7874
fn have_device(&self, info: &DeviceInfo) -> bool {

backend/src/lib.rs

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
#[macro_use]
22
extern crate log;
33

4+
use glib::{
5+
prelude::*,
6+
subclass::{prelude::*, Signal},
7+
};
8+
use once_cell::sync::Lazy;
9+
use std::{
10+
cell::RefCell,
11+
collections::{HashMap, HashSet},
12+
iter::FromIterator,
13+
process,
14+
rc::Rc,
15+
};
16+
417
mod board;
518
mod color;
619
mod daemon;
@@ -13,6 +26,127 @@ mod mode;
1326
mod rect;
1427

1528
pub use self::{
16-
board::*, color::*, daemon::*, deref_cell::*, key::*, keymap::*, layer::*, layout::*, mode::*,
17-
rect::*,
29+
board::*, color::*, deref_cell::*, key::*, keymap::*, layer::*, layout::*, mode::*, rect::*,
1830
};
31+
use daemon::*;
32+
33+
#[derive(Default)]
34+
pub struct BackendInner {
35+
daemon: DerefCell<Rc<dyn Daemon>>,
36+
ids: RefCell<HashSet<BoardId>>,
37+
boards: RefCell<HashMap<BoardId, Board>>,
38+
}
39+
40+
#[glib::object_subclass]
41+
impl ObjectSubclass for BackendInner {
42+
const NAME: &'static str = "S76KeyboardBackend";
43+
type ParentType = glib::Object;
44+
type Type = Backend;
45+
}
46+
47+
impl ObjectImpl for BackendInner {
48+
fn signals() -> &'static [Signal] {
49+
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
50+
vec![
51+
Signal::builder(
52+
"board-added",
53+
&[Board::static_type().into()],
54+
glib::Type::UNIT.into(),
55+
)
56+
.build(),
57+
Signal::builder(
58+
"board-removed",
59+
&[Board::static_type().into()],
60+
glib::Type::UNIT.into(),
61+
)
62+
.build(),
63+
]
64+
});
65+
SIGNALS.as_ref()
66+
}
67+
}
68+
69+
glib::wrapper! {
70+
pub struct Backend(ObjectSubclass<BackendInner>);
71+
}
72+
73+
impl Backend {
74+
fn new_internal<T: Daemon + 'static>(daemon: T) -> Result<Self, String> {
75+
let self_ = glib::Object::new::<Self>(&[]).unwrap();
76+
self_.inner().daemon.set(Rc::new(daemon));
77+
Ok(self_)
78+
}
79+
80+
pub fn new_dummy(board_names: Vec<String>) -> Result<Self, String> {
81+
Self::new_internal(DaemonDummy::new(board_names))
82+
}
83+
84+
pub fn new_s76power() -> Result<Self, String> {
85+
Self::new_internal(DaemonS76Power::new()?)
86+
}
87+
88+
pub fn new_pkexec() -> Result<Self, String> {
89+
Self::new_internal(DaemonClient::new_pkexec())
90+
}
91+
92+
pub fn new() -> Result<Self, String> {
93+
Self::new_internal(DaemonServer::new_stdio()?)
94+
}
95+
96+
fn inner(&self) -> &BackendInner {
97+
BackendInner::from_instance(self)
98+
}
99+
100+
pub fn refresh(&self) {
101+
if let Err(err) = self.inner().daemon.refresh() {
102+
error!("Failed to refresh boards: {}", err);
103+
}
104+
105+
let mut ids = self.inner().ids.borrow_mut();
106+
let new_ids = HashSet::from_iter(self.inner().daemon.boards().unwrap().into_iter());
107+
108+
let mut boards = self.inner().boards.borrow_mut();
109+
110+
// Removed boards
111+
for i in ids.difference(&new_ids) {
112+
self.emit_by_name("board-removed", &[&boards[i]]).unwrap();
113+
boards.remove(i);
114+
}
115+
116+
// Added boards
117+
// TODO: emit in correct order
118+
for i in new_ids.difference(&ids) {
119+
match Board::new(self.inner().daemon.clone(), *i) {
120+
Ok(board) => {
121+
boards.insert(*i, board.clone());
122+
self.emit_by_name("board-added", &[&board]).unwrap();
123+
}
124+
Err(err) => error!("Failed to add board: {}", err),
125+
}
126+
}
127+
128+
*ids = new_ids;
129+
}
130+
131+
pub fn connect_board_added<F: Fn(Board) + 'static>(&self, cb: F) {
132+
self.connect_local("board-added", false, move |values| {
133+
cb(values[1].get::<Board>().unwrap().unwrap());
134+
None
135+
})
136+
.unwrap();
137+
}
138+
139+
pub fn connect_board_removed<F: Fn(Board) + 'static>(&self, cb: F) {
140+
self.connect_local("board-removed", false, move |values| {
141+
cb(values[1].get::<Board>().unwrap().unwrap());
142+
None
143+
})
144+
.unwrap();
145+
}
146+
}
147+
148+
pub fn run_daemon() -> ! {
149+
let server = DaemonServer::new_stdio().expect("Failed to create server");
150+
server.run().expect("Failed to run server");
151+
process::exit(0)
152+
}

src/application/main_window.rs

Lines changed: 26 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@ use gtk::prelude::*;
44
use gtk::subclass::prelude::*;
55
use std::{
66
cell::RefCell,
7-
collections::HashSet,
8-
rc::Rc,
97
sync::atomic::{AtomicUsize, Ordering},
108
};
119

1210
use super::{shortcuts_window, ConfiguratorApp, Keyboard, KeyboardLayer, Page, Picker};
1311
use crate::DerefCell;
14-
use backend::{Board, Daemon, DaemonClient, DaemonDummy, DaemonServer};
12+
use backend::{Backend, Board};
1513

1614
#[derive(Default)]
1715
pub struct MainWindowInner {
18-
daemon: DerefCell<Rc<dyn Daemon>>,
16+
backend: DerefCell<Backend>,
1917
back_button: DerefCell<gtk::Button>,
2018
count: AtomicUsize,
2119
header_bar: DerefCell<gtk::HeaderBar>,
@@ -167,65 +165,34 @@ impl MainWindow {
167165
let window: Self = glib::Object::new(&[]).unwrap();
168166
app.add_window(&window);
169167

170-
let daemon = daemon();
171-
172-
for i in daemon.boards().expect("Failed to load boards") {
173-
match Board::new(daemon.clone(), i) {
174-
Ok(board) => window.add_keyboard(board),
175-
Err(err) => error!("{}", err),
176-
}
177-
}
168+
let backend = cascade! {
169+
daemon();
170+
..connect_board_added(clone!(@weak window => move |board| window.add_keyboard(board)));
171+
..connect_board_removed(clone!(@weak window => move |board| {
172+
let mut boards = window.inner().keyboards.borrow_mut();
173+
if let Some(idx) = boards.iter().position(|(kb, _)| kb.board() == &board) {
174+
let (keyboard, row) = boards.remove(idx);
175+
window.inner().stack.remove(&keyboard);
176+
window.inner().keyboard_list_box.remove(&row);
177+
}
178+
}));
179+
..refresh();
180+
};
178181

179182
let phony_board_names = app.phony_board_names().to_vec();
180183
if !phony_board_names.is_empty() {
181-
let daemon = Rc::new(DaemonDummy::new(phony_board_names));
182-
183-
for i in daemon.boards().unwrap() {
184-
match Board::new(daemon.clone(), i) {
185-
Ok(board) => window.add_keyboard(board),
186-
Err(err) => error!("{}", err),
187-
}
188-
}
184+
let backend = Backend::new_dummy(phony_board_names).unwrap();
185+
backend.connect_board_added(
186+
clone!(@weak window => move |board| window.add_keyboard(board)),
187+
);
188+
backend.refresh();
189189
}
190190

191-
window.inner().daemon.set(daemon);
191+
window.inner().backend.set(backend);
192192
glib::timeout_add_seconds_local(
193193
1,
194194
clone!(@weak window => move || {
195-
let daemon = &*window.inner().daemon;
196-
197-
if let Err(err) = daemon.refresh() {
198-
error!("Failed to refresh boards: {}", err);
199-
}
200-
201-
let boards = match daemon.boards() {
202-
Ok(boards) => boards,
203-
Err(_) => return glib::Continue(true),
204-
};
205-
206-
// Remove boards that aren't detected now
207-
let mut ids = HashSet::new();
208-
window.inner().keyboards.borrow_mut().retain(|(keyboard, row)| {
209-
let board = keyboard.board().board();
210-
ids.insert(board);
211-
if boards.iter().find(|i| **i == board).is_none() {
212-
window.inner().stack.remove(keyboard);
213-
window.inner().keyboard_list_box.remove(row);
214-
return false;
215-
}
216-
true
217-
});
218-
219-
// Add new boards
220-
for i in boards {
221-
if !ids.contains(&i) {
222-
match Board::new(daemon.clone(), i) {
223-
Ok(board) => window.add_keyboard(board),
224-
Err(err) => error!("{}", err),
225-
}
226-
}
227-
}
228-
195+
window.inner().backend.refresh();
229196
glib::Continue(true)
230197
}),
231198
);
@@ -319,14 +286,15 @@ impl MainWindow {
319286
}
320287

321288
#[cfg(target_os = "linux")]
322-
fn daemon() -> Rc<dyn Daemon> {
289+
fn daemon() -> Backend {
323290
if unsafe { libc::geteuid() == 0 } {
324291
info!("Already running as root");
325-
Rc::new(DaemonServer::new_stdio().expect("Failed to create server"))
292+
Backend::new()
326293
} else {
327294
info!("Not running as root, spawning daemon with pkexec");
328-
Rc::new(DaemonClient::new_pkexec())
295+
Backend::new_pkexec()
329296
}
297+
.expect("Failed to create server")
330298
}
331299

332300
#[cfg(not(target_os = "linux"))]

src/keyboard_backlight_widget.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
use cascade::cascade;
44
use glib::clone;
55
use gtk::prelude::*;
6-
use std::rc::Rc;
76

87
use crate::{KeyboardColor, KeyboardColorIndex};
9-
use backend::{Board, Daemon, DaemonS76Power};
8+
use backend::{Backend, Board};
109

1110
pub fn keyboard_backlight_widget() -> gtk::Widget {
1211
let stack = cascade! {
@@ -34,17 +33,11 @@ pub fn keyboard_backlight_widget() -> gtk::Widget {
3433
}
3534

3635
fn add_boards(stack: &gtk::Stack) -> Result<(), String> {
37-
let daemon = Rc::new(DaemonS76Power::new()?);
38-
39-
for i in daemon.boards()? {
40-
match Board::new(daemon.clone(), i) {
41-
Ok(board) => {
42-
let name = board.model().to_owned();
43-
stack.add_titled(&page(board), &name, &name);
44-
}
45-
Err(err) => error!("{}", err),
46-
}
47-
}
36+
let backend = Backend::new_s76power()?;
37+
backend.connect_board_added(clone!(@weak stack => move |board| {
38+
let name = board.model().to_owned();
39+
stack.add_titled(&page(board), &name, &name);
40+
}));
4841

4942
Ok(())
5043
}

src/main.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use std::env;
44
use std::process;
5-
use system76_keyboard_configurator::{application, backend::DaemonServer};
5+
use system76_keyboard_configurator::application;
66

77
fn main() {
88
env_logger::Builder::from_env(
@@ -15,9 +15,7 @@ fn main() {
1515
let args = env::args().collect::<Vec<_>>();
1616
for arg in args.iter().skip(1) {
1717
if arg.as_str() == "--daemon" {
18-
let server = DaemonServer::new_stdio().expect("Failed to create server");
19-
server.run().expect("Failed to run server");
20-
return;
18+
backend::run_daemon();
2119
}
2220
}
2321

0 commit comments

Comments
 (0)