feat: reduced max_width to 100 and ran cargo fmt

This commit is contained in:
Byson94
2025-08-23 08:54:42 +05:30
parent e1387caad4
commit 44cef22183
51 changed files with 1345 additions and 460 deletions

View File

@@ -3,11 +3,16 @@ use crate::{
daemon_response::DaemonResponseSender, daemon_response::DaemonResponseSender,
display_backend::DisplayBackend, display_backend::DisplayBackend,
error_handling_ctx, error_handling_ctx,
gtk::prelude::{ContainerExt, CssProviderExt, GtkWindowExt, MonitorExt, StyleContextExt, WidgetExt}, gtk::prelude::{
ContainerExt, CssProviderExt, GtkWindowExt, MonitorExt, StyleContextExt, WidgetExt,
},
paths::EwwPaths, paths::EwwPaths,
widgets::window::Window, widgets::window::Window,
// dynval::DynVal, // dynval::DynVal,
widgets::{build_widget::build_gtk_widget, build_widget::WidgetInput, widget_definitions::WidgetRegistry}, widgets::{
build_widget::build_gtk_widget, build_widget::WidgetInput,
widget_definitions::WidgetRegistry,
},
window::{ window::{
coords::Coords, coords::Coords,
monitor::MonitorIdentifier, monitor::MonitorIdentifier,
@@ -139,8 +144,8 @@ async fn wait_for_monitor_model() {
let display = gdk::Display::default().expect("could not get default display"); let display = gdk::Display::default().expect("could not get default display");
let start = std::time::Instant::now(); let start = std::time::Instant::now();
loop { loop {
let all_monitors_set = let all_monitors_set = (0..display.n_monitors())
(0..display.n_monitors()).all(|i| display.monitor(i).and_then(|monitor| monitor.model()).is_some()); .all(|i| display.monitor(i).and_then(|monitor| monitor.model()).is_some());
if all_monitors_set { if all_monitors_set {
break; break;
} }
@@ -241,13 +246,24 @@ impl<B: DisplayBackend> App<B> {
let result = if should_toggle && is_open { let result = if should_toggle && is_open {
self.close_window(&instance_id, false) self.close_window(&instance_id, false)
} else { } else {
self.open_window(&WindowArguments { instance_id, window_name, pos, size, monitor, anchor, duration }) self.open_window(&WindowArguments {
instance_id,
window_name,
pos,
size,
monitor,
anchor,
duration,
})
}; };
sender.respond_with_result(result)?; sender.respond_with_result(result)?;
} }
DaemonCommand::CloseWindows { windows, auto_reopen, sender } => { DaemonCommand::CloseWindows { windows, auto_reopen, sender } => {
let errors = windows.iter().map(|window| self.close_window(window, auto_reopen)).filter_map(Result::err); let errors = windows
.iter()
.map(|window| self.close_window(window, auto_reopen))
.filter_map(Result::err);
// Ignore sending errors, as the channel might already be closed // Ignore sending errors, as the channel might already be closed
let _ = sender.respond_with_error_list(errors); let _ = sender.respond_with_error_list(errors);
} }
@@ -256,7 +272,11 @@ impl<B: DisplayBackend> App<B> {
sender.send_success(output)? sender.send_success(output)?
} }
DaemonCommand::ListActiveWindows(sender) => { DaemonCommand::ListActiveWindows(sender) => {
let output = self.open_windows.iter().map(|(id, window)| format!("{id}: {}", window.name)).join("\n"); let output = self
.open_windows
.iter()
.map(|(id, window)| format!("{id}: {}", window.name))
.join("\n");
sender.send_success(output)? sender.send_success(output)?
} }
DaemonCommand::PrintDebug(sender) => { DaemonCommand::PrintDebug(sender) => {
@@ -282,10 +302,9 @@ impl<B: DisplayBackend> App<B> {
if let Some(old_abort_send) = self.window_close_timer_abort_senders.remove(instance_id) { if let Some(old_abort_send) = self.window_close_timer_abort_senders.remove(instance_id) {
_ = old_abort_send.send(()); _ = old_abort_send.send(());
} }
let ewwii_window = self let ewwii_window = self.open_windows.remove(instance_id).with_context(|| {
.open_windows format!("Tried to close window with id '{instance_id}', but no such window was open")
.remove(instance_id) })?;
.with_context(|| format!("Tried to close window with id '{instance_id}', but no such window was open"))?;
// let scope_index = ewwii_window.scope_index; // let scope_index = ewwii_window.scope_index;
ewwii_window.close(); ewwii_window.close();
@@ -324,7 +343,10 @@ impl<B: DisplayBackend> App<B> {
let window_name: &str = &window_args.window_name; let window_name: &str = &window_args.window_name;
let window_def = self.ewwii_config.get_window(window_name)?.clone(); let window_def = self.ewwii_config.get_window(window_name)?.clone();
assert_eq!(window_def.name, window_name, "window definition name did not equal the called window"); assert_eq!(
window_def.name, window_name,
"window definition name did not equal the called window"
);
let initiator = WindowInitiator::new(&window_def, window_args)?; let initiator = WindowInitiator::new(&window_def, window_args)?;
@@ -332,7 +354,8 @@ impl<B: DisplayBackend> App<B> {
// It is critical for supporting dynamic updates // It is critical for supporting dynamic updates
let mut widget_reg_store = WidgetRegistry::new(Some(&window_def.root_widget)); let mut widget_reg_store = WidgetRegistry::new(Some(&window_def.root_widget));
let root_widget = build_gtk_widget(WidgetInput::Window(window_def), &mut widget_reg_store)?; let root_widget =
build_gtk_widget(WidgetInput::Window(window_def), &mut widget_reg_store)?;
root_widget.style_context().add_class(window_name); root_widget.style_context().add_class(window_name);
@@ -344,14 +367,16 @@ impl<B: DisplayBackend> App<B> {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<String>(); let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<String>();
let config_path = self.paths.get_rhai_path(); let config_path = self.paths.get_rhai_path();
let compiled_ast = self.ewwii_config.get_owned_compiled_ast(); let compiled_ast = self.ewwii_config.get_owned_compiled_ast();
let store = iirhai::updates::handle_state_changes(self.ewwii_config.get_root_node()?, tx); let store =
iirhai::updates::handle_state_changes(self.ewwii_config.get_root_node()?, tx);
glib::MainContext::default().spawn_local(async move { glib::MainContext::default().spawn_local(async move {
while let Some(var_name) = rx.recv().await { while let Some(var_name) = rx.recv().await {
log::debug!("Received update for var: {}", var_name); log::debug!("Received update for var: {}", var_name);
let vars = store.read().unwrap().clone(); let vars = store.read().unwrap().clone();
match generate_new_widgetnode(&vars, &config_path, compiled_ast.as_ref()).await { match generate_new_widgetnode(&vars, &config_path, compiled_ast.as_ref()).await
{
Ok(new_widget) => { Ok(new_widget) => {
let _ = widget_reg_store.update_widget_tree(new_widget); let _ = widget_reg_store.update_widget_tree(new_widget);
} }
@@ -375,8 +400,11 @@ impl<B: DisplayBackend> App<B> {
// becomes available again // becomes available again
move |auto_reopen| { move |auto_reopen| {
let (response_sender, _) = daemon_response::create_pair(); let (response_sender, _) = daemon_response::create_pair();
let command = let command = DaemonCommand::CloseWindows {
DaemonCommand::CloseWindows { windows: vec![instance_id.clone()], auto_reopen, sender: response_sender }; windows: vec![instance_id.clone()],
auto_reopen,
sender: response_sender,
};
if let Err(err) = app_evt_sender.send(command) { if let Err(err) = app_evt_sender.send(command) {
log::error!("Error sending close window command: {}", err); log::error!("Error sending close window command: {}", err);
} }
@@ -386,26 +414,28 @@ impl<B: DisplayBackend> App<B> {
let closed_by_user = Rc::new(Cell::new(false)); let closed_by_user = Rc::new(Cell::new(false));
// handling users close request // handling users close request
ewwii_window.delete_event_handler_id = Some(ewwii_window.gtk_window.connect_delete_event({ ewwii_window.delete_event_handler_id =
let handler = gtk_close_handler.clone(); Some(ewwii_window.gtk_window.connect_delete_event({
let closed_by_user = closed_by_user.clone(); let handler = gtk_close_handler.clone();
move |_, _| { let closed_by_user = closed_by_user.clone();
handler(false); // -- false: don't reopen window to respect users intent move |_, _| {
closed_by_user.set(true); handler(false); // -- false: don't reopen window to respect users intent
glib::Propagation::Proceed closed_by_user.set(true);
} glib::Propagation::Proceed
})); }
}));
// handling destory request // handling destory request
ewwii_window.destroy_event_handler_id = Some(ewwii_window.gtk_window.connect_destroy({ ewwii_window.destroy_event_handler_id =
let handler = gtk_close_handler.clone(); Some(ewwii_window.gtk_window.connect_destroy({
let closed_by_user = closed_by_user.clone(); let handler = gtk_close_handler.clone();
move |_| { let closed_by_user = closed_by_user.clone();
if !closed_by_user.get() { move |_| {
handler(true); if !closed_by_user.get() {
handler(true);
}
} }
} }));
}));
let duration = window_args.duration; let duration = window_args.duration;
if let Some(duration) = duration { if let Some(duration) = duration {
@@ -430,7 +460,10 @@ impl<B: DisplayBackend> App<B> {
} }
}); });
if let Some(old_abort_send) = self.window_close_timer_abort_senders.insert(instance_id.to_string(), abort_send) { if let Some(old_abort_send) = self
.window_close_timer_abort_senders
.insert(instance_id.to_string(), abort_send)
{
_ = old_abort_send.send(()); _ = old_abort_send.send(());
} }
} }
@@ -454,8 +487,13 @@ impl<B: DisplayBackend> App<B> {
self.ewwii_config = config; self.ewwii_config = config;
let open_window_ids: Vec<String> = let open_window_ids: Vec<String> = self
self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect(); .open_windows
.keys()
.cloned()
.chain(self.failed_windows.iter().cloned())
.dedup()
.collect();
for instance_id in &open_window_ids { for instance_id in &open_window_ids {
let window_arguments = self.instance_id_to_args.get(instance_id).with_context(|| { let window_arguments = self.instance_id_to_args.get(instance_id).with_context(|| {
format!("Cannot reopen window, initial parameters were not saved correctly for {instance_id}") format!("Cannot reopen window, initial parameters were not saved correctly for {instance_id}")
@@ -468,7 +506,8 @@ impl<B: DisplayBackend> App<B> {
/// Load a given CSS string into the gtk css provider, returning a nicely formatted [`DiagError`] when GTK errors out /// Load a given CSS string into the gtk css provider, returning a nicely formatted [`DiagError`] when GTK errors out
pub fn load_css(&mut self, file_id: usize, css: &str) -> Result<()> { pub fn load_css(&mut self, file_id: usize, css: &str) -> Result<()> {
if let Err(err) = self.css_provider.load_from_data(css.as_bytes()) { if let Err(err) = self.css_provider.load_from_data(css.as_bytes()) {
static PATTERN: Lazy<regex::Regex> = Lazy::new(|| regex::Regex::new(r"[^:]*:(\d+):(\d+)(.*)$").unwrap()); static PATTERN: Lazy<regex::Regex> =
Lazy::new(|| regex::Regex::new(r"[^:]*:(\d+):(\d+)(.*)$").unwrap());
let nice_error_option: Option<_> = (|| { let nice_error_option: Option<_> = (|| {
let captures = PATTERN.captures(err.message())?; let captures = PATTERN.captures(err.message())?;
let line = captures.get(1).unwrap().as_str().parse::<usize>().ok()?; let line = captures.get(1).unwrap().as_str().parse::<usize>().ok()?;
@@ -501,8 +540,9 @@ fn initialize_window<B: DisplayBackend>(
} }
_ => (None, 0, 0), _ => (None, 0, 0),
}; };
let window = B::initialize_window(window_init, monitor_geometry, x, y) let window = B::initialize_window(window_init, monitor_geometry, x, y).with_context(|| {
.with_context(|| format!("monitor {} is unavailable", window_init.monitor.clone().unwrap()))?; format!("monitor {} is unavailable", window_init.monitor.clone().unwrap())
})?;
window.set_title(&format!("Ewwii - {}", window_init.name)); window.set_title(&format!("Ewwii - {}", window_init.name));
window.set_position(gtk::WindowPosition::None); window.set_position(gtk::WindowPosition::None);
@@ -528,7 +568,9 @@ fn initialize_window<B: DisplayBackend>(
if B::IS_X11 { if B::IS_X11 {
if let Some(geometry) = window_init.geometry { if let Some(geometry) = window_init.geometry {
let _ = apply_window_position(geometry, monitor_geometry, &window); let _ = apply_window_position(geometry, monitor_geometry, &window);
if window_init.backend_options.x11.window_type != crate::window::backend_window_options::X11WindowType::Normal { if window_init.backend_options.x11.window_type
!= crate::window::backend_window_options::X11WindowType::Normal
{
let last_pos = Rc::new(RefCell::new(None)); let last_pos = Rc::new(RefCell::new(None));
window.connect_configure_event({ window.connect_configure_event({
let last_pos = last_pos.clone(); let last_pos = last_pos.clone();
@@ -583,7 +625,11 @@ async fn generate_new_widgetnode(
/// Apply the provided window-positioning rules to the window. /// Apply the provided window-positioning rules to the window.
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
fn apply_window_position(mut window_geometry: WindowGeometry, monitor_geometry: gdk::Rectangle, window: &Window) -> Result<()> { fn apply_window_position(
mut window_geometry: WindowGeometry,
monitor_geometry: gdk::Rectangle,
window: &Window,
) -> Result<()> {
let gdk_window = window.window().context("Failed to get gdk window from gtk window")?; let gdk_window = window.window().context("Failed to get gdk window from gtk window")?;
window_geometry.size = crate::window::window_geometry::Coords::from_pixels(window.size()); window_geometry.size = crate::window::window_geometry::Coords::from_pixels(window.size());
let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry); let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry);
@@ -598,8 +644,9 @@ fn apply_window_position(mut window_geometry: WindowGeometry, monitor_geometry:
} }
fn on_screen_changed(window: &Window, _old_screen: Option<&gdk::Screen>) { fn on_screen_changed(window: &Window, _old_screen: Option<&gdk::Screen>) {
let visual = gtk::prelude::GtkWindowExt::screen(window) let visual = gtk::prelude::GtkWindowExt::screen(window).and_then(|screen| {
.and_then(|screen| screen.rgba_visual().filter(|_| screen.is_composited()).or_else(|| screen.system_visual())); screen.rgba_visual().filter(|_| screen.is_composited()).or_else(|| screen.system_visual())
});
window.set_visual(visual.as_ref()); window.set_visual(visual.as_ref());
} }
@@ -633,7 +680,10 @@ fn get_gdk_monitor(identifier: Option<MonitorIdentifier>) -> Result<Monitor> {
fn get_monitor_plug_name(display: &gdk::Display, monitor_num: i32) -> Option<&str> { fn get_monitor_plug_name(display: &gdk::Display, monitor_num: i32) -> Option<&str> {
unsafe { unsafe {
use glib::translate::ToGlibPtr; use glib::translate::ToGlibPtr;
let plug_name_pointer = gdk_sys::gdk_screen_get_monitor_plug_name(display.default_screen().to_glib_none().0, monitor_num); let plug_name_pointer = gdk_sys::gdk_screen_get_monitor_plug_name(
display.default_screen().to_glib_none().0,
monitor_num,
);
use std::ffi::CStr; use std::ffi::CStr;
CStr::from_ptr(plug_name_pointer).to_str().ok() CStr::from_ptr(plug_name_pointer).to_str().ok()
} }
@@ -641,7 +691,10 @@ fn get_monitor_plug_name(display: &gdk::Display, monitor_num: i32) -> Option<&st
/// Returns the [Monitor][gdk::Monitor] structure corresponding to the identifer. /// Returns the [Monitor][gdk::Monitor] structure corresponding to the identifer.
/// Outside of x11, only [MonitorIdentifier::Numeric] is supported /// Outside of x11, only [MonitorIdentifier::Numeric] is supported
pub fn get_monitor_from_display(display: &gdk::Display, identifier: &MonitorIdentifier) -> Option<gdk::Monitor> { pub fn get_monitor_from_display(
display: &gdk::Display,
identifier: &MonitorIdentifier,
) -> Option<gdk::Monitor> {
match identifier { match identifier {
MonitorIdentifier::List(list) => { MonitorIdentifier::List(list) => {
for ident in list { for ident in list {
@@ -666,10 +719,18 @@ pub fn get_monitor_from_display(display: &gdk::Display, identifier: &MonitorIden
} }
} }
pub fn get_window_rectangle(geometry: WindowGeometry, screen_rect: gdk::Rectangle) -> gdk::Rectangle { pub fn get_window_rectangle(
let (offset_x, offset_y) = geometry.offset.relative_to(screen_rect.width(), screen_rect.height()); geometry: WindowGeometry,
screen_rect: gdk::Rectangle,
) -> gdk::Rectangle {
let (offset_x, offset_y) =
geometry.offset.relative_to(screen_rect.width(), screen_rect.height());
let (width, height) = geometry.size.relative_to(screen_rect.width(), screen_rect.height()); let (width, height) = geometry.size.relative_to(screen_rect.width(), screen_rect.height());
let x = screen_rect.x() + offset_x + geometry.anchor_point.x.alignment_to_coordinate(width, screen_rect.width()); let x = screen_rect.x()
let y = screen_rect.y() + offset_y + geometry.anchor_point.y.alignment_to_coordinate(height, screen_rect.height()); + offset_x
+ geometry.anchor_point.x.alignment_to_coordinate(width, screen_rect.width());
let y = screen_rect.y()
+ offset_y
+ geometry.anchor_point.y.alignment_to_coordinate(height, screen_rect.height());
gdk::Rectangle::new(x, y, width, height) gdk::Rectangle::new(x, y, width, height)
} }

View File

@@ -6,7 +6,8 @@ use anyhow::{Context, Result};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use tokio::sync::broadcast; use tokio::sync::broadcast;
pub static APPLICATION_EXIT_SENDER: Lazy<broadcast::Sender<()>> = Lazy::new(|| broadcast::channel(2).0); pub static APPLICATION_EXIT_SENDER: Lazy<broadcast::Sender<()>> =
Lazy::new(|| broadcast::channel(2).0);
/// Notify all listening tasks of the termination of the eww application process. /// Notify all listening tasks of the termination of the eww application process.
pub fn send_exit() -> Result<()> { pub fn send_exit() -> Result<()> {

View File

@@ -26,18 +26,25 @@ pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) ->
/// Connect to the daemon and send the given request. /// Connect to the daemon and send the given request.
/// Returns the response from the daemon, or None if the daemon did not provide any useful response. An Ok(None) response does _not_ indicate failure. /// Returns the response from the daemon, or None if the daemon did not provide any useful response. An Ok(None) response does _not_ indicate failure.
pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result<Option<DaemonResponse>> { pub fn do_server_call(
stream: &mut UnixStream,
action: &opts::ActionWithServer,
) -> Result<Option<DaemonResponse>> {
log::debug!("Forwarding options to server"); log::debug!("Forwarding options to server");
stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?; stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?;
let message_bytes = bincode::serialize(&action)?; let message_bytes = bincode::serialize(&action)?;
stream.write(&(message_bytes.len() as u32).to_be_bytes()).context("Failed to send command size header to IPC stream")?; stream
.write(&(message_bytes.len() as u32).to_be_bytes())
.context("Failed to send command size header to IPC stream")?;
stream.write_all(&message_bytes).context("Failed to write command to IPC stream")?; stream.write_all(&message_bytes).context("Failed to write command to IPC stream")?;
let mut buf = Vec::new(); let mut buf = Vec::new();
stream.set_read_timeout(Some(std::time::Duration::from_millis(100))).context("Failed to set read timeout")?; stream
.set_read_timeout(Some(std::time::Duration::from_millis(100)))
.context("Failed to set read timeout")?;
stream.read_to_end(&mut buf).context("Error reading response from server")?; stream.read_to_end(&mut buf).context("Error reading response from server")?;
Ok(if buf.is_empty() { Ok(if buf.is_empty() {

View File

@@ -56,7 +56,11 @@ impl EwwiiConfig {
// get the iirhai widget tree // get the iirhai widget tree
let compiled_ast = config_parser.compile_code(&rhai_code)?; let compiled_ast = config_parser.compile_code(&rhai_code)?;
let poll_listen_scope = ParseConfig::initial_poll_listen_scope(&rhai_code)?; let poll_listen_scope = ParseConfig::initial_poll_listen_scope(&rhai_code)?;
let config_tree = config_parser.eval_code_with(&rhai_code, Some(poll_listen_scope), Some(&compiled_ast))?; let config_tree = config_parser.eval_code_with(
&rhai_code,
Some(poll_listen_scope),
Some(&compiled_ast),
)?;
let mut window_definitions = HashMap::new(); let mut window_definitions = HashMap::new();
@@ -76,7 +80,11 @@ impl EwwiiConfig {
bail!("Expected root node to be `Enter`, but got something else."); bail!("Expected root node to be `Enter`, but got something else.");
} }
Ok(EwwiiConfig { windows: window_definitions, root_node: Some(config_tree), compiled_ast: Some(compiled_ast) }) Ok(EwwiiConfig {
windows: window_definitions,
root_node: Some(config_tree),
compiled_ast: Some(compiled_ast),
})
} }
pub fn get_windows(&self) -> &HashMap<String, WindowDefinition> { pub fn get_windows(&self) -> &HashMap<String, WindowDefinition> {

View File

@@ -11,7 +11,9 @@ pub fn parse_scss_from_config(path: &Path) -> anyhow::Result<(usize, String)> {
let css_file = path.join("ewwii.css"); let css_file = path.join("ewwii.css");
let scss_file = path.join("ewwii.scss"); let scss_file = path.join("ewwii.scss");
if css_file.exists() && scss_file.exists() { if css_file.exists() && scss_file.exists() {
return Err(anyhow!("Encountered both an SCSS and CSS file. Only one of these may exist at a time")); return Err(anyhow!(
"Encountered both an SCSS and CSS file. Only one of these may exist at a time"
));
} }
let (s_css_path, css) = if css_file.exists() { let (s_css_path, css) = if css_file.exists() {
@@ -20,11 +22,12 @@ pub fn parse_scss_from_config(path: &Path) -> anyhow::Result<(usize, String)> {
let css = replace_env_var_references(css_file_content); let css = replace_env_var_references(css_file_content);
(css_file, css) (css_file, css)
} else { } else {
let scss_file_content = let scss_file_content = std::fs::read_to_string(&scss_file)
std::fs::read_to_string(&scss_file).with_context(|| format!("Given SCSS file doesn't exist! {}", path.display()))?; .with_context(|| format!("Given SCSS file doesn't exist! {}", path.display()))?;
let file_content = replace_env_var_references(scss_file_content); let file_content = replace_env_var_references(scss_file_content);
let grass_config = grass::Options::default().load_path(path); let grass_config = grass::Options::default().load_path(path);
let css = grass::from_string(file_content, &grass_config).map_err(|err| anyhow!("SCSS parsing error: {}", err))?; let css = grass::from_string(file_content, &grass_config)
.map_err(|err| anyhow!("SCSS parsing error: {}", err))?;
(scss_file, css) (scss_file, css)
}; };

View File

@@ -28,15 +28,22 @@ pub fn create_pair() -> (DaemonResponseSender, mpsc::UnboundedReceiver<DaemonRes
impl DaemonResponseSender { impl DaemonResponseSender {
pub fn send_success(&self, s: String) -> Result<()> { pub fn send_success(&self, s: String) -> Result<()> {
self.0.send(DaemonResponse::Success(s)).context("Failed to send success response from application thread") self.0
.send(DaemonResponse::Success(s))
.context("Failed to send success response from application thread")
} }
pub fn send_failure(&self, s: String) -> Result<()> { pub fn send_failure(&self, s: String) -> Result<()> {
self.0.send(DaemonResponse::Failure(s)).context("Failed to send failure response from application thread") self.0
.send(DaemonResponse::Failure(s))
.context("Failed to send failure response from application thread")
} }
/// Given a list of errors, respond with an error value if there are any errors, and respond with success otherwise. /// Given a list of errors, respond with an error value if there are any errors, and respond with success otherwise.
pub fn respond_with_error_list(&self, errors: impl IntoIterator<Item = anyhow::Error>) -> Result<()> { pub fn respond_with_error_list(
&self,
errors: impl IntoIterator<Item = anyhow::Error>,
) -> Result<()> {
let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(&e)).join("\n"); let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(&e)).join("\n");
if errors.is_empty() { if errors.is_empty() {
self.send_success(String::new()) self.send_success(String::new())

View File

@@ -12,7 +12,12 @@ pub trait DisplayBackend: Send + Sync + 'static {
const IS_X11: bool; const IS_X11: bool;
const IS_WAYLAND: bool; const IS_WAYLAND: bool;
fn initialize_window(window_init: &WindowInitiator, monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window>; fn initialize_window(
window_init: &WindowInitiator,
monitor: gdk::Rectangle,
x: i32,
y: i32,
) -> Option<Window>;
} }
pub struct NoBackend; pub struct NoBackend;
@@ -21,7 +26,12 @@ impl DisplayBackend for NoBackend {
const IS_X11: bool = false; const IS_X11: bool = false;
const IS_WAYLAND: bool = false; const IS_WAYLAND: bool = false;
fn initialize_window(_window_init: &WindowInitiator, _monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> { fn initialize_window(
_window_init: &WindowInitiator,
_monitor: gdk::Rectangle,
x: i32,
y: i32,
) -> Option<Window> {
Some(Window::new(gtk::WindowType::Toplevel, x, y)) Some(Window::new(gtk::WindowType::Toplevel, x, y))
} }
} }
@@ -43,7 +53,12 @@ mod platform_wayland {
const IS_X11: bool = false; const IS_X11: bool = false;
const IS_WAYLAND: bool = true; const IS_WAYLAND: bool = true;
fn initialize_window(window_init: &WindowInitiator, monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> { fn initialize_window(
window_init: &WindowInitiator,
monitor: gdk::Rectangle,
x: i32,
y: i32,
) -> Option<Window> {
let window = Window::new(gtk::WindowType::Toplevel, x, y); let window = Window::new(gtk::WindowType::Toplevel, x, y);
// Initialising a layer shell surface // Initialising a layer shell surface
window.init_layer_shell(); window.init_layer_shell();
@@ -155,9 +170,17 @@ mod platform_x11 {
const IS_X11: bool = true; const IS_X11: bool = true;
const IS_WAYLAND: bool = false; const IS_WAYLAND: bool = false;
fn initialize_window(window_init: &WindowInitiator, _monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> { fn initialize_window(
let window_type = window_init: &WindowInitiator,
if window_init.backend_options.x11.wm_ignore { gtk::WindowType::Popup } else { gtk::WindowType::Toplevel }; _monitor: gdk::Rectangle,
x: i32,
y: i32,
) -> Option<Window> {
let window_type = if window_init.backend_options.x11.wm_ignore {
gtk::WindowType::Popup
} else {
gtk::WindowType::Toplevel
};
let window = Window::new(window_type, x, y); let window = Window::new(window_type, x, y);
window.set_resizable(window_init.resizable); window.set_resizable(window_init.resizable);
window.set_keep_above(window_init.stacking == WindowStacking::Foreground); window.set_keep_above(window_init.stacking == WindowStacking::Foreground);
@@ -171,7 +194,11 @@ mod platform_x11 {
} }
} }
pub fn set_xprops(window: &Window, monitor: Monitor, window_init: &WindowInitiator) -> Result<()> { pub fn set_xprops(
window: &Window,
monitor: Monitor,
window_init: &WindowInitiator,
) -> Result<()> {
let backend = X11BackendConnection::new()?; let backend = X11BackendConnection::new()?;
backend.set_xprops_for(window, monitor, window_init)?; backend.set_xprops_for(window, monitor, window_init)?;
Ok(()) Ok(())
@@ -191,12 +218,19 @@ mod platform_x11 {
Ok(X11BackendConnection { conn, root_window: screen.root, atoms }) Ok(X11BackendConnection { conn, root_window: screen.root, atoms })
} }
fn set_xprops_for(&self, window: &Window, monitor: Monitor, window_init: &WindowInitiator) -> Result<()> { fn set_xprops_for(
&self,
window: &Window,
monitor: Monitor,
window_init: &WindowInitiator,
) -> Result<()> {
let monitor_rect = monitor.geometry(); let monitor_rect = monitor.geometry();
let scale_factor = monitor.scale_factor() as u32; let scale_factor = monitor.scale_factor() as u32;
let gdk_window = window.window().context("Couldn't get gdk window from gtk window")?; let gdk_window = window.window().context("Couldn't get gdk window from gtk window")?;
let win_id = let win_id = gdk_window
gdk_window.downcast_ref::<gdkx11::X11Window>().context("Failed to get x11 window for gtk window")?.xid() as u32; .downcast_ref::<gdkx11::X11Window>()
.context("Failed to get x11 window for gtk window")?
.xid() as u32;
let strut_def = window_init.backend_options.x11.struts; let strut_def = window_init.backend_options.x11.struts;
let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?; let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?;
@@ -206,8 +240,12 @@ mod platform_x11 {
let mon_end_y = scale_factor * (monitor_rect.y() + monitor_rect.height()) as u32 - 1u32; let mon_end_y = scale_factor * (monitor_rect.y() + monitor_rect.height()) as u32 - 1u32;
let dist = match strut_def.side { let dist = match strut_def.side {
Side::Left | Side::Right => strut_def.distance.pixels_relative_to(monitor_rect.width()) as u32, Side::Left | Side::Right => {
Side::Top | Side::Bottom => strut_def.distance.pixels_relative_to(monitor_rect.height()) as u32, strut_def.distance.pixels_relative_to(monitor_rect.width()) as u32
}
Side::Top | Side::Bottom => {
strut_def.distance.pixels_relative_to(monitor_rect.height()) as u32
}
}; };
// don't question it,..... // don't question it,.....

View File

@@ -18,7 +18,11 @@ pub struct ConversionError {
pub struct DurationParseError; pub struct DurationParseError;
impl ConversionError { impl ConversionError {
pub fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static + Sync + Send) -> Self { pub fn new(
value: DynVal,
target_type: &'static str,
source: impl std::error::Error + 'static + Sync + Send,
) -> Self {
ConversionError { value, target_type, source: Some(Box::new(source)) } ConversionError { value, target_type, source: Some(Box::new(source)) }
} }
} }
@@ -113,7 +117,11 @@ impl TryFrom<serde_json::Value> for DynVal {
impl From<Vec<DynVal>> for DynVal { impl From<Vec<DynVal>> for DynVal {
fn from(v: Vec<DynVal>) -> Self { fn from(v: Vec<DynVal>) -> Self {
let span = if let (Some(first), Some(last)) = (v.first(), v.last()) { first.span().to(last.span()) } else { Span::DUMMY }; let span = if let (Some(first), Some(last)) = (v.first(), v.last()) {
first.span().to(last.span())
} else {
Span::DUMMY
};
let elements = v.into_iter().map(|x| x.as_string().unwrap()).collect::<Vec<_>>(); let elements = v.into_iter().map(|x| x.as_string().unwrap()).collect::<Vec<_>>();
DynVal(serde_json::to_string(&elements).unwrap(), span) DynVal(serde_json::to_string(&elements).unwrap(), span)
} }
@@ -174,10 +182,15 @@ impl DynVal {
let s = &self.0; let s = &self.0;
if s.ends_with("ms") { if s.ends_with("ms") {
Ok(Duration::from_millis( Ok(Duration::from_millis(
s.trim_end_matches("ms").parse().map_err(|e| ConversionError::new(self.clone(), "integer", e))?, s.trim_end_matches("ms")
.parse()
.map_err(|e| ConversionError::new(self.clone(), "integer", e))?,
)) ))
} else if s.ends_with('s') { } else if s.ends_with('s') {
let secs = s.trim_end_matches('s').parse::<f64>().map_err(|e| ConversionError::new(self.clone(), "number", e))?; let secs = s
.trim_end_matches('s')
.parse::<f64>()
.map_err(|e| ConversionError::new(self.clone(), "number", e))?;
Ok(Duration::from_millis(f64::floor(secs * 1000f64) as u64)) Ok(Duration::from_millis(f64::floor(secs * 1000f64) as u64))
} else if s.ends_with('m') || s.ends_with("min") { } else if s.ends_with('m') || s.ends_with("min") {
let minutes = s let minutes = s
@@ -187,12 +200,19 @@ impl DynVal {
.map_err(|e| ConversionError::new(self.clone(), "number", e))?; .map_err(|e| ConversionError::new(self.clone(), "number", e))?;
Ok(Duration::from_secs(f64::floor(minutes * 60f64) as u64)) Ok(Duration::from_secs(f64::floor(minutes * 60f64) as u64))
} else if s.ends_with('h') { } else if s.ends_with('h') {
let hours = s.trim_end_matches('h').parse::<f64>().map_err(|e| ConversionError::new(self.clone(), "number", e))?; let hours = s
.trim_end_matches('h')
.parse::<f64>()
.map_err(|e| ConversionError::new(self.clone(), "number", e))?;
Ok(Duration::from_secs(f64::floor(hours * 60f64 * 60f64) as u64)) Ok(Duration::from_secs(f64::floor(hours * 60f64 * 60f64) as u64))
} else if let Ok(millis) = s.parse() { } else if let Ok(millis) = s.parse() {
Ok(Duration::from_millis(millis)) Ok(Duration::from_millis(millis))
} else { } else {
Err(ConversionError { value: self.clone(), target_type: "duration", source: Some(Box::new(DurationParseError)) }) Err(ConversionError {
value: self.clone(),
target_type: "duration",
source: Some(Box::new(DurationParseError)),
})
} }
} }

View File

@@ -13,7 +13,8 @@ use codespan_reporting::{
use ewwii_shared_util::Span; use ewwii_shared_util::Span;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
pub static FILE_DATABASE: Lazy<Arc<RwLock<FileDatabase>>> = Lazy::new(|| Arc::new(RwLock::new(FileDatabase::new()))); pub static FILE_DATABASE: Lazy<Arc<RwLock<FileDatabase>>> =
Lazy::new(|| Arc::new(RwLock::new(FileDatabase::new())));
// pub fn clear_files() { // pub fn clear_files() {
// *FILE_DATABASE.write().unwrap() = FileDatabase::new(); // *FILE_DATABASE.write().unwrap() = FileDatabase::new();
@@ -30,7 +31,9 @@ pub fn print_error(err: anyhow::Error) {
} }
pub fn format_error(err: &anyhow::Error) -> String { pub fn format_error(err: &anyhow::Error) -> String {
anyhow_err_to_diagnostic(err).and_then(|diag| stringify_diagnostic(diag).ok()).unwrap_or_else(|| format!("{:?}", err)) anyhow_err_to_diagnostic(err)
.and_then(|diag| stringify_diagnostic(diag).ok())
.unwrap_or_else(|| format!("{:?}", err))
} }
// * OLD // * OLD
@@ -63,8 +66,12 @@ pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Option<Diagnostic<usize>
} }
} }
pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diagnostic<usize>) -> anyhow::Result<String> { pub fn stringify_diagnostic(
diagnostic.labels.retain(|label| !Span(label.range.start, label.range.end, label.file_id).is_dummy()); mut diagnostic: codespan_reporting::diagnostic::Diagnostic<usize>,
) -> anyhow::Result<String> {
diagnostic
.labels
.retain(|label| !Span(label.range.start, label.range.end, label.file_id).is_dummy());
let mut config = term::Config::default(); let mut config = term::Config::default();
let mut chars = Chars::box_drawing(); let mut chars = Chars::box_drawing();

View File

@@ -40,7 +40,12 @@ impl FileDatabase {
pub fn insert_string(&mut self, name: String, content: String) -> Result<usize, DiagError> { pub fn insert_string(&mut self, name: String, content: String) -> Result<usize, DiagError> {
let line_starts = codespan_reporting::files::line_starts(&content).collect(); let line_starts = codespan_reporting::files::line_starts(&content).collect();
let code_file = CodeFile { name, line_starts, source_len_bytes: content.len(), source: CodeSource::Literal(content) }; let code_file = CodeFile {
name,
line_starts,
source_len_bytes: content.len(),
source: CodeSource::Literal(content),
};
let file_id = self.insert_code_file(code_file); let file_id = self.insert_code_file(code_file);
Ok(file_id) Ok(file_id)
} }
@@ -55,12 +60,23 @@ impl<'a> Files<'a> for FileDatabase {
Ok(&self.get_file(id)?.name) Ok(&self.get_file(id)?.name)
} }
fn source(&'a self, id: Self::FileId) -> Result<Self::Source, codespan_reporting::files::Error> { fn source(
&'a self,
id: Self::FileId,
) -> Result<Self::Source, codespan_reporting::files::Error> {
self.get_file(id)?.source.read_content().map_err(codespan_reporting::files::Error::Io) self.get_file(id)?.source.read_content().map_err(codespan_reporting::files::Error::Io)
} }
fn line_index(&self, id: Self::FileId, byte_index: usize) -> Result<usize, codespan_reporting::files::Error> { fn line_index(
Ok(self.get_file(id)?.line_starts.binary_search(&byte_index).unwrap_or_else(|next_line| next_line - 1)) &self,
id: Self::FileId,
byte_index: usize,
) -> Result<usize, codespan_reporting::files::Error> {
Ok(self
.get_file(id)?
.line_starts
.binary_search(&byte_index)
.unwrap_or_else(|next_line| next_line - 1))
} }
fn line_range( fn line_range(
@@ -90,11 +106,16 @@ impl CodeFile {
use std::cmp::Ordering; use std::cmp::Ordering;
match line_index.cmp(&self.line_starts.len()) { match line_index.cmp(&self.line_starts.len()) {
Ordering::Less => Ok(self.line_starts.get(line_index).cloned().expect("failed despite previous check")), Ordering::Less => Ok(self
.line_starts
.get(line_index)
.cloned()
.expect("failed despite previous check")),
Ordering::Equal => Ok(self.source_len_bytes), Ordering::Equal => Ok(self.source_len_bytes),
Ordering::Greater => { Ordering::Greater => Err(codespan_reporting::files::Error::LineTooLarge {
Err(codespan_reporting::files::Error::LineTooLarge { given: line_index, max: self.line_starts.len() - 1 }) given: line_index,
} max: self.line_starts.len() - 1,
}),
} }
} }
} }

View File

@@ -32,7 +32,10 @@ pub async fn run_ewwii_server<P: AsRef<std::path::Path>>(
} }
/// Handle a single IPC connection from start to end. /// Handle a single IPC connection from start to end.
async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: UnboundedSender<app::DaemonCommand>) -> Result<()> { async fn handle_connection(
mut stream: tokio::net::UnixStream,
evt_send: UnboundedSender<app::DaemonCommand>,
) -> Result<()> {
let (mut stream_read, mut stream_write) = stream.split(); let (mut stream_read, mut stream_write) = stream.split();
let action: opts::ActionWithServer = read_ewwii_action_from_stream(&mut stream_read).await?; let action: opts::ActionWithServer = read_ewwii_action_from_stream(&mut stream_read).await?;
@@ -45,7 +48,9 @@ async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: Unbound
if let Some(mut response_recv) = maybe_response_recv { if let Some(mut response_recv) = maybe_response_recv {
log::debug!("Waiting for response for IPC client"); log::debug!("Waiting for response for IPC client");
if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), response_recv.recv()).await { if let Ok(Some(response)) =
tokio::time::timeout(Duration::from_millis(100), response_recv.recv()).await
{
let response = bincode::serialize(&response)?; let response = bincode::serialize(&response)?;
let result = &stream_write.write_all(&response).await; let result = &stream_write.write_all(&response).await;
crate::print_result_err!("sending text response to ipc client", &result); crate::print_result_err!("sending text response to ipc client", &result);
@@ -57,13 +62,21 @@ async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: Unbound
/// Read a single message from a unix stream, and parses it into a `ActionWithServer` /// Read a single message from a unix stream, and parses it into a `ActionWithServer`
/// The format here requires the first 4 bytes to be the size of the rest of the message (in big-endian), followed by the rest of the message. /// The format here requires the first 4 bytes to be the size of the rest of the message (in big-endian), followed by the rest of the message.
async fn read_ewwii_action_from_stream(stream_read: &'_ mut tokio::net::unix::ReadHalf<'_>) -> Result<opts::ActionWithServer> { async fn read_ewwii_action_from_stream(
stream_read: &'_ mut tokio::net::unix::ReadHalf<'_>,
) -> Result<opts::ActionWithServer> {
let mut message_byte_length = [0u8; 4]; let mut message_byte_length = [0u8; 4];
stream_read.read_exact(&mut message_byte_length).await.context("Failed to read message size header in IPC message")?; stream_read
.read_exact(&mut message_byte_length)
.await
.context("Failed to read message size header in IPC message")?;
let message_byte_length = u32::from_be_bytes(message_byte_length); let message_byte_length = u32::from_be_bytes(message_byte_length);
let mut raw_message = Vec::<u8>::with_capacity(message_byte_length as usize); let mut raw_message = Vec::<u8>::with_capacity(message_byte_length as usize);
while raw_message.len() < message_byte_length as usize { while raw_message.len() < message_byte_length as usize {
stream_read.read_buf(&mut raw_message).await.context("Failed to read actual IPC message")?; stream_read
.read_buf(&mut raw_message)
.await
.context("Failed to read actual IPC message")?;
} }
bincode::deserialize(&raw_message).context("Failed to parse client message") bincode::deserialize(&raw_message).context("Failed to parse client message")

View File

@@ -83,7 +83,12 @@ fn main() {
} }
if let opts::Action::ShellCompletions { shell } = opts.action { if let opts::Action::ShellCompletions { shell } = opts.action {
clap_complete::generate(shell, &mut opts::RawOpt::command(), "ewwii", &mut std::io::stdout()); clap_complete::generate(
shell,
&mut opts::RawOpt::command(),
"ewwii",
&mut std::io::stdout(),
);
return; return;
} }
@@ -92,10 +97,18 @@ fn main() {
let use_wayland = opts.force_wayland || detected_wayland; let use_wayland = opts.force_wayland || detected_wayland;
#[cfg(all(feature = "wayland", feature = "x11"))] #[cfg(all(feature = "wayland", feature = "x11"))]
let result = if use_wayland { let result = if use_wayland {
log::debug!("Running on wayland. force_wayland={}, detected_wayland={}", opts.force_wayland, detected_wayland); log::debug!(
"Running on wayland. force_wayland={}, detected_wayland={}",
opts.force_wayland,
detected_wayland
);
run::<display_backend::WaylandBackend>(opts, eww_binary_name) run::<display_backend::WaylandBackend>(opts, eww_binary_name)
} else { } else {
log::debug!("Running on X11. force_wayland={}, detected_wayland={}", opts.force_wayland, detected_wayland); log::debug!(
"Running on X11. force_wayland={}, detected_wayland={}",
opts.force_wayland,
detected_wayland
);
run::<display_backend::X11Backend>(opts, eww_binary_name) run::<display_backend::X11Backend>(opts, eww_binary_name)
}; };
@@ -123,7 +136,8 @@ fn main() {
fn detect_wayland() -> bool { fn detect_wayland() -> bool {
let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_default(); let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_default();
let wayland_display = std::env::var("WAYLAND_DISPLAY").unwrap_or_default(); let wayland_display = std::env::var("WAYLAND_DISPLAY").unwrap_or_default();
session_type.contains("wayland") || (!wayland_display.is_empty() && !session_type.contains("x11")) session_type.contains("wayland")
|| (!wayland_display.is_empty() && !session_type.contains("x11"))
} }
fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()> { fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()> {
@@ -165,14 +179,19 @@ fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()
let _ = std::fs::remove_file(paths.get_ipc_socket_file()); let _ = std::fs::remove_file(paths.get_ipc_socket_file());
if !opts.show_logs { if !opts.show_logs {
println!("Run `{} logs` to see any errors while editing your configuration.", eww_binary_name); println!(
"Run `{} logs` to see any errors while editing your configuration.",
eww_binary_name
);
} }
let fork_result = server::initialize_server::<B>(paths.clone(), None, !opts.no_daemonize)?; let fork_result =
server::initialize_server::<B>(paths.clone(), None, !opts.no_daemonize)?;
opts.no_daemonize || fork_result == ForkResult::Parent opts.no_daemonize || fork_result == ForkResult::Parent
} }
opts::Action::WithServer(ActionWithServer::KillServer) => { opts::Action::WithServer(ActionWithServer::KillServer) => {
if let Some(response) = handle_server_command(&paths, &ActionWithServer::KillServer, 1)? { if let Some(response) = handle_server_command(&paths, &ActionWithServer::KillServer, 1)?
{
handle_daemon_response(response); handle_daemon_response(response);
} }
false false
@@ -191,15 +210,22 @@ fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()
Err(err) if action.can_start_daemon() && !opts.no_daemonize => { Err(err) if action.can_start_daemon() && !opts.no_daemonize => {
// connecting to the daemon failed. Thus, start the daemon here! // connecting to the daemon failed. Thus, start the daemon here!
log::warn!("Failed to connect to daemon: {}", err); log::warn!("Failed to connect to daemon: {}", err);
log::info!("Initializing ewwii server. ({})", paths.get_ipc_socket_file().display()); log::info!(
"Initializing ewwii server. ({})",
paths.get_ipc_socket_file().display()
);
let _ = std::fs::remove_file(paths.get_ipc_socket_file()); let _ = std::fs::remove_file(paths.get_ipc_socket_file());
if !opts.show_logs { if !opts.show_logs {
println!("Run `{} logs` to see any errors while editing your configuration.", eww_binary_name); println!(
"Run `{} logs` to see any errors while editing your configuration.",
eww_binary_name
);
} }
let (command, response_recv) = action.into_daemon_command(); let (command, response_recv) = action.into_daemon_command();
// start the daemon and give it the command // start the daemon and give it the command
let fork_result = server::initialize_server::<B>(paths.clone(), Some(command), true)?; let fork_result =
server::initialize_server::<B>(paths.clone(), Some(command), true)?;
let is_parent = fork_result == ForkResult::Parent; let is_parent = fork_result == ForkResult::Parent;
if let (Some(recv), true) = (response_recv, is_parent) { if let (Some(recv), true) = (response_recv, is_parent) {
listen_for_daemon_response(recv); listen_for_daemon_response(recv);
@@ -224,16 +250,26 @@ fn listen_for_daemon_response(mut recv: DaemonResponseReceiver) {
.build() .build()
.expect("Failed to initialize tokio runtime"); .expect("Failed to initialize tokio runtime");
rt.block_on(async { rt.block_on(async {
if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), recv.recv()).await { if let Ok(Some(response)) =
tokio::time::timeout(Duration::from_millis(100), recv.recv()).await
{
println!("{}", response); println!("{}", response);
} }
}) })
} }
/// attempt to send a command to the daemon and send it the given action repeatedly. /// attempt to send a command to the daemon and send it the given action repeatedly.
fn handle_server_command(paths: &EwwPaths, action: &ActionWithServer, connect_attempts: usize) -> Result<Option<DaemonResponse>> { fn handle_server_command(
log::debug!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display()); paths: &EwwPaths,
let mut stream = attempt_connect(paths.get_ipc_socket_file(), connect_attempts).context("Failed to connect to daemon")?; action: &ActionWithServer,
connect_attempts: usize,
) -> Result<Option<DaemonResponse>> {
log::debug!(
"Trying to find server process at socket {}",
paths.get_ipc_socket_file().display()
);
let mut stream = attempt_connect(paths.get_ipc_socket_file(), connect_attempts)
.context("Failed to connect to daemon")?;
log::debug!("Connected to Ewwii server ({}).", &paths.get_ipc_socket_file().display()); log::debug!("Connected to Ewwii server ({}).", &paths.get_ipc_socket_file().display());
client::do_server_call(&mut stream, action).context("Error while forwarding command to server") client::do_server_call(&mut stream, action).context("Error while forwarding command to server")
} }
@@ -262,8 +298,8 @@ fn attempt_connect(socket_path: impl AsRef<Path>, attempts: usize) -> Option<net
/// Check if a eww server is currently running by trying to send a ping message to it. /// Check if a eww server is currently running by trying to send a ping message to it.
fn check_server_running(socket_path: impl AsRef<Path>) -> bool { fn check_server_running(socket_path: impl AsRef<Path>) -> bool {
let response = net::UnixStream::connect(socket_path) let response = net::UnixStream::connect(socket_path).ok().and_then(|mut stream| {
.ok() client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok()
.and_then(|mut stream| client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok()); });
response.is_some() response.is_some()
} }

View File

@@ -197,8 +197,17 @@ impl Opt {
impl From<RawOpt> for Opt { impl From<RawOpt> for Opt {
fn from(other: RawOpt) -> Self { fn from(other: RawOpt) -> Self {
let RawOpt { log_debug, force_wayland, config, show_logs, no_daemonize, restart, action } = other; let RawOpt { log_debug, force_wayland, config, show_logs, no_daemonize, restart, action } =
Opt { log_debug, force_wayland, show_logs, restart, config_path: config, action, no_daemonize } other;
Opt {
log_debug,
force_wayland,
show_logs,
restart,
config_path: config,
action,
no_daemonize,
}
} }
} }
@@ -234,7 +243,9 @@ impl ActionWithServer {
matches!(self, ActionWithServer::OpenWindow { .. }) matches!(self, ActionWithServer::OpenWindow { .. })
} }
pub fn into_daemon_command(self) -> (app::DaemonCommand, Option<daemon_response::DaemonResponseReceiver>) { pub fn into_daemon_command(
self,
) -> (app::DaemonCommand, Option<daemon_response::DaemonResponseReceiver>) {
let command = match self { let command = match self {
ActionWithServer::OpenInspector => app::DaemonCommand::OpenInspector, ActionWithServer::OpenInspector => app::DaemonCommand::OpenInspector,
@@ -248,7 +259,16 @@ impl ActionWithServer {
// ActionWithServer::OpenMany { windows, should_toggle } => { // ActionWithServer::OpenMany { windows, should_toggle } => {
// return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender }); // return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender });
// } // }
ActionWithServer::OpenWindow { window_name, id, pos, size, screen, anchor, should_toggle, duration } => { ActionWithServer::OpenWindow {
window_name,
id,
pos,
size,
screen,
anchor,
should_toggle,
duration,
} => {
return with_response_channel(|sender| app::DaemonCommand::OpenWindow { return with_response_channel(|sender| app::DaemonCommand::OpenWindow {
window_name, window_name,
instance_id: id, instance_id: id,
@@ -263,18 +283,32 @@ impl ActionWithServer {
}); });
} }
ActionWithServer::CloseWindows { windows } => { ActionWithServer::CloseWindows { windows } => {
return with_response_channel(|sender| app::DaemonCommand::CloseWindows { windows, auto_reopen: false, sender }); return with_response_channel(|sender| app::DaemonCommand::CloseWindows {
windows,
auto_reopen: false,
sender,
});
}
ActionWithServer::Reload => {
return with_response_channel(app::DaemonCommand::ReloadConfigAndCss)
}
ActionWithServer::ListWindows => {
return with_response_channel(app::DaemonCommand::ListWindows)
}
ActionWithServer::ListActiveWindows => {
return with_response_channel(app::DaemonCommand::ListActiveWindows)
}
ActionWithServer::ShowDebug => {
return with_response_channel(app::DaemonCommand::PrintDebug)
} }
ActionWithServer::Reload => return with_response_channel(app::DaemonCommand::ReloadConfigAndCss),
ActionWithServer::ListWindows => return with_response_channel(app::DaemonCommand::ListWindows),
ActionWithServer::ListActiveWindows => return with_response_channel(app::DaemonCommand::ListActiveWindows),
ActionWithServer::ShowDebug => return with_response_channel(app::DaemonCommand::PrintDebug),
}; };
(command, None) (command, None)
} }
} }
fn with_response_channel<O, F>(f: F) -> (O, Option<tokio::sync::mpsc::UnboundedReceiver<DaemonResponse>>) fn with_response_channel<O, F>(
f: F,
) -> (O, Option<tokio::sync::mpsc::UnboundedReceiver<DaemonResponse>>)
where where
F: FnOnce(DaemonResponseSender) -> O, F: FnOnce(DaemonResponseSender) -> O,
{ {

View File

@@ -55,7 +55,12 @@ impl EwwPaths {
std::fs::create_dir_all(&log_dir)?; std::fs::create_dir_all(&log_dir)?;
} }
Ok(EwwPaths { config_dir, log_file: log_dir.join(format!("eww_{}.log", daemon_id)), log_dir, ipc_socket_file }) Ok(EwwPaths {
config_dir,
log_file: log_dir.join(format!("eww_{}.log", daemon_id)),
log_dir,
ipc_socket_file,
})
} }
pub fn default() -> Result<Self> { pub fn default() -> Result<Self> {

View File

@@ -25,8 +25,9 @@ pub fn initialize_server<B: DisplayBackend>(
) -> Result<ForkResult> { ) -> Result<ForkResult> {
let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel(); let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel();
std::env::set_current_dir(paths.get_config_dir()) std::env::set_current_dir(paths.get_config_dir()).with_context(|| {
.with_context(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?; format!("Failed to change working directory to {}", paths.get_config_dir().display())
})?;
log::info!("Loading paths: {}", &paths); log::info!("Loading paths: {}", &paths);
@@ -63,13 +64,16 @@ pub fn initialize_server<B: DisplayBackend>(
"# "#
); );
simple_signal::set_handler(&[simple_signal::Signal::Int, simple_signal::Signal::Term], move |_| { simple_signal::set_handler(
log::info!("Shutting down ewwii daemon..."); &[simple_signal::Signal::Int, simple_signal::Signal::Term],
if let Err(e) = crate::application_lifecycle::send_exit() { move |_| {
log::error!("Failed to send application shutdown event to workers: {:?}", e); log::info!("Shutting down ewwii daemon...");
std::process::exit(1); if let Err(e) = crate::application_lifecycle::send_exit() {
} log::error!("Failed to send application shutdown event to workers: {:?}", e);
}); std::process::exit(1);
}
},
);
if B::IS_WAYLAND { if B::IS_WAYLAND {
std::env::set_var("GDK_BACKEND", "wayland") std::env::set_var("GDK_BACKEND", "wayland")
@@ -89,7 +93,11 @@ pub fn initialize_server<B: DisplayBackend>(
}; };
if let Some(screen) = gtk::gdk::Screen::default() { if let Some(screen) = gtk::gdk::Screen::default() {
gtk::StyleContext::add_provider_for_screen(&screen, &app.css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION); gtk::StyleContext::add_provider_for_screen(
&screen,
&app.css_provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
} }
if let Ok((file_id, css)) = config::scss::parse_scss_from_config(app.paths.get_config_dir()) { if let Ok((file_id, css)) = config::scss::parse_scss_from_config(app.paths.get_config_dir()) {
@@ -156,7 +164,10 @@ fn reload_config_and_css(ui_send: &UnboundedSender<DaemonCommand>) -> Result<()>
Ok(()) Ok(())
} }
fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>) -> tokio::runtime::Handle { fn init_async_part(
paths: EwwPaths,
ui_send: UnboundedSender<app::DaemonCommand>,
) -> tokio::runtime::Handle {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.thread_name("main-async-runtime") .thread_name("main-async-runtime")
.enable_all() .enable_all()
@@ -176,7 +187,9 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>
let ipc_server_join_handle = { let ipc_server_join_handle = {
let ui_send = ui_send.clone(); let ui_send = ui_send.clone();
tokio::spawn(async move { ipc_server::run_ewwii_server(ui_send, paths.get_ipc_socket_file()).await }) tokio::spawn(async move {
ipc_server::run_ewwii_server(ui_send, paths.get_ipc_socket_file()).await
})
}; };
let forward_exit_to_app_handle = { let forward_exit_to_app_handle = {
@@ -190,7 +203,11 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>
}) })
}; };
let result = tokio::try_join!(filewatch_join_handle, ipc_server_join_handle, forward_exit_to_app_handle); let result = tokio::try_join!(
filewatch_join_handle,
ipc_server_join_handle,
forward_exit_to_app_handle
);
if let Err(e) = result { if let Err(e) = result {
log::error!("Ewwii exiting with error: {:?}", e); log::error!("Ewwii exiting with error: {:?}", e);
@@ -203,25 +220,29 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>
} }
/// Watch configuration files for changes, sending reload events to the eww app when the files change. /// Watch configuration files for changes, sending reload events to the eww app when the files change.
async fn run_filewatch<P: AsRef<Path>>(config_dir: P, evt_send: UnboundedSender<app::DaemonCommand>) -> Result<()> { async fn run_filewatch<P: AsRef<Path>>(
config_dir: P,
evt_send: UnboundedSender<app::DaemonCommand>,
) -> Result<()> {
use notify::{RecommendedWatcher, RecursiveMode, Watcher}; use notify::{RecommendedWatcher, RecursiveMode, Watcher};
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
let mut watcher: RecommendedWatcher = notify::recommended_watcher(move |res: notify::Result<notify::Event>| match res { let mut watcher: RecommendedWatcher =
Ok(notify::Event { kind: notify::EventKind::Modify(_), paths, .. }) => { notify::recommended_watcher(move |res: notify::Result<notify::Event>| match res {
let relevant_files_changed = paths.iter().any(|path| { Ok(notify::Event { kind: notify::EventKind::Modify(_), paths, .. }) => {
let ext = path.extension().unwrap_or_default(); let relevant_files_changed = paths.iter().any(|path| {
ext == "rhai" || ext == "scss" || ext == "css" let ext = path.extension().unwrap_or_default();
}); ext == "rhai" || ext == "scss" || ext == "css"
if relevant_files_changed { });
if let Err(err) = tx.send(()) { if relevant_files_changed {
log::warn!("Error forwarding file update event: {:?}", err); if let Err(err) = tx.send(()) {
log::warn!("Error forwarding file update event: {:?}", err);
}
} }
} }
} Ok(_) => {}
Ok(_) => {} Err(e) => log::error!("Encountered Error While Watching Files: {}", e),
Err(e) => log::error!("Encountered Error While Watching Files: {}", e), })?;
})?;
watcher.watch(config_dir.as_ref(), RecursiveMode::Recursive)?; watcher.watch(config_dir.as_ref(), RecursiveMode::Recursive)?;
// make sure to not trigger reloads too much by only accepting one reload every 500ms. // make sure to not trigger reloads too much by only accepting one reload every 500ms.
@@ -271,11 +292,15 @@ fn do_detach(log_file_path: impl AsRef<Path>) -> Result<ForkResult> {
} }
} }
let file = std::fs::OpenOptions::new() let file =
.create(true) std::fs::OpenOptions::new().create(true).append(true).open(&log_file_path).unwrap_or_else(
.append(true) |_| {
.open(&log_file_path) panic!(
.unwrap_or_else(|_| panic!("Error opening log file ({}), for writing", log_file_path.as_ref().to_string_lossy())); "Error opening log file ({}), for writing",
log_file_path.as_ref().to_string_lossy()
)
},
);
let fd = file.as_raw_fd(); let fd = file.as_raw_fd();
if nix::unistd::isatty(1)? { if nix::unistd::isatty(1)? {
@@ -297,7 +322,9 @@ fn cleanup_log_dir(log_dir: impl AsRef<Path>) -> Result<()> {
let entry = entry.ok()?; let entry = entry.ok()?;
let path = entry.path(); let path = entry.path();
if let Some(file_name) = path.file_name() { if let Some(file_name) = path.file_name() {
if file_name.to_string_lossy().starts_with("ewwii_") && file_name.to_string_lossy().ends_with(".log") { if file_name.to_string_lossy().starts_with("ewwii_")
&& file_name.to_string_lossy().ends_with(".log")
{
Some(path) Some(path)
} else { } else {
None None

View File

@@ -108,7 +108,9 @@ impl<T: AsRef<str>> T {
/// reference with an empty string. /// reference with an empty string.
pub fn replace_env_var_references(input: String) -> String { pub fn replace_env_var_references(input: String) -> String {
regex!(r"\$\{([^\s]*)\}") regex!(r"\$\{([^\s]*)\}")
.replace_all(&input, |var_name: &regex::Captures| std::env::var(var_name.get(1).unwrap().as_str()).unwrap_or_default()) .replace_all(&input, |var_name: &regex::Captures| {
std::env::var(var_name.get(1).unwrap().as_str()).unwrap_or_default()
})
.into_owned() .into_owned()
} }

View File

@@ -13,7 +13,10 @@ pub enum WidgetInput {
Window(WindowDefinition), Window(WindowDefinition),
} }
pub fn build_gtk_widget(input: WidgetInput, widget_reg: &mut WidgetRegistry) -> Result<gtk::Widget> { pub fn build_gtk_widget(
input: WidgetInput,
widget_reg: &mut WidgetRegistry,
) -> Result<gtk::Widget> {
let node = match input { let node = match input {
WidgetInput::Node(n) => n, WidgetInput::Node(n) => n,
WidgetInput::Window(w) => w.root_widget, WidgetInput::Window(w) => w.root_widget,
@@ -22,7 +25,10 @@ pub fn build_gtk_widget(input: WidgetInput, widget_reg: &mut WidgetRegistry) ->
} }
// TODO: implement the commented lines // TODO: implement the commented lines
fn build_gtk_widget_from_node(root_node: WidgetNode, widget_reg: &mut WidgetRegistry) -> Result<gtk::Widget> { fn build_gtk_widget_from_node(
root_node: WidgetNode,
widget_reg: &mut WidgetRegistry,
) -> Result<gtk::Widget> {
/* /*
When a a new widget is added to the build process, When a a new widget is added to the build process,
make sure to update get_id_to_props_map() found in make sure to update get_id_to_props_map() found in
@@ -32,10 +38,18 @@ fn build_gtk_widget_from_node(root_node: WidgetNode, widget_reg: &mut WidgetRegi
let gtk_widget = match root_node { let gtk_widget = match root_node {
WidgetNode::Box { props, children } => build_gtk_box(props, children, widget_reg)?.upcast(), WidgetNode::Box { props, children } => build_gtk_box(props, children, widget_reg)?.upcast(),
WidgetNode::CenterBox { props, children } => build_center_box(props, children, widget_reg)?.upcast(), WidgetNode::CenterBox { props, children } => {
WidgetNode::EventBox { props, children } => build_gtk_event_box(props, children, widget_reg)?.upcast(), build_center_box(props, children, widget_reg)?.upcast()
WidgetNode::ToolTip { props, children } => build_tooltip(props, children, widget_reg)?.upcast(), }
WidgetNode::CircularProgress { props } => build_circular_progress_bar(props, widget_reg)?.upcast(), WidgetNode::EventBox { props, children } => {
build_gtk_event_box(props, children, widget_reg)?.upcast()
}
WidgetNode::ToolTip { props, children } => {
build_tooltip(props, children, widget_reg)?.upcast()
}
WidgetNode::CircularProgress { props } => {
build_circular_progress_bar(props, widget_reg)?.upcast()
}
WidgetNode::Graph { props } => build_graph(props, widget_reg)?.upcast(), WidgetNode::Graph { props } => build_graph(props, widget_reg)?.upcast(),
WidgetNode::Transform { props } => build_transform(props, widget_reg)?.upcast(), WidgetNode::Transform { props } => build_transform(props, widget_reg)?.upcast(),
WidgetNode::Slider { props } => build_gtk_scale(props, widget_reg)?.upcast(), WidgetNode::Slider { props } => build_gtk_scale(props, widget_reg)?.upcast(),
@@ -47,14 +61,24 @@ fn build_gtk_widget_from_node(root_node: WidgetNode, widget_reg: &mut WidgetRegi
WidgetNode::Input { props } => build_gtk_input(props, widget_reg)?.upcast(), WidgetNode::Input { props } => build_gtk_input(props, widget_reg)?.upcast(),
WidgetNode::Calendar { props } => build_gtk_calendar(props, widget_reg)?.upcast(), WidgetNode::Calendar { props } => build_gtk_calendar(props, widget_reg)?.upcast(),
WidgetNode::ColorButton { props } => build_gtk_color_button(props, widget_reg)?.upcast(), WidgetNode::ColorButton { props } => build_gtk_color_button(props, widget_reg)?.upcast(),
WidgetNode::Expander { props, children } => build_gtk_expander(props, children, widget_reg)?.upcast(), WidgetNode::Expander { props, children } => {
build_gtk_expander(props, children, widget_reg)?.upcast()
}
WidgetNode::ColorChooser { props } => build_gtk_color_chooser(props, widget_reg)?.upcast(), WidgetNode::ColorChooser { props } => build_gtk_color_chooser(props, widget_reg)?.upcast(),
WidgetNode::ComboBoxText { props } => build_gtk_combo_box_text(props, widget_reg)?.upcast(), WidgetNode::ComboBoxText { props } => build_gtk_combo_box_text(props, widget_reg)?.upcast(),
WidgetNode::Checkbox { props } => build_gtk_checkbox(props, widget_reg)?.upcast(), WidgetNode::Checkbox { props } => build_gtk_checkbox(props, widget_reg)?.upcast(),
WidgetNode::Revealer { props, children } => build_gtk_revealer(props, children, widget_reg)?.upcast(), WidgetNode::Revealer { props, children } => {
WidgetNode::Scroll { props, children } => build_gtk_scrolledwindow(props, children, widget_reg)?.upcast(), build_gtk_revealer(props, children, widget_reg)?.upcast()
WidgetNode::OverLay { props, children } => build_gtk_overlay(props, children, widget_reg)?.upcast(), }
WidgetNode::Stack { props, children } => build_gtk_stack(props, children, widget_reg)?.upcast(), WidgetNode::Scroll { props, children } => {
build_gtk_scrolledwindow(props, children, widget_reg)?.upcast()
}
WidgetNode::OverLay { props, children } => {
build_gtk_overlay(props, children, widget_reg)?.upcast()
}
WidgetNode::Stack { props, children } => {
build_gtk_stack(props, children, widget_reg)?.upcast()
}
// WIDGET_NAME_SYSTRAY => build_systray(node)?.upcast(), // WIDGET_NAME_SYSTRAY => build_systray(node)?.upcast(),
unknown => { unknown => {
return Err(anyhow::anyhow!("Cannot build GTK widget from node: {:?}", unknown)); return Err(anyhow::anyhow!("Cannot build GTK widget from node: {:?}", unknown));

View File

@@ -13,13 +13,37 @@ wrapper! {
#[derive(Properties)] #[derive(Properties)]
#[properties(wrapper_type = CircProg)] #[properties(wrapper_type = CircProg)]
pub struct CircProgPriv { pub struct CircProgPriv {
#[property(get, set, nick = "Starting at", blurb = "Starting at", minimum = 0f64, maximum = 100f64, default = 0f64)] #[property(
get,
set,
nick = "Starting at",
blurb = "Starting at",
minimum = 0f64,
maximum = 100f64,
default = 0f64
)]
start_at: RefCell<f64>, start_at: RefCell<f64>,
#[property(get, set, nick = "Value", blurb = "The value", minimum = 0f64, maximum = 100f64, default = 0f64)] #[property(
get,
set,
nick = "Value",
blurb = "The value",
minimum = 0f64,
maximum = 100f64,
default = 0f64
)]
value: RefCell<f64>, value: RefCell<f64>,
#[property(get, set, nick = "Thickness", blurb = "Thickness", minimum = 0f64, maximum = 100f64, default = 1f64)] #[property(
get,
set,
nick = "Thickness",
blurb = "Thickness",
minimum = 0f64,
maximum = 100f64,
default = 1f64
)]
thickness: RefCell<f64>, thickness: RefCell<f64>,
#[property(get, set, nick = "Clockwise", blurb = "Clockwise", default = true)] #[property(get, set, nick = "Clockwise", blurb = "Clockwise", default = true)]
@@ -98,7 +122,9 @@ impl ContainerImpl for CircProgPriv {
fn add(&self, widget: &gtk::Widget) { fn add(&self, widget: &gtk::Widget) {
if let Some(content) = &*self.content.borrow() { if let Some(content) = &*self.content.borrow() {
// TODO: Handle this error when populating children widgets instead // TODO: Handle this error when populating children widgets instead
error_handling_ctx::print_error(anyhow!("Error, trying to add multiple children to a circular-progress widget")); error_handling_ctx::print_error(anyhow!(
"Error, trying to add multiple children to a circular-progress widget"
));
self.parent_remove(content); self.parent_remove(content);
} }
self.parent_add(widget); self.parent_add(widget);
@@ -125,9 +151,13 @@ impl WidgetImpl for CircProgPriv {
if let Some(child) = &*self.content.borrow() { if let Some(child) = &*self.content.borrow() {
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child); let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
(min_child + margin.right as i32 + margin.left as i32, natural_child + margin.right as i32 + margin.left as i32) (
min_child + margin.right as i32 + margin.left as i32,
natural_child + margin.right as i32 + margin.left as i32,
)
} else { } else {
let empty_width = (2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32; let empty_width =
(2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
(empty_width, empty_width) (empty_width, empty_width)
} }
} }
@@ -142,9 +172,13 @@ impl WidgetImpl for CircProgPriv {
if let Some(child) = &*self.content.borrow() { if let Some(child) = &*self.content.borrow() {
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child); let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
(min_child + margin.bottom as i32 + margin.top as i32, natural_child + margin.bottom as i32 + margin.top as i32) (
min_child + margin.bottom as i32 + margin.top as i32,
natural_child + margin.bottom as i32 + margin.top as i32,
)
} else { } else {
let empty_height = (2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32; let empty_height =
(2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
(empty_height, empty_height) (empty_height, empty_height)
} }
} }
@@ -164,9 +198,14 @@ impl WidgetImpl for CircProgPriv {
let margin = styles.margin(gtk::StateFlags::NORMAL); let margin = styles.margin(gtk::StateFlags::NORMAL);
// Padding is not supported yet // Padding is not supported yet
let fg_color: gdk::RGBA = styles.color(gtk::StateFlags::NORMAL); let fg_color: gdk::RGBA = styles.color(gtk::StateFlags::NORMAL);
let bg_color: gdk::RGBA = styles.style_property_for_state("background-color", gtk::StateFlags::NORMAL).get()?; let bg_color: gdk::RGBA = styles
let (start_angle, end_angle) = .style_property_for_state("background-color", gtk::StateFlags::NORMAL)
if clockwise { (0.0, perc_to_rad(value)) } else { (perc_to_rad(100.0 - value), 2f64 * std::f64::consts::PI) }; .get()?;
let (start_angle, end_angle) = if clockwise {
(0.0, perc_to_rad(value))
} else {
(perc_to_rad(100.0 - value), 2f64 * std::f64::consts::PI)
};
let total_width = self.obj().allocated_width() as f64; let total_width = self.obj().allocated_width() as f64;
let total_height = self.obj().allocated_height() as f64; let total_height = self.obj().allocated_height() as f64;

View File

@@ -79,7 +79,9 @@ impl GraphPriv {
*last_updated_at = std::time::Instant::now(); *last_updated_at = std::time::Instant::now();
while let Some(entry) = history.front() { while let Some(entry) = history.front() {
if last_updated_at.duration_since(entry.0).as_millis() as u64 > *self.time_range.borrow() { if last_updated_at.duration_since(entry.0).as_millis() as u64
> *self.time_range.borrow()
{
*last_value = history.pop_front(); *last_value = history.pop_front();
} else { } else {
break; break;
@@ -243,14 +245,24 @@ impl WidgetImpl for GraphPriv {
.iter() .iter()
.map(|(instant, value)| { .map(|(instant, value)| {
let t = last_updated_at.duration_since(*instant).as_millis() as f64; let t = last_updated_at.duration_since(*instant).as_millis() as f64;
self.value_to_point(width, height, t / time_range, (value - min) / value_range) self.value_to_point(
width,
height,
t / time_range,
(value - min) / value_range,
)
}) })
.collect::<VecDeque<(f64, f64)>>(); .collect::<VecDeque<(f64, f64)>>();
// Aad an extra point outside of the graph to extend the line to the left // Aad an extra point outside of the graph to extend the line to the left
if let Some((instant, value)) = extra_point { if let Some((instant, value)) = extra_point {
let t = last_updated_at.duration_since(instant).as_millis() as f64; let t = last_updated_at.duration_since(instant).as_millis() as f64;
let (x, y) = self.value_to_point(width, height, (t - time_range) / time_range, (value - min) / value_range); let (x, y) = self.value_to_point(
width,
height,
(t - time_range) / time_range,
(value - min) / value_range,
);
points.push_front(if *self.vertical.borrow() { (x, -y) } else { (-x, y) }); points.push_front(if *self.vertical.borrow() { (x, -y) } else { (-x, y) });
} }
points points
@@ -263,7 +275,9 @@ impl WidgetImpl for GraphPriv {
cr.clip(); cr.clip();
// Draw Background // Draw Background
let bg_color: gdk::RGBA = styles.style_property_for_state("background-color", gtk::StateFlags::NORMAL).get()?; let bg_color: gdk::RGBA = styles
.style_property_for_state("background-color", gtk::StateFlags::NORMAL)
.get()?;
if bg_color.alpha() > 0.0 { if bg_color.alpha() > 0.0 {
if let Some(first_point) = points.front() { if let Some(first_point) = points.front() {
cr.line_to(first_point.0, height + margin_bottom); cr.line_to(first_point.0, height + margin_bottom);
@@ -273,7 +287,12 @@ impl WidgetImpl for GraphPriv {
} }
cr.line_to(width, height); cr.line_to(width, height);
cr.set_source_rgba(bg_color.red(), bg_color.green(), bg_color.blue(), bg_color.alpha()); cr.set_source_rgba(
bg_color.red(),
bg_color.green(),
bg_color.blue(),
bg_color.alpha(),
);
cr.fill()?; cr.fill()?;
} }
@@ -288,7 +307,12 @@ impl WidgetImpl for GraphPriv {
let line_style = &*self.line_style.borrow(); let line_style = &*self.line_style.borrow();
apply_line_style(line_style.as_str(), cr)?; apply_line_style(line_style.as_str(), cr)?;
cr.set_line_width(thickness); cr.set_line_width(thickness);
cr.set_source_rgba(line_color.red(), line_color.green(), line_color.blue(), line_color.alpha()); cr.set_source_rgba(
line_color.red(),
line_color.green(),
line_color.blue(),
line_color.alpha(),
);
cr.stroke()?; cr.stroke()?;
} }

View File

@@ -126,7 +126,9 @@ impl ContainerImpl for TransformPriv {
fn add(&self, widget: &gtk::Widget) { fn add(&self, widget: &gtk::Widget) {
if let Some(content) = &*self.content.borrow() { if let Some(content) = &*self.content.borrow() {
// TODO: Handle this error when populating children widgets instead // TODO: Handle this error when populating children widgets instead
error_handling_ctx::print_error(anyhow!("Error, trying to add multiple children to a circular-progress widget")); error_handling_ctx::print_error(anyhow!(
"Error, trying to add multiple children to a circular-progress widget"
));
self.parent_remove(content); self.parent_remove(content);
} }
self.parent_add(widget); self.parent_add(widget);
@@ -145,31 +147,43 @@ impl WidgetImpl for TransformPriv {
cr.save()?; cr.save()?;
let transform_origin_x = match &*self.transform_origin_x.borrow() { let transform_origin_x = match &*self.transform_origin_x.borrow() {
Some(rcx) => NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64, Some(rcx) => {
NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64
}
None => 0.0, None => 0.0,
}; };
let transform_origin_y = match &*self.transform_origin_y.borrow() { let transform_origin_y = match &*self.transform_origin_y.borrow() {
Some(rcy) => NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64, Some(rcy) => {
NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64
}
None => 0.0, None => 0.0,
}; };
let translate_x = match &*self.translate_x.borrow() { let translate_x = match &*self.translate_x.borrow() {
Some(tx) => NumWithUnit::from_str(tx)?.pixels_relative_to(total_width as i32) as f64, Some(tx) => {
NumWithUnit::from_str(tx)?.pixels_relative_to(total_width as i32) as f64
}
None => 0.0, None => 0.0,
}; };
let translate_y = match &*self.translate_y.borrow() { let translate_y = match &*self.translate_y.borrow() {
Some(ty) => NumWithUnit::from_str(ty)?.pixels_relative_to(total_height as i32) as f64, Some(ty) => {
NumWithUnit::from_str(ty)?.pixels_relative_to(total_height as i32) as f64
}
None => 0.0, None => 0.0,
}; };
let scale_x = match &*self.scale_x.borrow() { let scale_x = match &*self.scale_x.borrow() {
Some(sx) => NumWithUnit::from_str(sx)?.perc_relative_to(total_width as i32) as f64 / 100.0, Some(sx) => {
NumWithUnit::from_str(sx)?.perc_relative_to(total_width as i32) as f64 / 100.0
}
None => 1.0, None => 1.0,
}; };
let scale_y = match &*self.scale_y.borrow() { let scale_y = match &*self.scale_y.borrow() {
Some(sy) => NumWithUnit::from_str(sy)?.perc_relative_to(total_height as i32) as f64 / 100.0, Some(sy) => {
NumWithUnit::from_str(sy)?.perc_relative_to(total_height as i32) as f64 / 100.0
}
None => 1.0, None => 1.0,
}; };

View File

@@ -80,12 +80,15 @@ impl WidgetRegistry {
for patch_req in patches { for patch_req in patches {
match patch_req { match patch_req {
PatchGtkWidget::Create(wdgt_node, wdgt_id, parent_id) => { PatchGtkWidget::Create(wdgt_node, wdgt_id, parent_id) => {
self.create_widget(wdgt_node, wdgt_id, parent_id).expect("failed to create new gtk widget"); self.create_widget(wdgt_node, wdgt_id, parent_id)
.expect("failed to create new gtk widget");
} }
PatchGtkWidget::Update(widget_id, new_props) => { PatchGtkWidget::Update(widget_id, new_props) => {
self.update_props(widget_id, new_props); self.update_props(widget_id, new_props);
} }
PatchGtkWidget::Remove(widget_id, parent_id) => self.remove_widget(widget_id, parent_id), PatchGtkWidget::Remove(widget_id, parent_id) => {
self.remove_widget(widget_id, parent_id)
}
} }
} }
@@ -124,24 +127,31 @@ impl WidgetRegistry {
// Removals // Removals
for (id, new_info) in &old_map { for (id, new_info) in &old_map {
if !new_map.contains_key(id) { if !new_map.contains_key(id) {
patch.push(PatchGtkWidget::Remove(*id, new_info.parent_id.expect("Parent ID must exist"))); patch.push(PatchGtkWidget::Remove(
*id,
new_info.parent_id.expect("Parent ID must exist"),
));
} }
} }
patch patch
} }
pub fn create_widget(&mut self, widget_node: WidgetNode, widget_id: u64, parent_id: u64) -> Result<()> { pub fn create_widget(
&mut self,
widget_node: WidgetNode,
widget_id: u64,
parent_id: u64,
) -> Result<()> {
log::trace!("Creating '{}'", widget_id); log::trace!("Creating '{}'", widget_id);
if let Some(parent) = self.widgets.get(&parent_id) { if let Some(parent) = self.widgets.get(&parent_id) {
let parent_widget = parent.widget.clone(); let parent_widget = parent.widget.clone();
if let Some(container) = parent_widget.dynamic_cast::<gtk::Container>().ok() { if let Some(container) = parent_widget.dynamic_cast::<gtk::Container>().ok() {
// check if the widget already exists // check if the widget already exists
let position = self let position = self.widgets.get(&widget_id).and_then(|old_entry| {
.widgets container.children().iter().position(|w| w == &old_entry.widget)
.get(&widget_id) });
.and_then(|old_entry| container.children().iter().position(|w| w == &old_entry.widget));
// obliterate that widget.... // obliterate that widget....
// how dare it try to create duplication... // how dare it try to create duplication...
@@ -200,7 +210,11 @@ impl WidgetRegistry {
} }
} }
pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> { pub(super) fn build_gtk_box(
props: Map,
children: Vec<WidgetNode>,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Box> {
// Parse initial props to create the widget: // Parse initial props to create the widget:
let orientation = props let orientation = props
.get("orientation") .get("orientation")
@@ -209,7 +223,8 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
.transpose()? .transpose()?
.unwrap_or(gtk::Orientation::Horizontal); .unwrap_or(gtk::Orientation::Horizontal);
let spacing = props.get("spacing").and_then(|v| v.clone().try_cast::<i64>()).unwrap_or(0) as i32; let spacing =
props.get("spacing").and_then(|v| v.clone().try_cast::<i64>()).unwrap_or(0) as i32;
let space_evenly = get_bool_prop(&props, "space_evenly", Some(true))?; let space_evenly = get_bool_prop(&props, "space_evenly", Some(true))?;
@@ -224,7 +239,9 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
let gtk_widget_clone = gtk_widget.clone(); let gtk_widget_clone = gtk_widget.clone();
let update_fn: UpdateFn = Box::new(move |props: &Map| { let update_fn: UpdateFn = Box::new(move |props: &Map| {
if let Some(orientation_str) = props.get("orientation").and_then(|v| v.clone().try_cast::<String>()) { if let Some(orientation_str) =
props.get("orientation").and_then(|v| v.clone().try_cast::<String>())
{
if let Ok(orientation) = parse_orientation(&orientation_str) { if let Ok(orientation) = parse_orientation(&orientation_str) {
gtk_widget_clone.set_orientation(orientation); gtk_widget_clone.set_orientation(orientation);
} }
@@ -239,14 +256,18 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
} }
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Box"); let id = hash_props_and_type(&props, "Box");
widget_registry.widgets.insert(id, WidgetEntry { widget: gtk_widget.clone().upcast(), update_fn }); widget_registry
.widgets
.insert(id, WidgetEntry { widget: gtk_widget.clone().upcast(), update_fn });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
@@ -266,7 +287,9 @@ pub(super) fn build_gtk_overlay(
bail!("overlay must contain at least one element"); bail!("overlay must contain at least one element");
} }
let mut children = children.into_iter().map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry)); let mut children = children
.into_iter()
.map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry));
// we have more than one child, we can unwrap // we have more than one child, we can unwrap
let first = children.next().unwrap()?; let first = children.next().unwrap()?;
@@ -284,7 +307,11 @@ pub(super) fn build_gtk_overlay(
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_tooltip(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> { pub(super) fn build_tooltip(
props: Map,
children: Vec<WidgetNode>,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Box> {
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0); let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
gtk_widget.set_has_tooltip(true); gtk_widget.set_has_tooltip(true);
@@ -304,8 +331,11 @@ pub(super) fn build_tooltip(props: Map, children: Vec<WidgetNode>, widget_regist
gtk_widget.add(&content_widget); gtk_widget.add(&content_widget);
let tooltip_node = Rc::new(tooltip_node); let tooltip_node = Rc::new(tooltip_node);
let tooltip_widget = build_gtk_widget(WidgetInput::Node(Rc::clone(&tooltip_node).as_ref().clone()), widget_registry) let tooltip_widget = build_gtk_widget(
.expect("Failed to build tooltip widget"); WidgetInput::Node(Rc::clone(&tooltip_node).as_ref().clone()),
widget_registry,
)
.expect("Failed to build tooltip widget");
gtk_widget.connect_query_tooltip(move |_widget, _x, _y, _keyboard_mode, tooltip| { gtk_widget.connect_query_tooltip(move |_widget, _x, _y, _keyboard_mode, tooltip| {
tooltip.set_custom(Some(&tooltip_widget)); tooltip.set_custom(Some(&tooltip_widget));
@@ -317,7 +347,11 @@ pub(super) fn build_tooltip(props: Map, children: Vec<WidgetNode>, widget_regist
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> { pub(super) fn build_center_box(
props: Map,
children: Vec<WidgetNode>,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Box> {
let orientation = props let orientation = props
.get("orientation") .get("orientation")
.and_then(|v| v.clone().try_cast::<String>()) .and_then(|v| v.clone().try_cast::<String>())
@@ -374,14 +408,18 @@ pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>, widget_reg
gtk_widget_clone.set_orientation(orientation); gtk_widget_clone.set_orientation(orientation);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "CenterBox"); let id = hash_props_and_type(&props, "CenterBox");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
@@ -435,7 +473,11 @@ pub(super) fn build_gtk_event_box(
let delta = evt.delta().1; let delta = evt.delta().1;
if delta != 0f64 { if delta != 0f64 {
// Ignore the first event https://bugzilla.gnome.org/show_bug.cgi?id=675959 // Ignore the first event https://bugzilla.gnome.org/show_bug.cgi?id=675959
run_command(timeout, &onscroll, &[if delta < 0f64 { "up" } else { "down" }]); run_command(
timeout,
&onscroll,
&[if delta < 0f64 { "up" } else { "down" }],
);
} }
glib::Propagation::Proceed glib::Propagation::Proceed
}) })
@@ -482,7 +524,8 @@ pub(super) fn build_gtk_event_box(
let display = gdk::Display::default(); let display = gdk::Display::default();
let gdk_window = widget.window(); let gdk_window = widget.window();
if let (Some(display), Some(gdk_window)) = (display, gdk_window) { if let (Some(display), Some(gdk_window)) = (display, gdk_window) {
gdk_window.set_cursor(gdk::Cursor::from_name(&display, &cursor).as_ref()); gdk_window
.set_cursor(gdk::Cursor::from_name(&display, &cursor).as_ref());
} }
} }
glib::Propagation::Proceed glib::Propagation::Proceed
@@ -507,20 +550,38 @@ pub(super) fn build_gtk_event_box(
widget.drag_dest_set( widget.drag_dest_set(
DestDefaults::ALL, DestDefaults::ALL,
&[ &[
TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0), TargetEntry::new(
TargetEntry::new("text/plain", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0), "text/uri-list",
gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
0,
),
TargetEntry::new(
"text/plain",
gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
0,
),
], ],
gdk::DragAction::COPY, gdk::DragAction::COPY,
); );
connect_signal_handler!( connect_signal_handler!(
widget, widget,
widget.connect_drag_data_received(move |_, _, _x, _y, selection_data, _target_type, _timestamp| { widget.connect_drag_data_received(
if let Some(data) = selection_data.uris().first() { move |_, _, _x, _y, selection_data, _target_type, _timestamp| {
run_command(timeout, &ondropped, &[data.to_string(), "file".to_string()]); if let Some(data) = selection_data.uris().first() {
} else if let Some(data) = selection_data.text() { run_command(
run_command(timeout, &ondropped, &[data.to_string(), "text".to_string()]); timeout,
&ondropped,
&[data.to_string(), "file".to_string()],
);
} else if let Some(data) = selection_data.text() {
run_command(
timeout,
&ondropped,
&[data.to_string(), "text".to_string()],
);
}
} }
}) )
); );
} }
@@ -534,12 +595,16 @@ pub(super) fn build_gtk_event_box(
widget.drag_source_unset(); widget.drag_source_unset();
} else { } else {
let target_entry = match dragtype { let target_entry = match dragtype {
DragEntryType::File => { DragEntryType::File => TargetEntry::new(
TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0) "text/uri-list",
} gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
DragEntryType::Text => { 0,
TargetEntry::new("text/plain", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0) ),
} DragEntryType::Text => TargetEntry::new(
"text/plain",
gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
0,
),
}; };
widget.drag_source_set( widget.drag_source_set(
ModifierType::BUTTON1_MASK, ModifierType::BUTTON1_MASK,
@@ -588,7 +653,9 @@ pub(super) fn build_gtk_event_box(
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
@@ -608,21 +675,29 @@ pub(super) fn build_gtk_event_box(
let id = hash_props_and_type(&props, "EventBox"); let id = hash_props_and_type(&props, "EventBox");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_stack(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Stack> { pub(super) fn build_gtk_stack(
props: Map,
children: Vec<WidgetNode>,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Stack> {
let gtk_widget = gtk::Stack::new(); let gtk_widget = gtk::Stack::new();
if children.is_empty() { if children.is_empty() {
return Err(anyhow!("stack must contain at least one element")); return Err(anyhow!("stack must contain at least one element"));
} }
let children = children.into_iter().map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry)); let children = children
.into_iter()
.map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry));
for (i, child) in children.enumerate() { for (i, child) in children.enumerate() {
let child = child?; let child = child?;
@@ -652,21 +727,28 @@ pub(super) fn build_gtk_stack(props: Map, children: Vec<WidgetNode>, widget_regi
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Stack"); let id = hash_props_and_type(&props, "Stack");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry) -> Result<Transform> { pub(super) fn build_transform(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<Transform> {
let widget = Transform::new(); let widget = Transform::new();
let apply_props = |props: &Map, widget: &Transform| -> Result<()> { let apply_props = |props: &Map, widget: &Transform| -> Result<()> {
@@ -715,7 +797,9 @@ pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry)
let _ = apply_props(props, &widget_clone); let _ = apply_props(props, &widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
@@ -729,7 +813,10 @@ pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry)
Ok(widget) Ok(widget)
} }
pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut WidgetRegistry) -> Result<CircProg> { pub(super) fn build_circular_progress_bar(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<CircProg> {
let widget = CircProg::new(); let widget = CircProg::new();
let apply_props = |props: &Map, widget: &CircProg| -> Result<()> { let apply_props = |props: &Map, widget: &CircProg| -> Result<()> {
@@ -759,7 +846,9 @@ pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut Widg
let _ = apply_props(props, &widget_clone); let _ = apply_props(props, &widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
@@ -773,7 +862,10 @@ pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut Widg
Ok(widget) Ok(widget)
} }
pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> Result<super::graph::Graph> { pub(super) fn build_graph(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<super::graph::Graph> {
let widget = super::graph::Graph::new(); let widget = super::graph::Graph::new();
let apply_props = |props: &Map, widget: &super::graph::Graph| -> Result<()> { let apply_props = |props: &Map, widget: &super::graph::Graph| -> Result<()> {
@@ -842,7 +934,9 @@ pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> R
let _ = apply_props(props, &widget_clone); let _ = apply_props(props, &widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
@@ -856,7 +950,10 @@ pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> R
Ok(widget) Ok(widget)
} }
pub(super) fn build_gtk_progress(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ProgressBar> { pub(super) fn build_gtk_progress(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::ProgressBar> {
let gtk_widget = gtk::ProgressBar::new(); let gtk_widget = gtk::ProgressBar::new();
let apply_props = |props: &Map, widget: &gtk::ProgressBar| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::ProgressBar| -> Result<()> {
@@ -887,21 +984,28 @@ pub(super) fn build_gtk_progress(props: Map, widget_registry: &mut WidgetRegistr
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Progress"); let id = hash_props_and_type(&props, "Progress");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Image> { pub(super) fn build_gtk_image(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Image> {
let gtk_widget = gtk::Image::new(); let gtk_widget = gtk::Image::new();
let apply_props = |props: &Map, widget: &gtk::Image| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::Image| -> Result<()> {
@@ -916,7 +1020,8 @@ pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry)
} }
if path.ends_with(".gif") { if path.ends_with(".gif") {
let pixbuf_animation = gtk::gdk_pixbuf::PixbufAnimation::from_file(std::path::PathBuf::from(path))?; let pixbuf_animation =
gtk::gdk_pixbuf::PixbufAnimation::from_file(std::path::PathBuf::from(path))?;
widget.set_from_animation(&pixbuf_animation); widget.set_from_animation(&pixbuf_animation);
} else { } else {
let pixbuf; let pixbuf;
@@ -931,7 +1036,9 @@ pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry)
let reg = regex::Regex::new(r"<svg")?; let reg = regex::Regex::new(r"<svg")?;
reg.replace(&svg_data, &format!("<svg fill=\"{}\"", fill_svg)) reg.replace(&svg_data, &format!("<svg fill=\"{}\"", fill_svg))
}; };
let stream = gtk::gio::MemoryInputStream::from_bytes(&gtk::glib::Bytes::from(svg_data.as_bytes())); let stream = gtk::gio::MemoryInputStream::from_bytes(&gtk::glib::Bytes::from(
svg_data.as_bytes(),
));
pixbuf = gtk::gdk_pixbuf::Pixbuf::from_stream_at_scale( pixbuf = gtk::gdk_pixbuf::Pixbuf::from_stream_at_scale(
&stream, &stream,
image_width, image_width,
@@ -967,21 +1074,28 @@ pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry)
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Image"); let id = hash_props_and_type(&props, "Image");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_button(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Button> { pub(super) fn build_gtk_button(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Button> {
let gtk_widget = gtk::Button::new(); let gtk_widget = gtk::Button::new();
let apply_props = |props: &Map, widget: &gtk::Button| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::Button| -> Result<()> {
@@ -1043,21 +1157,28 @@ pub(super) fn build_gtk_button(props: Map, widget_registry: &mut WidgetRegistry)
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Button"); let id = hash_props_and_type(&props, "Button");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Label> { pub(super) fn build_gtk_label(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Label> {
let gtk_widget = gtk::Label::new(None); let gtk_widget = gtk::Label::new(None);
let apply_props = |props: &Map, widget: &gtk::Label| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::Label| -> Result<()> {
@@ -1080,7 +1201,13 @@ pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry)
} else { } else {
widget.set_max_width_chars(limit_width); widget.set_max_width_chars(limit_width);
} }
apply_ellipsize_settings(&widget, truncate, limit_width, truncate_left, show_truncated); apply_ellipsize_settings(
&widget,
truncate,
limit_width,
truncate_left,
show_truncated,
);
text text
} else { } else {
widget.set_ellipsize(pango::EllipsizeMode::None); widget.set_ellipsize(pango::EllipsizeMode::None);
@@ -1098,7 +1225,8 @@ pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry)
} }
}; };
let unescaped = unescape::unescape(&t).ok_or_else(|| anyhow!("Failed to unescape..."))?; let unescaped =
unescape::unescape(&t).ok_or_else(|| anyhow!("Failed to unescape..."))?;
let final_text = if unindent { util::unindent(&unescaped) } else { unescaped }; let final_text = if unindent { util::unindent(&unescaped) } else { unescaped };
widget.set_text(&final_text); widget.set_text(&final_text);
} else if has_markup { } else if has_markup {
@@ -1148,21 +1276,28 @@ pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry)
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Label"); let id = hash_props_and_type(&props, "Label");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_input(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Entry> { pub(super) fn build_gtk_input(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Entry> {
let gtk_widget = gtk::Entry::new(); let gtk_widget = gtk::Entry::new();
let apply_props = |props: &Map, widget: &gtk::Entry| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::Entry| -> Result<()> {
@@ -1203,21 +1338,28 @@ pub(super) fn build_gtk_input(props: Map, widget_registry: &mut WidgetRegistry)
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Input"); let id = hash_props_and_type(&props, "Input");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Calendar> { pub(super) fn build_gtk_calendar(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Calendar> {
let gtk_widget = gtk::Calendar::new(); let gtk_widget = gtk::Calendar::new();
let apply_props = |props: &Map, widget: &gtk::Calendar| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::Calendar| -> Result<()> {
@@ -1271,7 +1413,9 @@ pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistr
if let Ok(onclick) = get_string_prop(&props, "onclick", None) { if let Ok(onclick) = get_string_prop(&props, "onclick", None) {
connect_signal_handler!( connect_signal_handler!(
widget, widget,
widget.connect_day_selected(move |w| { run_command(timeout, &onclick, &[w.day(), w.month(), w.year()]) }) widget.connect_day_selected(move |w| {
run_command(timeout, &onclick, &[w.day(), w.month(), w.year()])
})
); );
} }
Ok(()) Ok(())
@@ -1284,21 +1428,28 @@ pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistr
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Calendar"); let id = hash_props_and_type(&props, "Calendar");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_combo_box_text(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ComboBoxText> { pub(super) fn build_gtk_combo_box_text(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::ComboBoxText> {
let gtk_widget = gtk::ComboBoxText::new(); let gtk_widget = gtk::ComboBoxText::new();
let apply_props = |props: &Map, widget: &gtk::ComboBoxText| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::ComboBoxText| -> Result<()> {
@@ -1315,7 +1466,11 @@ pub(super) fn build_gtk_combo_box_text(props: Map, widget_registry: &mut WidgetR
connect_signal_handler!( connect_signal_handler!(
widget, widget,
widget.connect_changed(move |widget| { widget.connect_changed(move |widget| {
run_command(timeout, &onchange, &[widget.active_text().unwrap_or_else(|| "".into())]); run_command(
timeout,
&onchange,
&[widget.active_text().unwrap_or_else(|| "".into())],
);
}) })
); );
@@ -1329,14 +1484,18 @@ pub(super) fn build_gtk_combo_box_text(props: Map, widget_registry: &mut WidgetR
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "ComboBoxText"); let id = hash_props_and_type(&props, "ComboBoxText");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
@@ -1382,14 +1541,18 @@ pub(super) fn build_gtk_expander(
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Expander"); let id = hash_props_and_type(&props, "Expander");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
@@ -1425,7 +1588,9 @@ pub(super) fn build_gtk_revealer(
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
@@ -1433,7 +1598,8 @@ pub(super) fn build_gtk_revealer(
match children.len() { match children.len() {
0 => { /* maybe warn? */ } 0 => { /* maybe warn? */ }
1 => { 1 => {
let child_widget = build_gtk_widget(WidgetInput::Node(children[0].clone()), widget_registry)?; let child_widget =
build_gtk_widget(WidgetInput::Node(children[0].clone()), widget_registry)?;
gtk_widget.set_child(Some(&child_widget)); gtk_widget.set_child(Some(&child_widget));
} }
n => { n => {
@@ -1443,14 +1609,19 @@ pub(super) fn build_gtk_revealer(
let id = hash_props_and_type(&props, "Revealer"); let id = hash_props_and_type(&props, "Revealer");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_checkbox(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::CheckButton> { pub(super) fn build_gtk_checkbox(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::CheckButton> {
let gtk_widget = gtk::CheckButton::new(); let gtk_widget = gtk::CheckButton::new();
let apply_props = |props: &Map, widget: &gtk::CheckButton| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::CheckButton| -> Result<()> {
@@ -1464,7 +1635,11 @@ pub(super) fn build_gtk_checkbox(props: Map, widget_registry: &mut WidgetRegistr
connect_signal_handler!( connect_signal_handler!(
widget, widget,
widget.connect_toggled(move |widget| { widget.connect_toggled(move |widget| {
run_command(timeout, if widget.is_active() { &onchecked } else { &onunchecked }, &[] as &[&str]); run_command(
timeout,
if widget.is_active() { &onchecked } else { &onunchecked },
&[] as &[&str],
);
}) })
); );
@@ -1478,21 +1653,28 @@ pub(super) fn build_gtk_checkbox(props: Map, widget_registry: &mut WidgetRegistr
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Checkbox"); let id = hash_props_and_type(&props, "Checkbox");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_color_button(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ColorButton> { pub(super) fn build_gtk_color_button(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::ColorButton> {
let gtk_widget = gtk::ColorButton::builder().build(); let gtk_widget = gtk::ColorButton::builder().build();
let apply_props = |props: &Map, widget: &gtk::ColorButton| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::ColorButton| -> Result<()> {
@@ -1524,21 +1706,28 @@ pub(super) fn build_gtk_color_button(props: Map, widget_registry: &mut WidgetReg
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "ColorButton"); let id = hash_props_and_type(&props, "ColorButton");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_color_chooser(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ColorChooserWidget> { pub(super) fn build_gtk_color_chooser(
props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::ColorChooserWidget> {
let gtk_widget = gtk::ColorChooserWidget::new(); let gtk_widget = gtk::ColorChooserWidget::new();
let apply_props = |props: &Map, widget: &gtk::ColorChooserWidget| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::ColorChooserWidget| -> Result<()> {
@@ -1570,22 +1759,32 @@ pub(super) fn build_gtk_color_chooser(props: Map, widget_registry: &mut WidgetRe
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "ColorChooser"); let id = hash_props_and_type(&props, "ColorChooser");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
Ok(gtk_widget) Ok(gtk_widget)
} }
pub(super) fn build_gtk_scale(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Scale> { pub(super) fn build_gtk_scale(
let gtk_widget = gtk::Scale::new(gtk::Orientation::Horizontal, Some(&gtk::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0))); props: Map,
widget_registry: &mut WidgetRegistry,
) -> Result<gtk::Scale> {
let gtk_widget = gtk::Scale::new(
gtk::Orientation::Horizontal,
Some(&gtk::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)),
);
// Reusable closure for applying props // Reusable closure for applying props
let apply_props = |props: &Map, widget: &gtk::Scale| -> Result<()> { let apply_props = |props: &Map, widget: &gtk::Scale| -> Result<()> {
@@ -1617,14 +1816,18 @@ pub(super) fn build_gtk_scale(props: Map, widget_registry: &mut WidgetRegistry)
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
let id = hash_props_and_type(&props, "Slider"); let id = hash_props_and_type(&props, "Slider");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
@@ -1662,7 +1865,9 @@ pub(super) fn build_gtk_scrolledwindow(
let _ = apply_props(props, &gtk_widget_clone); let _ = apply_props(props, &gtk_widget_clone);
// now re-apply generic widget attrs // now re-apply generic widget attrs
if let Err(err) = resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props) { if let Err(err) =
resolve_rhai_widget_attrs(&gtk_widget_clone.clone().upcast::<gtk::Widget>(), &props)
{
eprintln!("Failed to update widget attrs: {:?}", err); eprintln!("Failed to update widget attrs: {:?}", err);
} }
}); });
@@ -1682,7 +1887,9 @@ pub(super) fn build_gtk_scrolledwindow(
let id = hash_props_and_type(&props, "ScrolledWindow"); let id = hash_props_and_type(&props, "ScrolledWindow");
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() }); widget_registry
.widgets
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?; resolve_rhai_widget_attrs(&gtk_widget.clone().upcast::<gtk::Widget>(), &props)?;
@@ -1730,14 +1937,20 @@ pub(super) fn resolve_rhai_widget_attrs(gtk_widget: &gtk::Widget, props: &Map) -
if let Ok(style_str) = get_string_prop(&props, "style", None) { if let Ok(style_str) = get_string_prop(&props, "style", None) {
let css_provider = gtk::CssProvider::new(); let css_provider = gtk::CssProvider::new();
let scss = format!("* {{ {} }}", style_str); let scss = format!("* {{ {} }}", style_str);
css_provider.load_from_data(grass::from_string(scss, &grass::Options::default())?.as_bytes())?; css_provider
gtk_widget.style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION); .load_from_data(grass::from_string(scss, &grass::Options::default())?.as_bytes())?;
gtk_widget
.style_context()
.add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
} }
if let Ok(css_str) = get_string_prop(&props, "css", None) { if let Ok(css_str) = get_string_prop(&props, "css", None) {
let css_provider = gtk::CssProvider::new(); let css_provider = gtk::CssProvider::new();
css_provider.load_from_data(grass::from_string(css_str, &grass::Options::default())?.as_bytes())?; css_provider
gtk_widget.style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION); .load_from_data(grass::from_string(css_str, &grass::Options::default())?.as_bytes())?;
gtk_widget
.style_context()
.add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
} }
if let Ok(valign) = get_string_prop(&props, "valign", None) { if let Ok(valign) = get_string_prop(&props, "valign", None) {

View File

@@ -14,7 +14,11 @@ where
std::thread::Builder::new() std::thread::Builder::new()
.name("command-execution-thread".to_string()) .name("command-execution-thread".to_string())
.spawn(move || { .spawn(move || {
log::debug!("Running command from widget [timeout: {}ms]: {}", timeout.as_millis(), cmd); log::debug!(
"Running command from widget [timeout: {}ms]: {}",
timeout.as_millis(),
cmd
);
let child = Command::new("/bin/sh").arg("-c").arg(&cmd).spawn(); let child = Command::new("/bin/sh").arg("-c").arg(&cmd).spawn();
match child { match child {
Ok(mut child) => match child.wait_timeout(timeout) { Ok(mut child) => match child.wait_timeout(timeout) {
@@ -89,10 +93,20 @@ pub(super) fn parse_orientation(ori: &str) -> Result<gtk::Orientation> {
} }
/// Gtk Label /// Gtk Label
pub(super) fn apply_ellipsize_settings(label: &gtk::Label, truncate: bool, limit_width: i32, truncate_left: bool, show: bool) { pub(super) fn apply_ellipsize_settings(
label: &gtk::Label,
truncate: bool,
limit_width: i32,
truncate_left: bool,
show: bool,
) {
if (truncate || limit_width != i32::MAX) && show { if (truncate || limit_width != i32::MAX) && show {
label.set_max_width_chars(if limit_width == i32::MAX { -1 } else { limit_width }); label.set_max_width_chars(if limit_width == i32::MAX { -1 } else { limit_width });
label.set_ellipsize(if truncate_left { pango::EllipsizeMode::Start } else { pango::EllipsizeMode::End }); label.set_ellipsize(if truncate_left {
pango::EllipsizeMode::Start
} else {
pango::EllipsizeMode::End
});
} else { } else {
label.set_ellipsize(pango::EllipsizeMode::None); label.set_ellipsize(pango::EllipsizeMode::None);
} }
@@ -146,7 +160,9 @@ where
{ {
if !args.is_empty() { if !args.is_empty() {
let cmd = cmd.replace("{}", &format!("{}", args[0])); let cmd = cmd.replace("{}", &format!("{}", args[0]));
args.iter().enumerate().fold(cmd, |acc, (i, arg)| acc.replace(&format!("{{{}}}", i), &format!("{}", arg))) args.iter()
.enumerate()
.fold(cmd, |acc, (i, arg)| acc.replace(&format!("{{{}}}", i), &format!("{}", arg)))
} else { } else {
cmd.to_string() cmd.to_string()
} }

View File

@@ -37,7 +37,10 @@ pub struct BackendWindowOptionsDef {
impl BackendWindowOptionsDef { impl BackendWindowOptionsDef {
pub fn eval(&self, properties: Map) -> Result<BackendWindowOptions, Error> { pub fn eval(&self, properties: Map) -> Result<BackendWindowOptions, Error> {
Ok(BackendWindowOptions { wayland: self.wayland.eval(properties.clone())?, x11: self.x11.eval(properties)? }) Ok(BackendWindowOptions {
wayland: self.wayland.eval(properties.clone())?,
x11: self.x11.eval(properties)?,
})
} }
// pub fn from_attrs(attrs: &mut Attributes) -> DiagResult<Self> { // pub fn from_attrs(attrs: &mut Attributes) -> DiagResult<Self> {
@@ -119,13 +122,22 @@ impl X11BackendWindowOptionsDef {
struts: match properties.get("reserve") { struts: match properties.get("reserve") {
Some(dynval) => { Some(dynval) => {
let obj_map = dynval.read_lock::<Map>().ok_or(Error::EnumParseErrorMessage("Expected map for reserve"))?; let obj_map = dynval
.read_lock::<Map>()
.ok_or(Error::EnumParseErrorMessage("Expected map for reserve"))?;
let distance_str = obj_map.get("distance").ok_or(Error::MissingField("distance"))?.clone_cast::<String>(); let distance_str = obj_map
.get("distance")
.ok_or(Error::MissingField("distance"))?
.clone_cast::<String>();
let distance = NumWithUnit::from_str(&distance_str)?; let distance = NumWithUnit::from_str(&distance_str)?;
let side = obj_map.get("side").map(|s| s.clone_cast::<String>()).map(|s| Side::from_str(&s)).transpose()?; let side = obj_map
.get("side")
.map(|s| s.clone_cast::<String>())
.map(|s| Side::from_str(&s))
.transpose()?;
X11StrutDefinition { distance, side: side.unwrap_or(Side::default()) } X11StrutDefinition { distance, side: side.unwrap_or(Side::default()) }
} }
@@ -142,7 +154,9 @@ impl X11BackendWindowOptionsDef {
wm_ignore: { wm_ignore: {
let wm_ignore = properties.get("wm_ignore").map(|d| d.clone_cast::<bool>()); let wm_ignore = properties.get("wm_ignore").map(|d| d.clone_cast::<bool>());
wm_ignore.unwrap_or_else(|| properties.get("windowtype").is_none() && properties.get("reserve").is_none()) wm_ignore.unwrap_or_else(|| {
properties.get("windowtype").is_none() && properties.get("reserve").is_none()
})
}, },
}) })
} }

View File

@@ -45,10 +45,16 @@ impl FromStr for NumWithUnit {
type Err = Error; type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
static PATTERN: Lazy<regex::Regex> = Lazy::new(|| regex::Regex::new("^(-?\\d+(?:.\\d+)?)(.*)$").unwrap()); static PATTERN: Lazy<regex::Regex> =
Lazy::new(|| regex::Regex::new("^(-?\\d+(?:.\\d+)?)(.*)$").unwrap());
let captures = PATTERN.captures(s).ok_or_else(|| Error::NumParseFailed(s.to_string()))?; let captures = PATTERN.captures(s).ok_or_else(|| Error::NumParseFailed(s.to_string()))?;
let value = captures.get(1).unwrap().as_str().parse::<f32>().map_err(|_| Error::NumParseFailed(s.to_string()))?; let value = captures
.get(1)
.unwrap()
.as_str()
.parse::<f32>()
.map_err(|_| Error::NumParseFailed(s.to_string()))?;
match captures.get(2).unwrap().as_str() { match captures.get(2).unwrap().as_str() {
"px" | "" => Ok(NumWithUnit::Pixels(value.floor() as i32)), "px" | "" => Ok(NumWithUnit::Pixels(value.floor() as i32)),
"%" => Ok(NumWithUnit::Percent(value)), "%" => Ok(NumWithUnit::Percent(value)),
@@ -109,7 +115,10 @@ mod test {
#[test] #[test]
fn test_parse_coords() { fn test_parse_coords() {
assert_eq!(Coords { x: NumWithUnit::Pixels(50), y: NumWithUnit::Pixels(60) }, Coords::from_str("50x60").unwrap()); assert_eq!(
Coords { x: NumWithUnit::Pixels(50), y: NumWithUnit::Pixels(60) },
Coords::from_str("50x60").unwrap()
);
assert!(Coords::from_str("5060").is_err()); assert!(Coords::from_str("5060").is_err());
} }
} }

View File

@@ -12,7 +12,16 @@ impl Display for EnumParseError {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, smart_default::SmartDefault, serde::Serialize)] #[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
derive_more::Display,
smart_default::SmartDefault,
serde::Serialize,
)]
pub enum WindowStacking { pub enum WindowStacking {
#[default] #[default]
Foreground, Foreground,

View File

@@ -35,7 +35,8 @@ impl FromStr for Coords {
type Err = Error; type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let (sx, sy) = s.split_once(|c: char| c == 'x' || c == '*').ok_or(Error::MalformedCoords)?; let (sx, sy) =
s.split_once(|c: char| c == 'x' || c == '*').ok_or(Error::MalformedCoords)?;
Ok(Coords { x: sx.parse()?, y: sy.parse()? }) Ok(Coords { x: sx.parse()?, y: sy.parse()? })
} }
} }
@@ -113,9 +114,10 @@ impl std::str::FromStr for AnchorPoint {
type Err = EnumParseError; type Err = EnumParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let (x_str, y_str) = s let (x_str, y_str) = s.split_once(' ').ok_or_else(|| EnumParseError {
.split_once(' ') input: s.to_string(),
.ok_or_else(|| EnumParseError { input: s.to_string(), expected: vec!["<horizontal> <vertical>"] })?; expected: vec!["<horizontal> <vertical>"],
})?;
let x = AnchorAlignment::from_x_alignment(x_str)?; let x = AnchorAlignment::from_x_alignment(x_str)?;
let y = AnchorAlignment::from_y_alignment(y_str)?; let y = AnchorAlignment::from_y_alignment(y_str)?;

View File

@@ -1,4 +1,7 @@
use crate::{window::coords::Coords, window::monitor::MonitorIdentifier, window::window_geometry::AnchorPoint}; use crate::{
window::coords::Coords, window::monitor::MonitorIdentifier,
window::window_geometry::AnchorPoint,
};
/// This stores the arguments given in the command line to create a window /// This stores the arguments given in the command line to create a window
/// While creating a window, we combine this with information from the /// While creating a window, we combine this with information from the

View File

@@ -41,10 +41,13 @@ impl WindowInitiator {
// Some(geo) => Some(geo.eval(&vars)?.override_if_given(args.anchor, args.pos, args.size)), // Some(geo) => Some(geo.eval(&vars)?.override_if_given(args.anchor, args.pos, args.size)),
None => None, None => None,
}; };
let monitor = args let monitor = args.monitor.clone().or_else(|| {
.monitor properties
.clone() .get("monitor")?
.or_else(|| properties.get("monitor")?.clone().try_cast::<i64>().map(|n| MonitorIdentifier::Numeric(n as i32))); .clone()
.try_cast::<i64>()
.map(|n| MonitorIdentifier::Numeric(n as i32))
});
Ok(WindowInitiator { Ok(WindowInitiator {
backend_options: window_def.backend_options.eval(properties.clone())?, backend_options: window_def.backend_options.eval(properties.clone())?,
geometry, geometry,
@@ -63,15 +66,23 @@ impl WindowInitiator {
// } // }
} }
fn parse_geometry(val: &Dynamic, args: &WindowArguments, override_geom: bool) -> Result<WindowGeometry> { fn parse_geometry(
val: &Dynamic,
args: &WindowArguments,
override_geom: bool,
) -> Result<WindowGeometry> {
let map = val.clone().cast::<rhai::Map>(); let map = val.clone().cast::<rhai::Map>();
let anchor = map.get("anchor").map(|dyn_value| anchor_point_from_str(&dyn_value.to_string())).transpose()?; let anchor = map
.get("anchor")
.map(|dyn_value| anchor_point_from_str(&dyn_value.to_string()))
.transpose()?;
let mut geom = WindowGeometry { let mut geom = WindowGeometry {
offset: get_coords_from_map(&map, "x", "y")?, offset: get_coords_from_map(&map, "x", "y")?,
size: get_coords_from_map(&map, "width", "height")?, size: get_coords_from_map(&map, "width", "height")?,
anchor_point: anchor.unwrap_or(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::START }), anchor_point: anchor
.unwrap_or(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::START }),
}; };
if override_geom { if override_geom {
@@ -111,7 +122,8 @@ fn anchor_point_from_str(s: &str) -> Result<AnchorPoint> {
match parts.as_slice() { match parts.as_slice() {
[single] => { [single] => {
// Apply to both x and y // Apply to both x and y
let alignment = AnchorAlignment::from_x_alignment(single).or_else(|_| AnchorAlignment::from_y_alignment(single))?; let alignment = AnchorAlignment::from_x_alignment(single)
.or_else(|_| AnchorAlignment::from_y_alignment(single))?;
Ok(AnchorPoint { x: alignment, y: alignment }) Ok(AnchorPoint { x: alignment, y: alignment })
} }
[y_part, x_part] => { [y_part, x_part] => {

View File

@@ -5,15 +5,23 @@ use std::time::Duration;
/// General purpose helpers /// General purpose helpers
pub fn get_string_prop(props: &Map, key: &str, default: Option<&str>) -> Result<String> { pub fn get_string_prop(props: &Map, key: &str, default: Option<&str>) -> Result<String> {
if let Some(value) = props.get(key) { if let Some(value) = props.get(key) {
value.clone().try_cast::<String>().ok_or_else(|| anyhow!("Expected property `{}` to be a string", key)) value
.clone()
.try_cast::<String>()
.ok_or_else(|| anyhow!("Expected property `{}` to be a string", key))
} else { } else {
default.map(|s| s.to_string()).ok_or_else(|| anyhow!("Missing required string property `{}`", key)) default
.map(|s| s.to_string())
.ok_or_else(|| anyhow!("Missing required string property `{}`", key))
} }
} }
pub fn get_bool_prop(props: &Map, key: &str, default: Option<bool>) -> Result<bool> { pub fn get_bool_prop(props: &Map, key: &str, default: Option<bool>) -> Result<bool> {
if let Some(value) = props.get(key) { if let Some(value) = props.get(key) {
value.clone().try_cast::<bool>().ok_or_else(|| anyhow!("Expected property `{}` to be a bool", key)) value
.clone()
.try_cast::<bool>()
.ok_or_else(|| anyhow!("Expected property `{}` to be a bool", key))
} else { } else {
default.map(|s| s).ok_or_else(|| anyhow!("Missing required bool property `{}`", key)) default.map(|s| s).ok_or_else(|| anyhow!("Missing required bool property `{}`", key))
} }
@@ -23,7 +31,8 @@ pub fn get_i64_prop(props: &Map, key: &str, default: Option<i64>) -> Result<i64>
if let Some(v) = value.clone().try_cast::<i64>() { if let Some(v) = value.clone().try_cast::<i64>() {
Ok(v) Ok(v)
} else if let Some(s) = value.clone().try_cast::<String>() { } else if let Some(s) = value.clone().try_cast::<String>() {
s.parse::<i64>().map_err(|_| anyhow!("Expected property `{}` to be an i64 or numeric string", key)) s.parse::<i64>()
.map_err(|_| anyhow!("Expected property `{}` to be an i64 or numeric string", key))
} else { } else {
Err(anyhow!("Expected property `{}` to be an i64 or numeric string", key)) Err(anyhow!("Expected property `{}` to be an i64 or numeric string", key))
} }
@@ -39,7 +48,9 @@ pub fn get_f64_prop(props: &Map, key: &str, default: Option<f64>) -> Result<f64>
} else if let Some(v) = value.clone().try_cast::<i64>() { } else if let Some(v) = value.clone().try_cast::<i64>() {
Ok(v as f64) Ok(v as f64)
} else if let Some(s) = value.clone().try_cast::<String>() { } else if let Some(s) = value.clone().try_cast::<String>() {
s.parse::<f64>().map_err(|_| anyhow!("Expected property `{}` to be an f64, i64, or numeric string", key)) s.parse::<f64>().map_err(|_| {
anyhow!("Expected property `{}` to be an f64, i64, or numeric string", key)
})
} else { } else {
Err(anyhow!("Expected property `{}` to be an f64, i64, or numeric string", key)) Err(anyhow!("Expected property `{}` to be an f64, i64, or numeric string", key))
} }
@@ -53,7 +64,8 @@ pub fn get_i32_prop(props: &Map, key: &str, default: Option<i32>) -> Result<i32>
if let Some(v) = value.clone().try_cast::<i32>() { if let Some(v) = value.clone().try_cast::<i32>() {
Ok(v) Ok(v)
} else if let Some(s) = value.clone().try_cast::<String>() { } else if let Some(s) = value.clone().try_cast::<String>() {
s.parse::<i32>().map_err(|_| anyhow!("Expected property `{}` to be an i32 or numeric string", key)) s.parse::<i32>()
.map_err(|_| anyhow!("Expected property `{}` to be an i32 or numeric string", key))
} else { } else {
Err(anyhow!("Expected property `{}` to be an i32 or numeric string", key)) Err(anyhow!("Expected property `{}` to be an i32 or numeric string", key))
} }
@@ -62,13 +74,23 @@ pub fn get_i32_prop(props: &Map, key: &str, default: Option<i32>) -> Result<i32>
} }
} }
pub fn get_vec_string_prop(props: &Map, key: &str, default: Option<Vec<String>>) -> Result<Vec<String>> { pub fn get_vec_string_prop(
props: &Map,
key: &str,
default: Option<Vec<String>>,
) -> Result<Vec<String>> {
if let Some(value) = props.get(key) { if let Some(value) = props.get(key) {
let array = value.clone().try_cast::<Vec<Dynamic>>().ok_or_else(|| anyhow!("Expected property `{}` to be a vec", key))?; let array = value
.clone()
.try_cast::<Vec<Dynamic>>()
.ok_or_else(|| anyhow!("Expected property `{}` to be a vec", key))?;
array array
.into_iter() .into_iter()
.map(|d| d.try_cast::<String>().ok_or_else(|| anyhow!("Expected all elements of `{}` to be strings", key))) .map(|d| {
d.try_cast::<String>()
.ok_or_else(|| anyhow!("Expected all elements of `{}` to be strings", key))
})
.collect() .collect()
} else { } else {
default.ok_or_else(|| anyhow!("Missing required vec property `{}`", key)) default.ok_or_else(|| anyhow!("Missing required vec property `{}`", key))
@@ -88,7 +110,8 @@ pub fn get_duration_prop(props: &Map, key: &str, default: Option<Duration>) -> R
Ok(Duration::from_secs(s)) Ok(Duration::from_secs(s))
} else if key_str.ends_with("min") { } else if key_str.ends_with("min") {
let num = &key_str[..key_str.len() - 3]; let num = &key_str[..key_str.len() - 3];
let mins = num.parse::<u64>().map_err(|_| anyhow!("Invalid min value: '{}'", key_str))?; let mins =
num.parse::<u64>().map_err(|_| anyhow!("Invalid min value: '{}'", key_str))?;
Ok(Duration::from_secs(mins * 60)) Ok(Duration::from_secs(mins * 60))
} else if key_str.ends_with("h") { } else if key_str.ends_with("h") {
let num = &key_str[..key_str.len() - 1]; let num = &key_str[..key_str.len() - 1];

View File

@@ -5,8 +5,7 @@ use std::env::var;
/// that order, which is the precedence order prescribed by Section 8.2 of POSIX.1-2017. /// that order, which is the precedence order prescribed by Section 8.2 of POSIX.1-2017.
/// If the environment variable is not defined or is malformed use the POSIX locale. /// If the environment variable is not defined or is malformed use the POSIX locale.
pub fn get_locale() -> Locale { pub fn get_locale() -> Locale {
var("LC_ALL") var("LC_ALL").or_else(|_| var("LC_TIME")).or_else(|_| var("LANG")).map_or(Locale::POSIX, |v| {
.or_else(|_| var("LC_TIME")) v.split('.').next().and_then(|x| x.try_into().ok()).unwrap_or_default()
.or_else(|_| var("LANG")) })
.map_or(Locale::POSIX, |v| v.split('.').next().and_then(|x| x.try_into().ok()).unwrap_or_default())
} }

View File

@@ -4,7 +4,20 @@ use serde::{Deserialize, Serialize};
/// The name of a variable /// The name of a variable
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, Debug, RefCast)] #[derive(
Clone,
Hash,
PartialEq,
Eq,
Serialize,
Deserialize,
AsRef,
From,
FromStr,
Display,
Debug,
RefCast,
)]
#[debug("VarName({})", _0)] #[debug("VarName({})", _0)]
pub struct VarName(pub String); pub struct VarName(pub String);
@@ -34,7 +47,20 @@ impl From<AttrName> for VarName {
/// The name of an attribute /// The name of an attribute
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, Debug, RefCast)] #[derive(
Clone,
Hash,
PartialEq,
Eq,
Serialize,
Deserialize,
AsRef,
From,
FromStr,
Display,
Debug,
RefCast,
)]
#[debug("AttrName({})", _0)] #[debug("AttrName({})", _0)]
pub struct AttrName(pub String); pub struct AttrName(pub String);

View File

@@ -91,15 +91,21 @@ pub fn register_all_widgets(engine: &mut Engine) {
// } // }
// }); // });
engine.register_fn("defwindow", |name: &str, props: Map, node: WidgetNode| WidgetNode::DefWindow { engine.register_fn("defwindow", |name: &str, props: Map, node: WidgetNode| {
name: name.to_string(), WidgetNode::DefWindow { name: name.to_string(), props, node: Box::new(node) }
props,
node: Box::new(node),
}); });
engine.register_fn("poll", |var: &str, props: Map| WidgetNode::Poll { var: var.to_string(), props }); engine.register_fn("poll", |var: &str, props: Map| WidgetNode::Poll {
var: var.to_string(),
props,
});
engine.register_fn("listen", |var: &str, props: Map| WidgetNode::Listen { var: var.to_string(), props }); engine.register_fn("listen", |var: &str, props: Map| WidgetNode::Listen {
var: var.to_string(),
props,
});
engine.register_fn("enter", |children: Array| WidgetNode::Enter(children.into_iter().map(|v| v.cast()).collect())); engine.register_fn("enter", |children: Array| {
WidgetNode::Enter(children.into_iter().map(|v| v.cast()).collect())
});
} }

View File

@@ -33,7 +33,13 @@ impl<'a> Diagnostic<'a> {
// bar seperator // bar seperator
out.push_str(&format!(" {bar}\n", bar = "|".dimmed())); out.push_str(&format!(" {bar}\n", bar = "|".dimmed()));
out.push_str(&format!("{:>width$} {sep} {}\n", self.line, self.line_text, width = num_width, sep = "|".dimmed(),)); out.push_str(&format!(
"{:>width$} {sep} {}\n",
self.line,
self.line_text,
width = num_width,
sep = "|".dimmed(),
));
// The caret line, pointing at the column // The caret line, pointing at the column
let caret_padding = " ".repeat(self.column.saturating_sub(1)); let caret_padding = " ".repeat(self.column.saturating_sub(1));
@@ -72,7 +78,12 @@ impl<'a> Diagnostic<'a> {
)); ));
for line in note_lines { for line in note_lines {
out.push_str(&format!("{v} {line:<width$} {v}\n", v = "".green().bold(), line = line.green(), width = width)); out.push_str(&format!(
"{v} {line:<width$} {v}\n",
v = "".green().bold(),
line = line.green(),
width = width
));
} }
out.push_str(&format!( out.push_str(&format!(
@@ -127,21 +138,29 @@ fn get_root_cause<'a>(err: &'a EvalAltResult) -> &'a EvalAltResult {
} }
} }
fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &Engine, code: &str) -> ErrorHelp { fn get_error_info(
root_err: &EvalAltResult,
outer_err: &EvalAltResult,
engine: &Engine,
code: &str,
) -> ErrorHelp {
let (help, hint) = match root_err { let (help, hint) = match root_err {
EvalAltResult::ErrorParsing(..) => ( EvalAltResult::ErrorParsing(..) => (
"Syntax error encountered while parsing.".into(), "Syntax error encountered while parsing.".into(),
"Check for unmatched tokens, invalid constructs, or misplaced punctuation.".into(), "Check for unmatched tokens, invalid constructs, or misplaced punctuation.".into(),
), ),
EvalAltResult::ErrorVariableExists(name, ..) => { EvalAltResult::ErrorVariableExists(name, ..) => (
(format!("Variable '{}' is already defined.", name), "Remove or rename the duplicate declaration.".into()) format!("Variable '{}' is already defined.", name),
} "Remove or rename the duplicate declaration.".into(),
EvalAltResult::ErrorForbiddenVariable(name, ..) => { ),
(format!("Usage of forbidden variable '{}'.", name), "Avoid using reserved or protected variable names.".into()) EvalAltResult::ErrorForbiddenVariable(name, ..) => (
} format!("Usage of forbidden variable '{}'.", name),
EvalAltResult::ErrorVariableNotFound(name, ..) => { "Avoid using reserved or protected variable names.".into(),
(format!("Unknown variable '{}'.", name), "Check for typos or ensure the variable is initialized before use.".into()) ),
} EvalAltResult::ErrorVariableNotFound(name, ..) => (
format!("Unknown variable '{}'.", name),
"Check for typos or ensure the variable is initialized before use.".into(),
),
EvalAltResult::ErrorPropertyNotFound(name, ..) => ( EvalAltResult::ErrorPropertyNotFound(name, ..) => (
format!("Property '{}' not found on this object.", name), format!("Property '{}' not found on this object.", name),
"Verify the property name and the objects available fields.".into(), "Verify the property name and the objects available fields.".into(),
@@ -177,7 +196,10 @@ fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &
format!("Did you mean one of:\n {}", candidates.join("\n ")), format!("Did you mean one of:\n {}", candidates.join("\n ")),
) )
} else { } else {
(format!("Function '{}' is not defined.", fn_sig), "Check spelling, module path, or argument count.".into()) (
format!("Function '{}' is not defined.", fn_sig),
"Check spelling, module path, or argument count.".into(),
)
} }
} }
EvalAltResult::ErrorModuleNotFound(name, ..) => ( EvalAltResult::ErrorModuleNotFound(name, ..) => (
@@ -188,12 +210,14 @@ fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &
format!("Error inside function '{}': {}", fn_name, msg), format!("Error inside function '{}': {}", fn_name, msg),
"Inspect the function implementation and arguments passed.".into(), "Inspect the function implementation and arguments passed.".into(),
), ),
EvalAltResult::ErrorInModule(name, ..) => { EvalAltResult::ErrorInModule(name, ..) => (
(format!("Error while loading module '{}'.", name), "Check the module code for syntax or runtime errors.".into()) format!("Error while loading module '{}'.", name),
} "Check the module code for syntax or runtime errors.".into(),
EvalAltResult::ErrorUnboundThis(..) => { ),
("`this` is unbound in this context.".into(), "Only use `this` inside methods or bound closures.".into()) EvalAltResult::ErrorUnboundThis(..) => (
} "`this` is unbound in this context.".into(),
"Only use `this` inside methods or bound closures.".into(),
),
EvalAltResult::ErrorMismatchDataType(found, expected, ..) => ( EvalAltResult::ErrorMismatchDataType(found, expected, ..) => (
format!("Data type mismatch: found '{}', expected '{}'.", found, expected), format!("Data type mismatch: found '{}', expected '{}'.", found, expected),
"Convert or cast values to the required type.".into(), "Convert or cast values to the required type.".into(),
@@ -206,9 +230,10 @@ fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &
format!("Cannot index into value of type '{}'.", typ), format!("Cannot index into value of type '{}'.", typ),
"Only arrays, maps, bitfields, or strings support indexing.".into(), "Only arrays, maps, bitfields, or strings support indexing.".into(),
), ),
EvalAltResult::ErrorArrayBounds(len, idx, ..) => { EvalAltResult::ErrorArrayBounds(len, idx, ..) => (
(format!("Array index {} out of bounds (0..{}).", idx, len), "Use a valid index within the arrays range.".into()) format!("Array index {} out of bounds (0..{}).", idx, len),
} "Use a valid index within the arrays range.".into(),
),
EvalAltResult::ErrorStringBounds(len, idx, ..) => ( EvalAltResult::ErrorStringBounds(len, idx, ..) => (
format!("String index {} out of bounds (0..{}).", idx, len), format!("String index {} out of bounds (0..{}).", idx, len),
"Ensure you index only valid character positions.".into(), "Ensure you index only valid character positions.".into(),
@@ -217,47 +242,61 @@ fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &
format!("Bitfield index {} out of bounds (0..{}).", idx, len), format!("Bitfield index {} out of bounds (0..{}).", idx, len),
"Use a valid bit position within the bitfields size.".into(), "Use a valid bit position within the bitfields size.".into(),
), ),
EvalAltResult::ErrorFor(..) => { EvalAltResult::ErrorFor(..) => (
("`for` loop value is not iterable.".into(), "Iterate only over arrays, strings, ranges, or iterators.".into()) "`for` loop value is not iterable.".into(),
"Iterate only over arrays, strings, ranges, or iterators.".into(),
),
EvalAltResult::ErrorDataRace(name, ..) => (
format!("Data race detected on '{}'.", name),
"Avoid shared mutable data or use synchronization primitives.".into(),
),
EvalAltResult::ErrorAssignmentToConstant(name, ..) => (
format!("Cannot assign to constant '{}'.", name),
"Constants cannot be reassigned after declaration.".into(),
),
EvalAltResult::ErrorDotExpr(field, ..) => (
format!("Invalid member access '{}'.", field),
"Verify the object has this member or method.".into(),
),
EvalAltResult::ErrorArithmetic(msg, ..) => {
("Arithmetic error encountered.".into(), msg.clone())
} }
EvalAltResult::ErrorDataRace(name, ..) => {
(format!("Data race detected on '{}'.", name), "Avoid shared mutable data or use synchronization primitives.".into())
}
EvalAltResult::ErrorAssignmentToConstant(name, ..) => {
(format!("Cannot assign to constant '{}'.", name), "Constants cannot be reassigned after declaration.".into())
}
EvalAltResult::ErrorDotExpr(field, ..) => {
(format!("Invalid member access '{}'.", field), "Verify the object has this member or method.".into())
}
EvalAltResult::ErrorArithmetic(msg, ..) => ("Arithmetic error encountered.".into(), msg.clone()),
EvalAltResult::ErrorTooManyOperations(..) => ( EvalAltResult::ErrorTooManyOperations(..) => (
"Script exceeded the maximum number of operations.".into(), "Script exceeded the maximum number of operations.".into(),
"Break complex expressions into smaller steps or increase the limit.".into(), "Break complex expressions into smaller steps or increase the limit.".into(),
), ),
EvalAltResult::ErrorTooManyModules(..) => { EvalAltResult::ErrorTooManyModules(..) => (
("Too many modules have been loaded.".into(), "Use fewer modules or increase the module limit.".into()) "Too many modules have been loaded.".into(),
} "Use fewer modules or increase the module limit.".into(),
EvalAltResult::ErrorStackOverflow(..) => { ),
("Call stack overflow detected.".into(), "Check for infinite recursion or deeply nested calls.".into()) EvalAltResult::ErrorStackOverflow(..) => (
} "Call stack overflow detected.".into(),
EvalAltResult::ErrorDataTooLarge(name, ..) => { "Check for infinite recursion or deeply nested calls.".into(),
(format!("Data '{}' is too large to handle.", name), "Use smaller data sizes or adjust engine limits.".into()) ),
} EvalAltResult::ErrorDataTooLarge(name, ..) => (
EvalAltResult::ErrorTerminated(..) => { format!("Data '{}' is too large to handle.", name),
("Script execution was terminated.".into(), "This occurs when a `stop` or external termination is triggered.".into()) "Use smaller data sizes or adjust engine limits.".into(),
} ),
EvalAltResult::ErrorCustomSyntax(msg, options, ..) => { EvalAltResult::ErrorTerminated(..) => (
(format!("Custom syntax error: {}.", msg), format!("Expected one of: {}.", options.join(", "))) "Script execution was terminated.".into(),
} "This occurs when a `stop` or external termination is triggered.".into(),
EvalAltResult::ErrorRuntime(..) => { ),
("Runtime error encountered.".into(), "Inspect the error message and script logic for issues.".into()) EvalAltResult::ErrorCustomSyntax(msg, options, ..) => (
} format!("Custom syntax error: {}.", msg),
EvalAltResult::LoopBreak(..) => { format!("Expected one of: {}.", options.join(", ")),
("`break` used outside of a loop.".into(), "Only use `break` inside `for` or `while` loops.".into()) ),
} EvalAltResult::ErrorRuntime(..) => (
EvalAltResult::Return(..) => { "Runtime error encountered.".into(),
("`return` statement encountered.".into(), "Script terminated with an explicit return value.".into()) "Inspect the error message and script logic for issues.".into(),
} ),
EvalAltResult::LoopBreak(..) => (
"`break` used outside of a loop.".into(),
"Only use `break` inside `for` or `while` loops.".into(),
),
EvalAltResult::Return(..) => (
"`return` statement encountered.".into(),
"Script terminated with an explicit return value.".into(),
),
_ => ("Unknown error".into(), "No additional information available for this error.".into()), _ => ("Unknown error".into(), "No additional information available for this error.".into()),
}; };

View File

@@ -73,7 +73,13 @@ struct TempSignal {
fn register_temp_poll_listen(engine: &mut rhai::Engine) { fn register_temp_poll_listen(engine: &mut rhai::Engine) {
engine.register_type::<TempSignal>(); engine.register_type::<TempSignal>();
engine.register_fn("poll", |var: &str, props: rhai::Map| TempSignal { var: var.to_string(), props }); engine.register_fn("poll", |var: &str, props: rhai::Map| TempSignal {
var: var.to_string(),
props,
});
engine.register_fn("listen", |var: &str, props: rhai::Map| TempSignal { var: var.to_string(), props }); engine.register_fn("listen", |var: &str, props: rhai::Map| TempSignal {
var: var.to_string(),
props,
});
} }

View File

@@ -21,21 +21,26 @@ impl ModuleResolver for SimpleFileResolver {
let base_dir = if let Some(src) = source_path { let base_dir = if let Some(src) = source_path {
PathBuf::from(src).parent().map(|p| p.to_path_buf()).unwrap_or( PathBuf::from(src).parent().map(|p| p.to_path_buf()).unwrap_or(
std::env::current_dir().map_err(|e| EvalAltResult::ErrorSystem("getting current_dir".into(), e.into()))?, std::env::current_dir().map_err(|e| {
EvalAltResult::ErrorSystem("getting current_dir".into(), e.into())
})?,
) )
} else { } else {
std::env::current_dir().map_err(|e| EvalAltResult::ErrorSystem("getting current_dir".into(), e.into()))? std::env::current_dir()
.map_err(|e| EvalAltResult::ErrorSystem("getting current_dir".into(), e.into()))?
}; };
if !file_path.is_absolute() { if !file_path.is_absolute() {
file_path = base_dir.join(file_path); file_path = base_dir.join(file_path);
} }
let full_path = let full_path = file_path
file_path.canonicalize().map_err(|e| EvalAltResult::ErrorSystem(format!("resolving path: {path}"), e.into()))?; .canonicalize()
.map_err(|e| EvalAltResult::ErrorSystem(format!("resolving path: {path}"), e.into()))?;
let script = fs::read_to_string(&full_path) let script = fs::read_to_string(&full_path).map_err(|e| {
.map_err(|e| EvalAltResult::ErrorSystem(format!("reading file: {full_path:?}"), e.into()))?; EvalAltResult::ErrorSystem(format!("reading file: {full_path:?}"), e.into())
})?;
let ast: AST = engine.compile(&script)?; let ast: AST = engine.compile(&script)?;
let scope = Scope::new(); let scope = Scope::new();
@@ -59,7 +64,9 @@ impl<R1: ModuleResolver, R2: ModuleResolver> ModuleResolver for ChainedResolver<
path: &str, path: &str,
pos: Position, pos: Position,
) -> Result<Rc<Module>, Box<EvalAltResult>> { ) -> Result<Rc<Module>, Box<EvalAltResult>> {
self.first.resolve(engine, source_path, path, pos).or_else(|_| self.second.resolve(engine, source_path, path, pos)) self.first
.resolve(engine, source_path, path, pos)
.or_else(|_| self.second.resolve(engine, source_path, path, pos))
} }
} }

View File

@@ -36,7 +36,12 @@ impl ParseConfig {
Ok(self.engine.compile(code)?) Ok(self.engine.compile(code)?)
} }
pub fn eval_code_with(&mut self, code: &str, rhai_scope: Option<Scope>, compiled_ast: Option<&AST>) -> Result<WidgetNode> { pub fn eval_code_with(
&mut self,
code: &str,
rhai_scope: Option<Scope>,
compiled_ast: Option<&AST>,
) -> Result<WidgetNode> {
let mut scope = match rhai_scope { let mut scope = match rhai_scope {
Some(s) => s, Some(s) => s,
None => Scope::new(), None => Scope::new(),
@@ -55,7 +60,8 @@ impl ParseConfig {
} }
pub fn code_from_file<P: AsRef<Path>>(&mut self, file_path: P) -> Result<String> { pub fn code_from_file<P: AsRef<Path>>(&mut self, file_path: P) -> Result<String> {
Ok(fs::read_to_string(&file_path).map_err(|e| anyhow!("Failed to read {:?}: {}", file_path.as_ref(), e))?) Ok(fs::read_to_string(&file_path)
.map_err(|e| anyhow!("Failed to read {:?}: {}", file_path.as_ref(), e))?)
} }
pub fn initial_poll_listen_scope(code: &str) -> Result<Scope> { pub fn initial_poll_listen_scope(code: &str) -> Result<Scope> {

View File

@@ -15,7 +15,8 @@ pub mod wifi {
.output() .output()
.map_err(|e| format!("Failed to run nmcli: {e}"))?; .map_err(|e| format!("Failed to run nmcli: {e}"))?;
let stdout = String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output from nmcli: {e}"))?; let stdout = String::from_utf8(output.stdout)
.map_err(|e| format!("Invalid UTF-8 output from nmcli: {e}"))?;
let mut result = Array::new(); let mut result = Array::new();
for line in stdout.lines() { for line in stdout.lines() {
@@ -40,7 +41,8 @@ pub mod wifi {
.output() .output()
.map_err(|e| format!("Failed to run airport: {e}"))?; .map_err(|e| format!("Failed to run airport: {e}"))?;
let stdout = String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output from airport: {e}"))?; let stdout = String::from_utf8(output.stdout)
.map_err(|e| format!("Invalid UTF-8 output from airport: {e}"))?;
let mut result = Array::new(); let mut result = Array::new();
for line in stdout.lines().skip(1) { for line in stdout.lines().skip(1) {
@@ -100,7 +102,8 @@ pub mod wifi {
.args(&["-t", "-f", "ACTIVE,SSID,SIGNAL,SECURITY", "device", "wifi", "list"]) .args(&["-t", "-f", "ACTIVE,SSID,SIGNAL,SECURITY", "device", "wifi", "list"])
.output() .output()
.map_err(|e| format!("Failed to run nmcli: {e}"))?; .map_err(|e| format!("Failed to run nmcli: {e}"))?;
let stdout = String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output: {}", e))?; let stdout =
String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output: {}", e))?;
let mut map = Map::new(); let mut map = Map::new();
if let Some(line) = stdout.lines().find(|l| l.starts_with("yes:")) { if let Some(line) = stdout.lines().find(|l| l.starts_with("yes:")) {
let parts: Vec<&str> = line.split(':').collect(); let parts: Vec<&str> = line.split(':').collect();
@@ -144,7 +147,10 @@ pub mod wifi {
args.push("password"); args.push("password");
args.push(pw); args.push(pw);
} }
let status = Command::new("nmcli").args(&args).status().map_err(|e| format!("Failed to run nmcli: {e}"))?; let status = Command::new("nmcli")
.args(&args)
.status()
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
if status.success() { if status.success() {
Ok(()) Ok(())
} else { } else {
@@ -196,7 +202,8 @@ pub mod wifi {
.output() .output()
.map_err(|e| format!("Failed to run nmcli: {e}"))?; .map_err(|e| format!("Failed to run nmcli: {e}"))?;
let stdout = String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 from nmcli: {e}"))?; let stdout = String::from_utf8(output.stdout)
.map_err(|e| format!("Invalid UTF-8 from nmcli: {e}"))?;
let ssid = stdout let ssid = stdout
.lines() .lines()
@@ -243,8 +250,10 @@ pub mod wifi {
pub fn disable_adapter() -> Result<(), Box<EvalAltResult>> { pub fn disable_adapter() -> Result<(), Box<EvalAltResult>> {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
let status = let status = Command::new("nmcli")
Command::new("nmcli").args(&["networking", "off"]).status().map_err(|e| format!("Failed to run nmcli: {e}"))?; .args(&["networking", "off"])
.status()
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
if status.success() { if status.success() {
Ok(()) Ok(())
} else { } else {
@@ -275,8 +284,10 @@ pub mod wifi {
pub fn enable_adapter() -> Result<(), Box<EvalAltResult>> { pub fn enable_adapter() -> Result<(), Box<EvalAltResult>> {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
let status = let status = Command::new("nmcli")
Command::new("nmcli").args(&["networking", "on"]).status().map_err(|e| format!("Failed to run nmcli: {e}"))?; .args(&["networking", "on"])
.status()
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
if status.success() { if status.success() {
Ok(()) Ok(())
} else { } else {

View File

@@ -21,7 +21,9 @@ pub mod env {
pub fn get_current_dir() -> Result<String, Box<EvalAltResult>> { pub fn get_current_dir() -> Result<String, Box<EvalAltResult>> {
std::env::current_dir() std::env::current_dir()
.map_err(|e| format!("Failed to get CURRENT DIRECTORY: {e}").into()) .map_err(|e| format!("Failed to get CURRENT DIRECTORY: {e}").into())
.and_then(|p| p.into_os_string().into_string().map_err(|_| "Invalid path encoding".into())) .and_then(|p| {
p.into_os_string().into_string().map_err(|_| "Invalid path encoding".into())
})
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]

View File

@@ -8,7 +8,9 @@ pub mod json {
// parse a JSON string into a Dynamic representing serde_json::Value // parse a JSON string into a Dynamic representing serde_json::Value
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn parse_json(json_str: &str) -> Result<Dynamic, Box<EvalAltResult>> { pub fn parse_json(json_str: &str) -> Result<Dynamic, Box<EvalAltResult>> {
serde_json::from_str::<Value>(json_str).map(Dynamic::from).map_err(|e| format!("Failed to parse JSON: {e}").into()) serde_json::from_str::<Value>(json_str)
.map(Dynamic::from)
.map_err(|e| format!("Failed to parse JSON: {e}").into())
} }
// Turn a dyn JSON val back to a string // Turn a dyn JSON val back to a string

View File

@@ -8,7 +8,9 @@ use rhai::exported_module;
use rhai::module_resolvers::StaticModuleResolver; use rhai::module_resolvers::StaticModuleResolver;
pub fn register_stdlib(resolver: &mut StaticModuleResolver) { pub fn register_stdlib(resolver: &mut StaticModuleResolver) {
use crate::providers::stdlib::{env::env, json::json, math::math, monitor::monitor, text::text}; use crate::providers::stdlib::{
env::env, json::json, math::math, monitor::monitor, text::text,
};
// adding modules // adding modules
let text_mod = exported_module!(text); let text_mod = exported_module!(text);

View File

@@ -22,7 +22,11 @@ pub mod monitor {
} }
pub fn all_resolutions_str() -> String { pub fn all_resolutions_str() -> String {
get_all_monitor_resolutions().into_iter().map(|(w, h)| format!("{w}x{h}")).collect::<Vec<_>>().join(", ") get_all_monitor_resolutions()
.into_iter()
.map(|(w, h)| format!("{w}x{h}"))
.collect::<Vec<_>>()
.join(", ")
} }
pub fn dimensions(index: i64) -> (i64, i64, i64, i64) { pub fn dimensions(index: i64) -> (i64, i64, i64, i64) {

View File

@@ -5,7 +5,10 @@ pub mod text {
pub fn to_slug(text: &str) -> String { pub fn to_slug(text: &str) -> String {
let lower = text.to_lowercase(); let lower = text.to_lowercase();
let sanitized: String = lower.chars().map(|c| if c.is_alphanumeric() || c.is_whitespace() { c } else { ' ' }).collect(); let sanitized: String = lower
.chars()
.map(|c| if c.is_alphanumeric() || c.is_whitespace() { c } else { ' ' })
.collect();
let words = sanitized.split_whitespace(); let words = sanitized.split_whitespace();
let slug = words.collect::<Vec<_>>().join("-"); let slug = words.collect::<Vec<_>>().join("-");
@@ -14,7 +17,10 @@ pub mod text {
} }
pub fn to_camel_case(text: &str) -> String { pub fn to_camel_case(text: &str) -> String {
let cleaned: String = text.chars().map(|c| if c.is_alphanumeric() || c.is_whitespace() { c } else { ' ' }).collect(); let cleaned: String = text
.chars()
.map(|c| if c.is_alphanumeric() || c.is_whitespace() { c } else { ' ' })
.collect();
let words = cleaned.split_whitespace(); let words = cleaned.split_whitespace();

View File

@@ -22,7 +22,12 @@ use tokio::io::BufReader;
use tokio::process::Command; use tokio::process::Command;
use tokio::sync::watch; use tokio::sync::watch;
pub fn handle_listen(var_name: String, props: Map, store: ReactiveVarStore, tx: tokio::sync::mpsc::UnboundedSender<String>) { pub fn handle_listen(
var_name: String,
props: Map,
store: ReactiveVarStore,
tx: tokio::sync::mpsc::UnboundedSender<String>,
) {
let cmd = match get_string_prop(&props, "cmd", Some("")) { let cmd = match get_string_prop(&props, "cmd", Some("")) {
Ok(c) => c, Ok(c) => c,
Err(e) => { Err(e) => {

View File

@@ -27,9 +27,13 @@ use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::watch; use tokio::sync::watch;
pub type ReactiveVarStore = Arc<RwLock<HashMap<String, String>>>; pub type ReactiveVarStore = Arc<RwLock<HashMap<String, String>>>;
pub static SHUTDOWN_REGISTRY: Lazy<Mutex<Vec<watch::Sender<bool>>>> = Lazy::new(|| Mutex::new(Vec::new())); pub static SHUTDOWN_REGISTRY: Lazy<Mutex<Vec<watch::Sender<bool>>>> =
Lazy::new(|| Mutex::new(Vec::new()));
pub fn handle_state_changes(enter_node: WidgetNode, tx: UnboundedSender<String>) -> ReactiveVarStore { pub fn handle_state_changes(
enter_node: WidgetNode,
tx: UnboundedSender<String>,
) -> ReactiveVarStore {
// Enter node is the WidgetNode of Enter() // Enter node is the WidgetNode of Enter()
// it is the very root of every config. // it is the very root of every config.
let store: ReactiveVarStore = Arc::new(RwLock::new(HashMap::new())); let store: ReactiveVarStore = Arc::new(RwLock::new(HashMap::new()));

View File

@@ -22,7 +22,12 @@ use tokio::process::Command;
use tokio::sync::watch; use tokio::sync::watch;
use tokio::time::sleep; use tokio::time::sleep;
pub fn handle_poll(var_name: String, props: Map, store: ReactiveVarStore, tx: tokio::sync::mpsc::UnboundedSender<String>) { pub fn handle_poll(
var_name: String,
props: Map,
store: ReactiveVarStore,
tx: tokio::sync::mpsc::UnboundedSender<String>,
) {
// Parse polling interval // Parse polling interval
let interval = get_duration_prop(&props, "interval", Some(Duration::from_secs(1))); let interval = get_duration_prop(&props, "interval", Some(Duration::from_secs(1)));
let interval = interval.expect("Error parsing interval property of poll"); let interval = interval.expect("Error parsing interval property of poll");

View File

@@ -188,7 +188,13 @@ fn insert_wdgt_info(
id_to_info: &mut HashMap<u64, WidgetInfo>, id_to_info: &mut HashMap<u64, WidgetInfo>,
) -> Result<()> { ) -> Result<()> {
let id = hash_props_and_type(props, widget_type); let id = hash_props_and_type(props, widget_type);
let info = WidgetInfo { node: node.clone(), props: props.clone(), widget_type: widget_type.to_string(), children, parent_id }; let info = WidgetInfo {
node: node.clone(),
props: props.clone(),
widget_type: widget_type.to_string(),
children,
parent_id,
};
id_to_info.insert(id, info); id_to_info.insert(id, info);
Ok(()) Ok(())
} }

View File

@@ -28,7 +28,8 @@ pub trait Host {
/// removed items. /// removed items.
pub async fn register_as_host( pub async fn register_as_host(
con: &zbus::Connection, con: &zbus::Connection,
) -> zbus::Result<(zbus::names::WellKnownName<'static>, proxy::StatusNotifierWatcherProxy<'static>)> { ) -> zbus::Result<(zbus::names::WellKnownName<'static>, proxy::StatusNotifierWatcherProxy<'static>)>
{
let snw = proxy::StatusNotifierWatcherProxy::new(con).await?; let snw = proxy::StatusNotifierWatcherProxy::new(con).await?;
// get a well-known name // get a well-known name
@@ -39,14 +40,17 @@ pub async fn register_as_host(
i += 1; i += 1;
let wellknown = format!("org.freedesktop.StatusNotifierHost-{}-{}", pid, i); let wellknown = format!("org.freedesktop.StatusNotifierHost-{}-{}", pid, i);
let wellknown: zbus::names::WellKnownName = wellknown.try_into().expect("generated well-known name is invalid"); let wellknown: zbus::names::WellKnownName =
wellknown.try_into().expect("generated well-known name is invalid");
let flags = [zbus::fdo::RequestNameFlags::DoNotQueue]; let flags = [zbus::fdo::RequestNameFlags::DoNotQueue];
match con.request_name_with_flags(&wellknown, flags.into_iter().collect()).await? { match con.request_name_with_flags(&wellknown, flags.into_iter().collect()).await? {
PrimaryOwner => break wellknown, PrimaryOwner => break wellknown,
Exists => {} Exists => {}
AlreadyOwner => {} AlreadyOwner => {}
InQueue => unreachable!("request_name_with_flags returned InQueue even though we specified DoNotQueue"), InQueue => unreachable!(
"request_name_with_flags returned InQueue even though we specified DoNotQueue"
),
}; };
}; };
@@ -64,7 +68,10 @@ pub async fn register_as_host(
/// This async function runs forever, and only returns if it gets an error! As such, it is /// This async function runs forever, and only returns if it gets an error! As such, it is
/// recommended to call this via something like `tokio::spawn` that runs this in the /// recommended to call this via something like `tokio::spawn` that runs this in the
/// background. /// background.
pub async fn run_host(host: &mut dyn Host, snw: &proxy::StatusNotifierWatcherProxy<'static>) -> zbus::Error { pub async fn run_host(
host: &mut dyn Host,
snw: &proxy::StatusNotifierWatcherProxy<'static>,
) -> zbus::Error {
// Replacement for ? operator since we're not returning a Result. // Replacement for ? operator since we're not returning a Result.
macro_rules! try_ { macro_rules! try_ {
($e:expr) => { ($e:expr) => {
@@ -116,7 +123,11 @@ pub async fn run_host(host: &mut dyn Host, snw: &proxy::StatusNotifierWatcherPro
host.add_item(svc, item); host.add_item(svc, item);
} }
Err(e) => { Err(e) => {
log::warn!("Could not create StatusNotifierItem from address {:?}: {:?}", svc, e); log::warn!(
"Could not create StatusNotifierItem from address {:?}: {:?}",
svc,
e
);
} }
} }
} }

View File

@@ -30,7 +30,8 @@ enum IconError {
/// Get the fallback GTK icon, as a final fallback if the tray item has no icon. /// Get the fallback GTK icon, as a final fallback if the tray item has no icon.
async fn fallback_icon(size: i32, scale: i32) -> Option<gtk::gdk_pixbuf::Pixbuf> { async fn fallback_icon(size: i32, scale: i32) -> Option<gtk::gdk_pixbuf::Pixbuf> {
let theme = gtk::IconTheme::default().expect("Could not get default gtk theme"); let theme = gtk::IconTheme::default().expect("Could not get default gtk theme");
match theme.load_icon_for_scale("image-missing", size, scale, gtk::IconLookupFlags::FORCE_SIZE) { match theme.load_icon_for_scale("image-missing", size, scale, gtk::IconLookupFlags::FORCE_SIZE)
{
Ok(pb) => pb, Ok(pb) => pb,
Err(e) => { Err(e) => {
log::error!("failed to load \"image-missing\" from default theme: {}", e); log::error!("failed to load \"image-missing\" from default theme: {}", e);
@@ -70,7 +71,10 @@ fn icon_from_pixmap(width: i32, height: i32, mut data: Vec<u8>) -> gtk::gdk_pixb
/// From a list of pixmaps, create an icon from the most appropriately sized one. /// From a list of pixmaps, create an icon from the most appropriately sized one.
/// ///
/// This function returns None if and only if no pixmaps are provided. /// This function returns None if and only if no pixmaps are provided.
fn icon_from_pixmaps(pixmaps: Vec<(i32, i32, Vec<u8>)>, size: i32) -> Option<gtk::gdk_pixbuf::Pixbuf> { fn icon_from_pixmaps(
pixmaps: Vec<(i32, i32, Vec<u8>)>,
size: i32,
) -> Option<gtk::gdk_pixbuf::Pixbuf> {
pixmaps pixmaps
.into_iter() .into_iter()
.max_by(|(w1, h1, _), (w2, h2, _)| { .max_by(|(w1, h1, _), (w2, h2, _)| {
@@ -190,7 +194,9 @@ pub async fn load_icon_from_sni(
}, },
Err(zbus::Error::FDO(e)) => match *e { Err(zbus::Error::FDO(e)) => match *e {
// property not existing is an expected error // property not existing is an expected error
zbus::fdo::Error::UnknownProperty(_) | zbus::fdo::Error::InvalidArgs(_) => Err(IconError::NotAvailable), zbus::fdo::Error::UnknownProperty(_) | zbus::fdo::Error::InvalidArgs(_) => {
Err(IconError::NotAvailable)
}
_ => Err(IconError::DBusPixmap(zbus::Error::FDO(e))), _ => Err(IconError::DBusPixmap(zbus::Error::FDO(e))),
}, },

View File

@@ -65,16 +65,24 @@ impl Item {
} else if service.starts_with(':') { } else if service.starts_with(':') {
( (
service.to_owned(), service.to_owned(),
resolve_pathless_address(con, service, "/".to_owned()) resolve_pathless_address(con, service, "/".to_owned()).await?.ok_or_else(
.await? || {
.ok_or_else(|| zbus::Error::Failure(format!("no StatusNotifierItem found for {service}")))?, zbus::Error::Failure(format!(
"no StatusNotifierItem found for {service}"
))
},
)?,
) )
} else { } else {
return Err(zbus::Error::Address(service.to_owned())); return Err(zbus::Error::Address(service.to_owned()));
} }
}; };
let sni = proxy::StatusNotifierItemProxy::builder(con).destination(addr)?.path(path)?.build().await?; let sni = proxy::StatusNotifierItemProxy::builder(con)
.destination(addr)?
.path(path)?
.build()
.await?;
Ok(Self { sni, gtk_menu: None }) Ok(Self { sni, gtk_menu: None })
} }
@@ -95,7 +103,12 @@ impl Item {
Ok(()) Ok(())
} }
pub async fn popup_menu(&self, event: &gtk::gdk::EventButton, x: i32, y: i32) -> zbus::Result<()> { pub async fn popup_menu(
&self,
event: &gtk::gdk::EventButton,
x: i32,
y: i32,
) -> zbus::Result<()> {
if let Some(menu) = &self.gtk_menu { if let Some(menu) = &self.gtk_menu {
menu.popup_at_pointer(event.downcast_ref::<gtk::gdk::Event>()); menu.popup_at_pointer(event.downcast_ref::<gtk::gdk::Event>());
Ok(()) Ok(())
@@ -131,12 +144,21 @@ struct DBusInterface {
name: String, name: String,
} }
async fn resolve_pathless_address(con: &zbus::Connection, service: &str, path: String) -> zbus::Result<Option<String>> { async fn resolve_pathless_address(
let introspection_xml = con: &zbus::Connection,
IntrospectableProxy::builder(con).destination(service)?.path(path.as_str())?.build().await?.introspect().await?; service: &str,
path: String,
) -> zbus::Result<Option<String>> {
let introspection_xml = IntrospectableProxy::builder(con)
.destination(service)?
.path(path.as_str())?
.build()
.await?
.introspect()
.await?;
let dbus_node = let dbus_node = quick_xml::de::from_str::<DBusNode>(&introspection_xml)
quick_xml::de::from_str::<DBusNode>(&introspection_xml).map_err(|err| zbus::Error::Failure(err.to_string()))?; .map_err(|err| zbus::Error::Failure(err.to_string()))?;
if dbus_node.interface.iter().any(|interface| interface.name == "org.kde.StatusNotifierItem") { if dbus_node.interface.iter().any(|interface| interface.name == "org.kde.StatusNotifierItem") {
// This item implements the desired interface, so bubble it back up // This item implements the desired interface, so bubble it back up
@@ -150,7 +172,9 @@ async fn resolve_pathless_address(con: &zbus::Connection, service: &str, path: S
return Ok(Some(join_to_path(&path, name))); return Ok(Some(join_to_path(&path, name)));
} }
let path = Box::pin(resolve_pathless_address(con, service, join_to_path(&path, name))).await?; let path =
Box::pin(resolve_pathless_address(con, service, join_to_path(&path, name)))
.await?;
if path.is_some() { if path.is_some() {
// Return the first item found from a child // Return the first item found from a child

View File

@@ -75,7 +75,8 @@ impl Watcher {
}; };
if removed_last { if removed_last {
if let Err(e) = Watcher::is_status_notifier_host_registered_refresh(&ctxt).await { if let Err(e) = Watcher::is_status_notifier_host_registered_refresh(&ctxt).await
{
log::error!("failed to signal Watcher: {}", e); log::error!("failed to signal Watcher: {}", e);
} }
} }
@@ -149,7 +150,9 @@ impl Watcher {
if let Err(e) = Watcher::registered_status_notifier_items_refresh(&ctxt).await { if let Err(e) = Watcher::registered_status_notifier_items_refresh(&ctxt).await {
log::error!("failed to signal Watcher: {}", e); log::error!("failed to signal Watcher: {}", e);
} }
if let Err(e) = Watcher::status_notifier_item_unregistered(&ctxt, item.as_ref()).await { if let Err(e) =
Watcher::status_notifier_item_unregistered(&ctxt, item.as_ref()).await
{
log::error!("failed to signal Watcher: {}", e); log::error!("failed to signal Watcher: {}", e);
} }
} }
@@ -160,11 +163,17 @@ impl Watcher {
/// StatusNotifierItemRegistered signal /// StatusNotifierItemRegistered signal
#[dbus_interface(signal)] #[dbus_interface(signal)]
async fn status_notifier_item_registered(ctxt: &zbus::SignalContext<'_>, service: &str) -> zbus::Result<()>; async fn status_notifier_item_registered(
ctxt: &zbus::SignalContext<'_>,
service: &str,
) -> zbus::Result<()>;
/// StatusNotifierItemUnregistered signal /// StatusNotifierItemUnregistered signal
#[dbus_interface(signal)] #[dbus_interface(signal)]
async fn status_notifier_item_unregistered(ctxt: &zbus::SignalContext<'_>, service: &str) -> zbus::Result<()>; async fn status_notifier_item_unregistered(
ctxt: &zbus::SignalContext<'_>,
service: &str,
) -> zbus::Result<()>;
/// RegisteredStatusNotifierItems property /// RegisteredStatusNotifierItems property
#[dbus_interface(property)] #[dbus_interface(property)]
@@ -208,7 +217,9 @@ impl Watcher {
/// Equivalent to `is_status_notifier_host_registered_invalidate`, but without requiring /// Equivalent to `is_status_notifier_host_registered_invalidate`, but without requiring
/// `self`. /// `self`.
async fn is_status_notifier_host_registered_refresh(ctxt: &zbus::SignalContext<'_>) -> zbus::Result<()> { async fn is_status_notifier_host_registered_refresh(
ctxt: &zbus::SignalContext<'_>,
) -> zbus::Result<()> {
zbus::fdo::Properties::properties_changed( zbus::fdo::Properties::properties_changed(
ctxt, ctxt,
Self::name(), Self::name(),
@@ -219,7 +230,9 @@ impl Watcher {
} }
/// Equivalent to `registered_status_notifier_items_invalidate`, but without requiring `self`. /// Equivalent to `registered_status_notifier_items_invalidate`, but without requiring `self`.
async fn registered_status_notifier_items_refresh(ctxt: &zbus::SignalContext<'_>) -> zbus::Result<()> { async fn registered_status_notifier_items_refresh(
ctxt: &zbus::SignalContext<'_>,
) -> zbus::Result<()> {
zbus::fdo::Properties::properties_changed( zbus::fdo::Properties::properties_changed(
ctxt, ctxt,
Self::name(), Self::name(),
@@ -279,7 +292,10 @@ async fn parse_service<'a>(
} }
/// Wait for a DBus service to disappear /// Wait for a DBus service to disappear
async fn wait_for_service_exit(con: &zbus::Connection, service: zbus::names::BusName<'_>) -> zbus::fdo::Result<()> { async fn wait_for_service_exit(
con: &zbus::Connection,
service: zbus::names::BusName<'_>,
) -> zbus::fdo::Result<()> {
let dbus = zbus::fdo::DBusProxy::new(con).await?; let dbus = zbus::fdo::DBusProxy::new(con).await?;
let mut owner_changes = dbus.receive_name_owner_changed_with_args(&[(0, &service)]).await?; let mut owner_changes = dbus.receive_name_owner_changed_with_args(&[(0, &service)]).await?;

View File

@@ -1,5 +1,5 @@
use_small_heuristics = "Max" use_small_heuristics = "Max"
max_width = 130 max_width = 100
use_field_init_shorthand = true use_field_init_shorthand = true
# these where set when we where still on nightly # these where set when we where still on nightly