mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-11-29 05:19:03 -05:00
update cursive
`cursive` now requires `Send + Sync` bounds, so I had to switch from `Rc<RefCell<...>>` to `Arc<Mutex<...>>`. I also coalesced some lock calls together, hopefully without introducing any deadlocks. I don't see any calls into the UI that would need the model while these are held, and it seemed fine in a quick test.
This commit is contained in:
@@ -9,11 +9,9 @@ use cursive::view::Scrollable;
|
||||
use cursive::Cursive;
|
||||
use cursive::{views, With};
|
||||
use db::writer;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use super::tab_complete::TabCompleteEditView;
|
||||
@@ -58,10 +56,8 @@ fn update_limits(model: &Model, siv: &mut Cursive) {
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_limit(model: &RefCell<Model>, siv: &mut Cursive, id: i32, content: &str) {
|
||||
fn edit_limit(model: &mut Model, siv: &mut Cursive, id: i32, content: &str) {
|
||||
debug!("on_edit called for id {}", id);
|
||||
let mut model = model.borrow_mut();
|
||||
let model: &mut Model = &mut model;
|
||||
let stream = model.streams.get_mut(&id).unwrap();
|
||||
let new_value = decode_size(content).ok();
|
||||
let delta = new_value.unwrap_or(0) - stream.retain.unwrap_or(0);
|
||||
@@ -96,14 +92,12 @@ fn edit_limit(model: &RefCell<Model>, siv: &mut Cursive, id: i32, content: &str)
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_record(model: &RefCell<Model>, id: i32, record: bool) {
|
||||
let mut model = model.borrow_mut();
|
||||
let model: &mut Model = &mut model;
|
||||
fn edit_record(model: &mut Model, id: i32, record: bool) {
|
||||
let stream = model.streams.get_mut(&id).unwrap();
|
||||
stream.record = record;
|
||||
}
|
||||
|
||||
fn confirm_deletion(model: &RefCell<Model>, siv: &mut Cursive, to_delete: i64) {
|
||||
fn confirm_deletion(model: &Mutex<Model>, siv: &mut Cursive, to_delete: i64) {
|
||||
let typed = siv
|
||||
.find_name::<views::EditView>("confirm")
|
||||
.unwrap()
|
||||
@@ -124,8 +118,8 @@ fn confirm_deletion(model: &RefCell<Model>, siv: &mut Cursive, to_delete: i64) {
|
||||
}
|
||||
}
|
||||
|
||||
fn actually_delete(model: &RefCell<Model>, siv: &mut Cursive) {
|
||||
let model = &*model.borrow();
|
||||
fn actually_delete(model: &Mutex<Model>, siv: &mut Cursive) {
|
||||
let model = model.lock().unwrap();
|
||||
let new_limits: Vec<_> = model
|
||||
.streams
|
||||
.iter()
|
||||
@@ -147,20 +141,21 @@ fn actually_delete(model: &RefCell<Model>, siv: &mut Cursive) {
|
||||
.dismiss_button("Abort"),
|
||||
);
|
||||
} else {
|
||||
update_limits(model, siv);
|
||||
update_limits(&model, siv);
|
||||
}
|
||||
}
|
||||
|
||||
fn press_change(model: &Rc<RefCell<Model>>, siv: &mut Cursive) {
|
||||
if model.borrow().errors > 0 {
|
||||
return;
|
||||
}
|
||||
let to_delete = model
|
||||
.borrow()
|
||||
.streams
|
||||
.values()
|
||||
.map(|s| ::std::cmp::max(s.used - s.retain.unwrap(), 0))
|
||||
.sum();
|
||||
fn press_change(model: &Arc<Mutex<Model>>, siv: &mut Cursive) {
|
||||
let to_delete = {
|
||||
let l = model.lock().unwrap();
|
||||
if l.errors > 0 {
|
||||
return;
|
||||
}
|
||||
l.streams
|
||||
.values()
|
||||
.map(|s| ::std::cmp::max(s.used - s.retain.unwrap(), 0))
|
||||
.sum()
|
||||
};
|
||||
debug!("change press, to_delete={}", to_delete);
|
||||
if to_delete > 0 {
|
||||
let prompt = format!(
|
||||
@@ -190,7 +185,7 @@ fn press_change(model: &Rc<RefCell<Model>>, siv: &mut Cursive) {
|
||||
siv.add_layer(dialog);
|
||||
} else {
|
||||
siv.pop_layer();
|
||||
update_limits(&model.borrow(), siv);
|
||||
update_limits(&model.lock().unwrap(), siv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +362,7 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
|
||||
fs_capacity = stat.block_size() as i64 * stat.blocks_available() as i64 + total_used;
|
||||
path = dir.path.clone();
|
||||
}
|
||||
Rc::new(RefCell::new(Model {
|
||||
Arc::new(Mutex::new(Model {
|
||||
dir_id,
|
||||
db: db.clone(),
|
||||
fs_capacity,
|
||||
@@ -389,12 +384,13 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
|
||||
.child(views::TextView::new("usage").fixed_width(BYTES_WIDTH))
|
||||
.child(views::TextView::new("limit").fixed_width(BYTES_WIDTH)),
|
||||
);
|
||||
for (&id, stream) in &model.borrow().streams {
|
||||
let l = model.lock().unwrap();
|
||||
for (&id, stream) in &l.streams {
|
||||
let mut record_cb = views::Checkbox::new();
|
||||
record_cb.set_checked(stream.record);
|
||||
record_cb.set_on_change({
|
||||
let model = model.clone();
|
||||
move |_siv, record| edit_record(&model, id, record)
|
||||
move |_siv, record| edit_record(&mut model.lock().unwrap(), id, record)
|
||||
});
|
||||
list.add_child(
|
||||
&stream.label,
|
||||
@@ -406,7 +402,9 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
|
||||
.content(encode_size(stream.retain.unwrap()))
|
||||
.on_edit({
|
||||
let model = model.clone();
|
||||
move |siv, content, _pos| edit_limit(&model, siv, id, content)
|
||||
move |siv, content, _pos| {
|
||||
edit_limit(&mut model.lock().unwrap(), siv, id, content)
|
||||
}
|
||||
})
|
||||
.on_submit({
|
||||
let model = model.clone();
|
||||
@@ -421,17 +419,14 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
|
||||
),
|
||||
);
|
||||
}
|
||||
let over = model.borrow().total_retain > model.borrow().fs_capacity;
|
||||
let over = l.total_retain > l.fs_capacity;
|
||||
list.add_child(
|
||||
"total",
|
||||
views::LinearLayout::horizontal()
|
||||
.child(views::DummyView {}.fixed_width(RECORD_WIDTH))
|
||||
.child(views::TextView::new(encode_size(l.total_used)).fixed_width(BYTES_WIDTH))
|
||||
.child(
|
||||
views::TextView::new(encode_size(model.borrow().total_used))
|
||||
.fixed_width(BYTES_WIDTH),
|
||||
)
|
||||
.child(
|
||||
views::TextView::new(encode_size(model.borrow().total_retain))
|
||||
views::TextView::new(encode_size(l.total_retain))
|
||||
.with_name("total_retain")
|
||||
.fixed_width(BYTES_WIDTH),
|
||||
)
|
||||
@@ -442,11 +437,9 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
|
||||
views::LinearLayout::horizontal()
|
||||
.child(views::DummyView {}.fixed_width(RECORD_WIDTH))
|
||||
.child(views::DummyView {}.fixed_width(BYTES_WIDTH))
|
||||
.child(
|
||||
views::TextView::new(encode_size(model.borrow().fs_capacity))
|
||||
.fixed_width(BYTES_WIDTH),
|
||||
),
|
||||
.child(views::TextView::new(encode_size(l.fs_capacity)).fixed_width(BYTES_WIDTH)),
|
||||
);
|
||||
drop(l);
|
||||
let mut change_button = views::Button::new("Change", move |siv| press_change(&model, siv));
|
||||
change_button.set_enabled(!over);
|
||||
let mut buttons = views::LinearLayout::horizontal().child(views::DummyView.full_width());
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use cursive::{
|
||||
direction::Direction,
|
||||
@@ -13,46 +13,49 @@ use cursive::{
|
||||
Printer, Rect, Vec2, View, With,
|
||||
};
|
||||
|
||||
type TabCompleteFn = Rc<dyn Fn(&str) -> Vec<String>>;
|
||||
type TabCompleteFn = Arc<dyn Fn(&str) -> Vec<String> + Send + Sync>;
|
||||
|
||||
pub struct TabCompleteEditView {
|
||||
edit_view: Rc<RefCell<EditView>>,
|
||||
edit_view: Arc<Mutex<EditView>>,
|
||||
tab_completer: Option<TabCompleteFn>,
|
||||
}
|
||||
|
||||
impl TabCompleteEditView {
|
||||
pub fn new(edit_view: EditView) -> Self {
|
||||
Self {
|
||||
edit_view: Rc::new(RefCell::new(edit_view)),
|
||||
edit_view: Arc::new(Mutex::new(edit_view)),
|
||||
tab_completer: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_tab_complete(mut self, handler: impl Fn(&str) -> Vec<String> + 'static) -> Self {
|
||||
self.tab_completer = Some(Rc::new(handler));
|
||||
pub fn on_tab_complete(
|
||||
mut self,
|
||||
handler: impl Fn(&str) -> Vec<String> + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
self.tab_completer = Some(Arc::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_content(&self) -> Rc<String> {
|
||||
self.edit_view.borrow_mut().get_content()
|
||||
pub fn get_content(&self) -> Arc<String> {
|
||||
self.edit_view.lock().unwrap().get_content()
|
||||
}
|
||||
}
|
||||
|
||||
impl View for TabCompleteEditView {
|
||||
fn draw(&self, printer: &Printer) {
|
||||
self.edit_view.borrow().draw(printer)
|
||||
self.edit_view.lock().unwrap().draw(printer)
|
||||
}
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.edit_view.borrow_mut().layout(size)
|
||||
self.edit_view.lock().unwrap().layout(size)
|
||||
}
|
||||
|
||||
fn take_focus(&mut self, source: Direction) -> Result<EventResult, CannotFocus> {
|
||||
self.edit_view.borrow_mut().take_focus(source)
|
||||
self.edit_view.lock().unwrap().take_focus(source)
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
if !self.edit_view.borrow().is_enabled() {
|
||||
if !self.edit_view.lock().unwrap().is_enabled() {
|
||||
return EventResult::Ignored;
|
||||
}
|
||||
|
||||
@@ -63,32 +66,32 @@ impl View for TabCompleteEditView {
|
||||
EventResult::consumed()
|
||||
}
|
||||
} else {
|
||||
self.edit_view.borrow_mut().on_event(event)
|
||||
self.edit_view.lock().unwrap().on_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
fn important_area(&self, view_size: Vec2) -> Rect {
|
||||
self.edit_view.borrow().important_area(view_size)
|
||||
self.edit_view.lock().unwrap().important_area(view_size)
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_complete(
|
||||
edit_view: Rc<RefCell<EditView>>,
|
||||
edit_view: Arc<Mutex<EditView>>,
|
||||
tab_completer: TabCompleteFn,
|
||||
autofill_one: bool,
|
||||
) -> EventResult {
|
||||
let completions = tab_completer(edit_view.borrow().get_content().as_str());
|
||||
let completions = tab_completer(edit_view.lock().unwrap().get_content().as_str());
|
||||
EventResult::with_cb_once(move |siv| match *completions {
|
||||
[] => {}
|
||||
[ref completion] if autofill_one => edit_view.borrow_mut().set_content(completion)(siv),
|
||||
[ref completion] if autofill_one => edit_view.lock().unwrap().set_content(completion)(siv),
|
||||
[..] => {
|
||||
siv.add_layer(TabCompletePopup {
|
||||
popup: views::MenuPopup::new(Rc::new({
|
||||
popup: views::MenuPopup::new(Arc::new({
|
||||
menu::Tree::new().with(|tree| {
|
||||
for completion in completions {
|
||||
let edit_view = edit_view.clone();
|
||||
tree.add_leaf(completion.clone(), move |siv| {
|
||||
edit_view.borrow_mut().set_content(&completion)(siv)
|
||||
edit_view.lock().unwrap().set_content(&completion)(siv)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -101,7 +104,7 @@ fn tab_complete(
|
||||
}
|
||||
|
||||
struct TabCompletePopup {
|
||||
edit_view: Rc<RefCell<EditView>>,
|
||||
edit_view: Arc<Mutex<EditView>>,
|
||||
popup: MenuPopup,
|
||||
tab_completer: TabCompleteFn,
|
||||
}
|
||||
@@ -111,7 +114,7 @@ impl TabCompletePopup {
|
||||
let tab_completer = self.tab_completer.clone();
|
||||
EventResult::with_cb_once(move |s| {
|
||||
s.pop_layer();
|
||||
edit_view.borrow_mut().on_event(event).process(s);
|
||||
edit_view.lock().unwrap().on_event(event).process(s);
|
||||
tab_complete(edit_view, tab_completer, false).process(s);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ fn edit_user_dialog(db: &Arc<db::Database>, siv: &mut Cursive, item: Option<i32>
|
||||
] {
|
||||
let mut checkbox = views::Checkbox::new();
|
||||
checkbox.set_checked(*b);
|
||||
perms.add_child(name, checkbox.with_name(format!("perm_{name}")));
|
||||
perms.add_child(*name, checkbox.with_name(format!("perm_{name}")));
|
||||
}
|
||||
layout.add_child(perms);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user