diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 92fcae450..011ac1475 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,8 @@ name: build on: - #push: - #branches: [master] + push: + branches: [master] pull_request: branches: [master] @@ -15,24 +15,31 @@ jobs: steps: - name: Install dependencies run: sudo apt-get update && sudo apt-get install libgtk-3-dev libgtk-layer-shell-dev - - name: Set up - uses: actions-rs/toolchain@v1 + + - uses: actions/checkout@v4 + + - name: Setup rust + uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly - override: true - components: rustfmt - - uses: actions/checkout@v2 - - uses: Swatinem/rust-cache@v1 - - uses: r7kamura/rust-problem-matchers@v1 + components: clippy,rustfmt + + - name: Load rust cache + uses: Swatinem/rust-cache@v2 + + - name: Setup problem matchers + uses: r7kamura/rust-problem-matchers@v1 + - name: Check formatting run: cargo fmt -- --check - name: Check with default features run: cargo check + - name: Run tests run: cargo test - - name: Build x11 only + + - name: Check x11 only run: cargo check --no-default-features --features=x11 - - name: Build wayland only + - name: Check wayland only run: cargo check --no-default-features --features=wayland - - name: Build no-backend + - name: Check no-backend run: cargo check --no-default-features diff --git a/CHANGELOG.md b/CHANGELOG.md index 86a57d198..8e7559aed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ All notable changes to eww will be listed here, starting at changes since versio - Add support for multiple matchers in `monitor` field - Add `stack` widget (By: vladaviedov) - Add `unindent` property to the label widget, allowing to disable removal of leading spaces (By: nrv) +- Switch to stable rust toolchain (1.76) ## [0.4.0] (04.09.2022) diff --git a/crates/eww/build.rs b/crates/eww/build.rs index f6e6768ca..b2ac29388 100644 --- a/crates/eww/build.rs +++ b/crates/eww/build.rs @@ -1,13 +1,13 @@ use std::process::Command; fn main() { - let output = Command::new("git").args(&["rev-parse", "HEAD"]).output(); + let output = Command::new("git").args(["rev-parse", "HEAD"]).output(); if let Ok(output) = output { if let Ok(hash) = String::from_utf8(output.stdout) { println!("cargo:rustc-env=GIT_HASH={}", hash); println!("cargo:rustc-env=CARGO_PKG_VERSION={} {}", env!("CARGO_PKG_VERSION"), hash); } } - let output = Command::new("git").args(&["show", "-s", "--format=%ci"]).output(); + let output = Command::new("git").args(["show", "-s", "--format=%ci"]).output(); if let Ok(output) = output { if let Ok(date) = String::from_utf8(output.stdout) { println!("cargo:rustc-env=GIT_COMMIT_DATE={}", date); diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index a3bc918a8..8b13a671b 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -146,150 +146,148 @@ impl std::fmt::Debug for App { } impl App { - /// Handle a [`DaemonCommand`] event. + /// Handle a [`DaemonCommand`] event, logging any errors that occur. pub fn handle_command(&mut self, event: DaemonCommand) { + if let Err(err) = self.try_handle_command(event) { + error_handling_ctx::print_error(err); + } + } + + /// Try to handle a [`DaemonCommand`] event. + fn try_handle_command(&mut self, event: DaemonCommand) -> Result<()> { log::debug!("Handling event: {:?}", &event); - let result: Result<_> = try { - match event { - DaemonCommand::NoOp => {} - DaemonCommand::OpenInspector => { - gtk::Window::set_interactive_debugging(true); - } - DaemonCommand::UpdateVars(mappings) => { - for (var_name, new_value) in mappings { - self.update_global_variable(var_name, new_value); - } + match event { + DaemonCommand::NoOp => {} + DaemonCommand::OpenInspector => { + gtk::Window::set_interactive_debugging(true); + } + DaemonCommand::UpdateVars(mappings) => { + for (var_name, new_value) in mappings { + self.update_global_variable(var_name, new_value); } - DaemonCommand::ReloadConfigAndCss(sender) => { - let mut errors = Vec::new(); - - let config_result = config::read_from_eww_paths(&self.paths); - if let Err(e) = config_result.and_then(|new_config| self.load_config(new_config)) { - errors.push(e) - } - match crate::config::scss::parse_scss_from_config(self.paths.get_config_dir()) { - Ok((file_id, css)) => { - if let Err(e) = self.load_css(file_id, &css) { - errors.push(anyhow!(e)); - } - } - Err(e) => { - errors.push(e); - } - } + } + DaemonCommand::ReloadConfigAndCss(sender) => { + let mut errors = Vec::new(); - sender.respond_with_error_list(errors)?; - } - DaemonCommand::KillServer => { - log::info!("Received kill command, stopping server!"); - self.stop_application(); + let config_result = config::read_from_eww_paths(&self.paths); + if let Err(e) = config_result.and_then(|new_config| self.load_config(new_config)) { + errors.push(e) } - DaemonCommand::CloseAll => { - log::info!("Received close command, closing all windows"); - for window_name in self.open_windows.keys().cloned().collect::>() { - self.close_window(&window_name)?; + match crate::config::scss::parse_scss_from_config(self.paths.get_config_dir()) { + Ok((file_id, css)) => { + if let Err(e) = self.load_css(file_id, &css) { + errors.push(anyhow!(e)); + } } - } - DaemonCommand::OpenMany { windows, args, should_toggle, sender } => { - let errors = windows - .iter() - .map(|w| { - let (config_name, id) = w; - if should_toggle && self.open_windows.contains_key(id) { - self.close_window(id) - } else { - log::debug!("Config: {}, id: {}", config_name, id); - let window_args = args - .iter() - .filter(|(win_id, ..)| win_id.is_empty() || win_id == id) - .map(|(_, n, v)| (n.clone(), v.clone())) - .collect(); - self.open_window(&WindowArguments::new_from_args( - id.to_string(), - config_name.clone(), - window_args, - )?) - } - }) - .filter_map(Result::err); - sender.respond_with_error_list(errors)?; - } - DaemonCommand::OpenWindow { - window_name, - instance_id, - pos, - size, - anchor, - screen: monitor, - should_toggle, - duration, - sender, - args, - } => { - let instance_id = instance_id.unwrap_or_else(|| window_name.clone()); - - let is_open = self.open_windows.contains_key(&instance_id); - - let result = if should_toggle && is_open { - self.close_window(&instance_id) - } else { - self.open_window(&WindowArguments { - instance_id, - window_name, - pos, - size, - monitor, - anchor, - duration, - args: args.unwrap_or_default().into_iter().collect(), - }) - }; - - sender.respond_with_result(result)?; - } - DaemonCommand::CloseWindows { windows, sender } => { - let errors = windows.iter().map(|window| self.close_window(window)).filter_map(Result::err); - sender.respond_with_error_list(errors)?; - } - DaemonCommand::PrintState { all, sender } => { - let scope_graph = self.scope_graph.borrow(); - let used_globals_names = scope_graph.currently_used_globals(); - let output = scope_graph - .global_scope() - .data - .iter() - .filter(|(key, _)| all || used_globals_names.contains(*key)) - .map(|(key, value)| format!("{}: {}", key, value)) - .join("\n"); - sender.send_success(output)? - } - DaemonCommand::GetVar { name, sender } => { - let scope_graph = &*self.scope_graph.borrow(); - let vars = &scope_graph.global_scope().data; - match vars.get(name.as_str()) { - Some(x) => sender.send_success(x.to_string())?, - None => sender.send_failure(format!("Variable not found \"{}\"", name))?, + Err(e) => { + errors.push(e); } } - DaemonCommand::ListWindows(sender) => { - let output = self.eww_config.get_windows().keys().join("\n"); - sender.send_success(output)? - } - DaemonCommand::ListActiveWindows(sender) => { - let output = self.open_windows.iter().map(|(id, window)| format!("{id}: {}", window.name)).join("\n"); - sender.send_success(output)? - } - DaemonCommand::PrintDebug(sender) => { - let output = format!("{:#?}", &self); - sender.send_success(output)? + + sender.respond_with_error_list(errors)?; + } + DaemonCommand::KillServer => { + log::info!("Received kill command, stopping server!"); + self.stop_application(); + } + DaemonCommand::CloseAll => { + log::info!("Received close command, closing all windows"); + for window_name in self.open_windows.keys().cloned().collect::>() { + self.close_window(&window_name)?; } - DaemonCommand::PrintGraph(sender) => sender.send_success(self.scope_graph.borrow().visualize())?, } - }; + DaemonCommand::OpenMany { windows, args, should_toggle, sender } => { + let errors = windows + .iter() + .map(|w| { + let (config_name, id) = w; + if should_toggle && self.open_windows.contains_key(id) { + self.close_window(id) + } else { + log::debug!("Config: {}, id: {}", config_name, id); + let window_args = args + .iter() + .filter(|(win_id, ..)| win_id.is_empty() || win_id == id) + .map(|(_, n, v)| (n.clone(), v.clone())) + .collect(); + self.open_window(&WindowArguments::new_from_args(id.to_string(), config_name.clone(), window_args)?) + } + }) + .filter_map(Result::err); + sender.respond_with_error_list(errors)?; + } + DaemonCommand::OpenWindow { + window_name, + instance_id, + pos, + size, + anchor, + screen: monitor, + should_toggle, + duration, + sender, + args, + } => { + let instance_id = instance_id.unwrap_or_else(|| window_name.clone()); + + let is_open = self.open_windows.contains_key(&instance_id); + + let result = if should_toggle && is_open { + self.close_window(&instance_id) + } else { + self.open_window(&WindowArguments { + instance_id, + window_name, + pos, + size, + monitor, + anchor, + duration, + args: args.unwrap_or_default().into_iter().collect(), + }) + }; - if let Err(err) = result { - error_handling_ctx::print_error(err); + sender.respond_with_result(result)?; + } + DaemonCommand::CloseWindows { windows, sender } => { + let errors = windows.iter().map(|window| self.close_window(window)).filter_map(Result::err); + sender.respond_with_error_list(errors)?; + } + DaemonCommand::PrintState { all, sender } => { + let scope_graph = self.scope_graph.borrow(); + let used_globals_names = scope_graph.currently_used_globals(); + let output = scope_graph + .global_scope() + .data + .iter() + .filter(|(key, _)| all || used_globals_names.contains(*key)) + .map(|(key, value)| format!("{}: {}", key, value)) + .join("\n"); + sender.send_success(output)? + } + DaemonCommand::GetVar { name, sender } => { + let scope_graph = &*self.scope_graph.borrow(); + let vars = &scope_graph.global_scope().data; + match vars.get(name.as_str()) { + Some(x) => sender.send_success(x.to_string())?, + None => sender.send_failure(format!("Variable not found \"{}\"", name))?, + } + } + DaemonCommand::ListWindows(sender) => { + let output = self.eww_config.get_windows().keys().join("\n"); + sender.send_success(output)? + } + DaemonCommand::ListActiveWindows(sender) => { + let output = self.open_windows.iter().map(|(id, window)| format!("{id}: {}", window.name)).join("\n"); + sender.send_success(output)? + } + DaemonCommand::PrintDebug(sender) => { + let output = format!("{:#?}", &self); + sender.send_success(output)? + } + DaemonCommand::PrintGraph(sender) => sender.send_success(self.scope_graph.borrow().visualize())?, } + Ok(()) } /// Fully stop eww: @@ -375,7 +373,7 @@ impl App { self.instance_id_to_args.insert(instance_id.to_string(), window_args.clone()); - let open_result: Result<_> = try { + let open_result: Result<_> = (|| { let window_name: &str = &window_args.window_name; let window_def = self.eww_config.get_window(window_name)?.clone(); @@ -461,7 +459,8 @@ impl App { } self.open_windows.insert(instance_id.to_string(), eww_window); - }; + Ok(()) + })(); if let Err(err) = open_result { self.failed_windows.insert(instance_id.to_string()); @@ -499,15 +498,15 @@ impl App { 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()) { static PATTERN: Lazy = Lazy::new(|| regex::Regex::new(r"[^:]*:(\d+):(\d+)(.*)$").unwrap()); - let nice_error_option: Option<_> = try { + let nice_error_option: Option<_> = (|| { let captures = PATTERN.captures(err.message())?; let line = captures.get(1).unwrap().as_str().parse::().ok()?; let msg = captures.get(3).unwrap().as_str(); let db = error_handling_ctx::FILE_DATABASE.read().ok()?; let line_range = db.line_range(file_id, line - 1).ok()?; let span = Span(line_range.start, line_range.end - 1, file_id); - DiagError(gen_diagnostic!(msg, span)) - }; + Some(DiagError(gen_diagnostic!(msg, span))) + })(); match nice_error_option { Some(error) => Err(anyhow!(error)), None => Err(anyhow!("CSS error: {}", err.message())), diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index 59229c800..65eaf46e8 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -45,12 +45,8 @@ impl EwwConfig { let config = Config::generate_from_main_file(files, yuck_path)?; // run some validations on the configuration - let magic_globals: Vec<_> = inbuilt::INBUILT_VAR_NAMES - .iter() - .chain(inbuilt::MAGIC_CONSTANT_NAMES) - .into_iter() - .map(|x| VarName::from(*x)) - .collect(); + let magic_globals: Vec<_> = + inbuilt::INBUILT_VAR_NAMES.iter().chain(inbuilt::MAGIC_CONSTANT_NAMES).map(|x| VarName::from(*x)).collect(); yuck::config::validate::validate(&config, magic_globals)?; for (name, def) in &config.widget_definitions { diff --git a/crates/eww/src/config/mod.rs b/crates/eww/src/config/mod.rs index 21dab5c05..3ffa02574 100644 --- a/crates/eww/src/config/mod.rs +++ b/crates/eww/src/config/mod.rs @@ -6,4 +6,3 @@ pub mod system_stats; pub mod window_definition; pub use eww_config::*; pub use script_var::*; -pub use window_definition::*; diff --git a/crates/eww/src/display_backend.rs b/crates/eww/src/display_backend.rs index e653123ee..74a6033e2 100644 --- a/crates/eww/src/display_backend.rs +++ b/crates/eww/src/display_backend.rs @@ -197,8 +197,8 @@ mod platform_x11 { let strut_list: Vec = match strut_def.side { Side::Left => vec![dist + mon_x, 0, 0, 0, mon_x, mon_end_y, 0, 0, 0, 0, 0, 0], Side::Right => vec![0, root_window_geometry.width as u32 - mon_end_x + dist, 0, 0, 0, 0, mon_x, mon_end_y, 0, 0, 0, 0], - Side::Top => vec![0, 0, dist + mon_y as u32, 0, 0, 0, 0, 0, mon_x, mon_end_x, 0, 0], - Side::Bottom => vec![0, 0, 0, root_window_geometry.height as u32 - mon_end_y + dist, 0, 0, 0, 0, 0, 0, mon_x as u32, mon_end_x], + Side::Top => vec![0, 0, dist + mon_y, 0, 0, 0, 0, 0, mon_x, mon_end_x, 0, 0], + Side::Bottom => vec![0, 0, 0, root_window_geometry.height as u32 - mon_end_y + dist, 0, 0, 0, 0, 0, 0, mon_x, mon_end_x], // This should never happen but if it does the window will be anchored on the // right of the screen }.iter().flat_map(|x| x.to_le_bytes().to_vec()).collect(); diff --git a/crates/eww/src/geometry.rs b/crates/eww/src/geometry.rs index 1b845a5d5..bde5180fc 100644 --- a/crates/eww/src/geometry.rs +++ b/crates/eww/src/geometry.rs @@ -1,7 +1,4 @@ use derive_more::*; -pub trait Rectangular { - fn get_rect(&self) -> Rect; -} #[derive(Debug, Copy, Clone, Eq, PartialEq, Display)] #[display(fmt = ".x*.y:.width*.height")] @@ -11,15 +8,3 @@ pub struct Rect { pub width: i32, pub height: i32, } - -impl Rectangular for Rect { - fn get_rect(&self) -> Rect { - *self - } -} - -impl Rectangular for gdk::Rectangle { - fn get_rect(&self) -> Rect { - Rect { x: self.x(), y: self.y(), width: self.width(), height: self.height() } - } -} diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index a2fc52829..aac52a6f7 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -1,10 +1,3 @@ -#![feature(trace_macros)] -#![feature(extract_if)] -#![feature(box_patterns)] -#![feature(slice_concat_trait)] -#![feature(try_blocks)] -#![feature(hash_extract_if)] -#![feature(let_chains)] #![allow(rustdoc::private_intra_doc_links)] extern crate gtk; @@ -190,7 +183,7 @@ fn listen_for_daemon_response(mut recv: DaemonResponseReceiver) { /// 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> { 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")?; + let mut stream = attempt_connect(paths.get_ipc_socket_file(), connect_attempts).context("Failed to connect to daemon")?; log::debug!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display()); client::do_server_call(&mut stream, action).context("Error while forwarding command to server") } diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index 0847b72be..717d4d92a 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -33,7 +33,7 @@ pub fn init(evt_send: UnboundedSender) -> ScriptVarHandlerHandle .build() .expect("Failed to initialize tokio runtime for script var handlers"); rt.block_on(async { - let _: Result<_> = try { + let _: Result<_> = async { let mut handler = ScriptVarHandler { listen_handler: ListenVarHandler::new(evt_send.clone())?, poll_handler: PollVarHandler::new(evt_send)?, @@ -53,7 +53,9 @@ pub fn init(evt_send: UnboundedSender) -> ScriptVarHandlerHandle }, else => break, }; - }; + Ok(()) + } + .await; }) }) .expect("Failed to start script-var-handler thread"); @@ -158,9 +160,10 @@ impl PollVarHandler { self.poll_handles.insert(var.name.clone(), cancellation_token.clone()); let evt_send = self.evt_send.clone(); tokio::spawn(async move { - let result: Result<_> = try { + let result: Result<_> = (|| { evt_send.send(app::DaemonCommand::UpdateVars(vec![(var.name.clone(), run_poll_once(&var)?)]))?; - }; + Ok(()) + })(); if let Err(err) = result { crate::error_handling_ctx::print_error(err); } @@ -168,9 +171,10 @@ impl PollVarHandler { crate::loop_select_exiting! { _ = cancellation_token.cancelled() => break, _ = tokio::time::sleep(var.interval) => { - let result: Result<_> = try { + let result: Result<_> = (|| { evt_send.send(app::DaemonCommand::UpdateVars(vec![(var.name.clone(), run_poll_once(&var)?)]))?; - }; + Ok(()) + })(); if let Err(err) = result { crate::error_handling_ctx::print_error(err); @@ -233,17 +237,18 @@ impl ListenVarHandler { let evt_send = self.evt_send.clone(); tokio::spawn(async move { - crate::try_logging_errors!(format!("Executing listen var-command {}", &var.command) => { + let result: Result<_> = async { let mut handle = unsafe { tokio::process::Command::new("sh") - .args(&["-c", &var.command]) - .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::piped()) - .stdin(std::process::Stdio::null()) - .pre_exec(|| { - let _ = setpgid(Pid::from_raw(0), Pid::from_raw(0)); - Ok(()) - }).spawn()? + .args(["-c", &var.command]) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .stdin(std::process::Stdio::null()) + .pre_exec(|| { + let _ = setpgid(Pid::from_raw(0), Pid::from_raw(0)); + Ok(()) + }) + .spawn()? }; let mut stdout_lines = BufReader::new(handle.stdout.take().unwrap()).lines(); let mut stderr_lines = BufReader::new(handle.stderr.take().unwrap()).lines(); @@ -268,7 +273,19 @@ impl ListenVarHandler { if let Some(completion_notify) = completion_notify { completion_notify.completed().await; } - }); + Ok(()) + } + .await; + + if let Err(err) = result { + log::error!( + "[{}:{}] Error while executing listen-var command {}: {:?}", + ::std::file!(), + ::std::line!(), + &var.command, + err + ); + } }); } diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 5a7ecc873..66cb4f640 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -26,7 +26,7 @@ pub fn initialize_server( ) -> Result { 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(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?; log::info!("Loading paths: {}", &paths); @@ -42,7 +42,7 @@ pub fn initialize_server( }; if should_daemonize { - let fork_result = do_detach(&paths.get_log_file())?; + let fork_result = do_detach(paths.get_log_file())?; if fork_result == ForkResult::Parent { return Ok(ForkResult::Parent); diff --git a/crates/eww/src/util.rs b/crates/eww/src/util.rs index fb5ae1674..b20fc2e9b 100644 --- a/crates/eww/src/util.rs +++ b/crates/eww/src/util.rs @@ -2,16 +2,6 @@ use extend::ext; use itertools::Itertools; use std::fmt::Write; -#[macro_export] -macro_rules! try_logging_errors { - ($context:expr => $code:block) => {{ - let result: Result<_> = try { $code }; - if let Err(err) = result { - log::error!("[{}:{}] Error while {}: {:?}", ::std::file!(), ::std::line!(), $context, err); - } - }}; -} - #[macro_export] macro_rules! print_result_err { ($context:expr, $result:expr $(,)?) => {{ diff --git a/crates/eww/src/widgets/circular_progressbar.rs b/crates/eww/src/widgets/circular_progressbar.rs index 02d015511..76861afcd 100644 --- a/crates/eww/src/widgets/circular_progressbar.rs +++ b/crates/eww/src/widgets/circular_progressbar.rs @@ -116,6 +116,7 @@ fn calc_widget_lowest_preferred_dimension(widget: >k::Widget) -> (i32, i32) { } impl BinImpl for CircProgPriv {} + impl WidgetImpl for CircProgPriv { // We overwrite preferred_* so that overflowing content from the children gets cropped // We return min(child_width, child_height) @@ -154,29 +155,26 @@ impl WidgetImpl for CircProgPriv { } fn draw(&self, cr: &cairo::Context) -> Inhibit { - let res: Result<()> = try { + let res: Result<()> = (|| { let value = *self.value.borrow(); - let start_at = *self.start_at.borrow() as f64; - let thickness = *self.thickness.borrow() as f64; - let clockwise = *self.clockwise.borrow() as bool; + let start_at = *self.start_at.borrow(); + let thickness = *self.thickness.borrow(); + let clockwise = *self.clockwise.borrow(); let styles = self.obj().style_context(); let margin = styles.margin(gtk::StateFlags::NORMAL); // Padding is not supported yet 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 (start_angle, end_angle) = if clockwise { - (0.0, perc_to_rad(value as f64)) - } else { - (perc_to_rad(100.0 - value as f64), 2f64 * std::f64::consts::PI) - }; + 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_height = self.obj().allocated_height() as f64; let center = (total_width / 2.0, total_height / 2.0); let circle_width = total_width - margin.left as f64 - margin.right as f64; - let circle_height = total_height as f64 - margin.top as f64 - margin.bottom as f64; + let circle_height = total_height - margin.top as f64 - margin.bottom as f64; let outer_ring = f64::min(circle_width, circle_height) / 2.0; let inner_ring = (f64::min(circle_width, circle_height) / 2.0) - thickness; @@ -221,7 +219,8 @@ impl WidgetImpl for CircProgPriv { cr.reset_clip(); cr.restore()?; } - }; + Ok(()) + })(); if let Err(error) = res { error_handling_ctx::print_error(error) diff --git a/crates/eww/src/widgets/def_widget_macro.rs b/crates/eww/src/widgets/def_widget_macro.rs index 282802970..311ce6f85 100644 --- a/crates/eww/src/widgets/def_widget_macro.rs +++ b/crates/eww/src/widgets/def_widget_macro.rs @@ -19,14 +19,14 @@ macro_rules! def_widget { // If an attribute is explicitly marked as optional (? appended to type) // the attribute will still show up here, as a `None` value. Otherwise, all values in this map // will be `Some`. - let attr_map: Result>> = try { - ::maplit::hashmap! { + let attr_map: Result>> = (|| { + Ok(::maplit::hashmap! { $( eww_shared_util::AttrName(::std::stringify!($attr_name).to_owned()) => def_widget!(@get_value $args, &::std::stringify!($attr_name).replace('_', "-"), $(? $($optional)?)? $(= $default)?) ),* - } - }; + }) + })(); // Only proceed if any attributes from this `prop` where actually provided if let Ok(attr_map) = attr_map { diff --git a/crates/eww/src/widgets/graph.rs b/crates/eww/src/widgets/graph.rs index 60814d993..a3a77135a 100644 --- a/crates/eww/src/widgets/graph.rs +++ b/crates/eww/src/widgets/graph.rs @@ -171,7 +171,7 @@ impl WidgetImpl for GraphPriv { } fn draw(&self, cr: &cairo::Context) -> Inhibit { - let res: Result<()> = try { + let res: Result<()> = (|| { let history = &*self.history.borrow(); let extra_point = *self.extra_point.borrow(); @@ -179,7 +179,7 @@ impl WidgetImpl for GraphPriv { let (min, max) = { let mut max = *self.max.borrow(); let min = *self.min.borrow(); - let dynamic = *self.dynamic.borrow() as bool; + let dynamic = *self.dynamic.borrow(); if dynamic { // Check for points higher than max for (_, value) in history { @@ -269,7 +269,8 @@ impl WidgetImpl for GraphPriv { cr.reset_clip(); cr.restore()?; - }; + Ok(()) + })(); if let Err(error) = res { error_handling_ctx::print_error(error) diff --git a/crates/eww/src/widgets/mod.rs b/crates/eww/src/widgets/mod.rs index e5e25b7c6..6e7c6c933 100644 --- a/crates/eww/src/widgets/mod.rs +++ b/crates/eww/src/widgets/mod.rs @@ -11,7 +11,7 @@ pub mod widget_definitions; /// This command may use placeholders which will be replaced by the values of the arguments given. /// This can either be the placeholder `{}`, which will be replaced by the first argument, /// Or a placeholder like `{0}`, `{1}`, etc, which will refer to the respective argument. -pub(self) fn run_command(timeout: std::time::Duration, cmd: &str, args: &[T]) +fn run_command(timeout: std::time::Duration, cmd: &str, args: &[T]) where T: 'static + std::fmt::Display + Send + Sync + Clone, { diff --git a/crates/eww/src/widgets/transform.rs b/crates/eww/src/widgets/transform.rs index 6474416c0..faa9aa466 100644 --- a/crates/eww/src/widgets/transform.rs +++ b/crates/eww/src/widgets/transform.rs @@ -122,7 +122,7 @@ impl ContainerImpl for TransformPriv { impl BinImpl for TransformPriv {} impl WidgetImpl for TransformPriv { fn draw(&self, cr: &cairo::Context) -> Inhibit { - let res: Result<()> = try { + let res: Result<()> = (|| { let rotate = *self.rotate.borrow(); let total_width = self.obj().allocated_width() as f64; let total_height = self.obj().allocated_height() as f64; @@ -159,7 +159,8 @@ impl WidgetImpl for TransformPriv { } cr.restore()?; - }; + Ok(()) + })(); if let Err(error) = res { error_handling_ctx::print_error(error) diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 20b1f9ab4..60fdb6f91 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -83,7 +83,7 @@ pub const BUILTIN_WIDGET_NAMES: &[&str] = &[ WIDGET_NAME_STACK, ]; -//// widget definitions +/// widget definitions pub(super) fn widget_use_to_gtk_widget(bargs: &mut BuilderArgs) -> Result { let gtk_widget = match bargs.widget_use.name.as_str() { WIDGET_NAME_BOX => build_gtk_box(bargs)?.upcast(), @@ -128,8 +128,7 @@ static DEPRECATED_ATTRS: Lazy> = /// @widget widget /// @desc these properties apply to _all_ widgets, and can be used anywhere! pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Widget) -> Result<()> { - let deprecated: HashSet<_> = DEPRECATED_ATTRS.to_owned(); - let contained_deprecated: Vec<_> = bargs.unhandled_attrs.extract_if(|a, _| deprecated.contains(&a.0 as &str)).collect(); + let contained_deprecated: Vec<_> = DEPRECATED_ATTRS.iter().filter_map(|x| bargs.unhandled_attrs.remove_entry(*x)).collect(); if !contained_deprecated.is_empty() { let diag = error_handling_ctx::stringify_diagnostic(gen_diagnostic! { kind = Severity::Error, @@ -146,7 +145,7 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Wi let css_provider = gtk::CssProvider::new(); let css_provider2 = css_provider.clone(); - let visible_result: Result<_> = try { + let visible_result: Result<_> = (|| { let visible_expr = bargs.widget_use.attrs.attrs.get("visible").map(|x| x.value.as_simplexpr()).transpose()?; if let Some(visible_expr) = visible_expr { let visible = bargs.scope_graph.evaluate_simplexpr_in_scope(bargs.calling_scope, &visible_expr)?.as_bool()?; @@ -158,7 +157,8 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Wi } }); } - }; + Ok(()) + })(); if let Err(err) = visible_result { error_handling_ctx::print_error(err); } @@ -899,7 +899,7 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { prop(content: as_string) { gtk_widget.children().iter().for_each(|w| gtk_widget.remove(w)); if !content.is_empty() { - let content_widget_use: DiagResult<_> = try { + let content_widget_use: DiagResult<_> = (||{ let ast = { let mut yuck_files = error_handling_ctx::FILE_DATABASE.write().unwrap(); let (span, asts) = yuck_files.load_yuck_str("".to_string(), content)?; @@ -909,8 +909,8 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { yuck::parser::require_single_toplevel(span, asts)? }; - yuck::config::widget_use::WidgetUse::from_ast(ast)? - }; + yuck::config::widget_use::WidgetUse::from_ast(ast) + })(); let content_widget_use = content_widget_use?; // TODO a literal should create a new scope, that I'm not even sure should inherit from root @@ -1050,7 +1050,7 @@ fn build_circular_progress_bar(bargs: &mut BuilderArgs) -> Result { // @prop thickness - the thickness of the circle prop(thickness: as_f64) { w.set_property("thickness", thickness); }, // @prop clockwise - wether the progress bar spins clockwise or counter clockwise - prop(clockwise: as_bool) { w.set_property("clockwise", &clockwise); }, + prop(clockwise: as_bool) { w.set_property("clockwise", clockwise); }, }); Ok(w) } @@ -1062,11 +1062,11 @@ fn build_graph(bargs: &mut BuilderArgs) -> Result { let w = super::graph::Graph::new(); def_widget!(bargs, _g, w, { // @prop value - the value, between 0 - 100 - prop(value: as_f64) { w.set_property("value", &value); }, + prop(value: as_f64) { w.set_property("value", value); }, // @prop thickness - the thickness of the line - prop(thickness: as_f64) { w.set_property("thickness", &thickness); }, + prop(thickness: as_f64) { w.set_property("thickness", thickness); }, // @prop time-range - the range of time to show - prop(time_range: as_duration) { w.set_property("time-range", &(time_range.as_millis() as u64)); }, + prop(time_range: as_duration) { w.set_property("time-range", time_range.as_millis() as u64); }, // @prop min - the minimum value to show (defaults to 0 if value_max is provided) // @prop max - the maximum value to show prop(min: as_f64 = 0, max: as_f64 = 100) { @@ -1075,14 +1075,14 @@ fn build_graph(bargs: &mut BuilderArgs) -> Result { format!("Graph's min ({min}) should never be higher than max ({max})") )).into()); } - w.set_property("min", &min); - w.set_property("max", &max); + w.set_property("min", min); + w.set_property("max", max); }, // @prop dynamic - whether the y range should dynamically change based on value - prop(dynamic: as_bool) { w.set_property("dynamic", &dynamic); }, + prop(dynamic: as_bool) { w.set_property("dynamic", dynamic); }, // @prop line-style - changes the look of the edges in the graph. Values: "miter" (default), "round", // "bevel" - prop(line_style: as_string) { w.set_property("line-style", &line_style); }, + prop(line_style: as_string) { w.set_property("line-style", line_style); }, }); Ok(w) } diff --git a/crates/eww/src/window_arguments.rs b/crates/eww/src/window_arguments.rs index 8833bac91..44681e9e0 100644 --- a/crates/eww/src/window_arguments.rs +++ b/crates/eww/src/window_arguments.rs @@ -77,7 +77,7 @@ impl WindowArguments { } if local_variables.len() != window_def.expected_args.len() { - let unexpected_vars: Vec<_> = local_variables.keys().cloned().filter(|n| !expected_args.contains(&n.0)).collect(); + let unexpected_vars: Vec<_> = local_variables.keys().filter(|&n| !expected_args.contains(&n.0)).cloned().collect(); bail!( "variables {} unexpectedly defined when creating window with id '{}'", unexpected_vars.join(", "), diff --git a/crates/simplexpr/rust-toolchain b/crates/simplexpr/rust-toolchain deleted file mode 100644 index bf867e0ae..000000000 --- a/crates/simplexpr/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index a77b703e1..ef7383b58 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -9,7 +9,7 @@ use crate::{ use eww_shared_util::{Span, Spanned, VarName}; use std::{ collections::HashMap, - convert::{TryFrom, TryInto}, + convert::{Infallible, TryFrom, TryInto}, str::FromStr, sync::Arc, }; @@ -55,7 +55,7 @@ pub enum EvalError { JaqError(String), #[error(transparent)] - JaqParseError(JaqParseError), + JaqParseError(Box), #[error("Error parsing date: {0}")] ChronoError(String), @@ -98,13 +98,13 @@ impl SimplExpr { pub fn try_map_var_refs Result + Copy>(self, f: F) -> Result { use SimplExpr::*; Ok(match self { - BinOp(span, box a, op, box b) => BinOp(span, Box::new(a.try_map_var_refs(f)?), op, Box::new(b.try_map_var_refs(f)?)), + BinOp(span, a, op, b) => BinOp(span, Box::new(a.try_map_var_refs(f)?), op, Box::new(b.try_map_var_refs(f)?)), Concat(span, elems) => Concat(span, elems.into_iter().map(|x| x.try_map_var_refs(f)).collect::>()?), - UnaryOp(span, op, box a) => UnaryOp(span, op, Box::new(a.try_map_var_refs(f)?)), - IfElse(span, box a, box b, box c) => { + UnaryOp(span, op, a) => UnaryOp(span, op, Box::new(a.try_map_var_refs(f)?)), + IfElse(span, a, b, c) => { IfElse(span, Box::new(a.try_map_var_refs(f)?), Box::new(b.try_map_var_refs(f)?), Box::new(c.try_map_var_refs(f)?)) } - JsonAccess(span, safe, box a, box b) => { + JsonAccess(span, safe, a, b) => { JsonAccess(span, safe, Box::new(a.try_map_var_refs(f)?), Box::new(b.try_map_var_refs(f)?)) } FunctionCall(span, name, args) => { @@ -126,7 +126,7 @@ impl SimplExpr { } pub fn map_var_refs(self, f: impl Fn(Span, VarName) -> SimplExpr) -> Self { - self.try_map_var_refs(|span, var| Ok::<_, !>(f(span, var))).into_ok() + self.try_map_var_refs(|span, var| Ok::<_, Infallible>(f(span, var))).unwrap() } /// resolve partially. @@ -154,13 +154,13 @@ impl SimplExpr { Literal(..) => Vec::new(), VarRef(span, name) => vec![(*span, name)], Concat(_, elems) => elems.iter().flat_map(|x| x.var_refs_with_span().into_iter()).collect(), - BinOp(_, box a, _, box b) | JsonAccess(_, _, box a, box b) => { + BinOp(_, a, _, b) | JsonAccess(_, _, a, b) => { let mut refs = a.var_refs_with_span(); refs.extend(b.var_refs_with_span().iter()); refs } - UnaryOp(_, _, box x) => x.var_refs_with_span(), - IfElse(_, box a, box b, box c) => { + UnaryOp(_, _, x) => x.var_refs_with_span(), + IfElse(_, a, b, c) => { let mut refs = a.var_refs_with_span(); refs.extend(b.var_refs_with_span().iter()); refs.extend(c.var_refs_with_span().iter()); @@ -463,7 +463,7 @@ fn prepare_jaq_filter(code: String) -> Result, EvalError> let (filter, mut errors) = jaq_core::parse::parse(&code, jaq_core::parse::main()); let filter = match filter { Some(x) => x, - None => return Err(EvalError::JaqParseError(JaqParseError(errors.pop()))), + None => return Err(EvalError::JaqParseError(Box::new(JaqParseError(errors.pop())))), }; let mut defs = jaq_core::Definitions::core(); for def in jaq_std::std() { @@ -473,7 +473,7 @@ fn prepare_jaq_filter(code: String) -> Result, EvalError> let filter = defs.finish(filter, Vec::new(), &mut errors); if let Some(error) = errors.pop() { - return Err(EvalError::JaqParseError(JaqParseError(Some(error)))); + return Err(EvalError::JaqParseError(Box::new(JaqParseError(Some(error))))); } Ok(Arc::new(filter)) } diff --git a/crates/simplexpr/src/lib.rs b/crates/simplexpr/src/lib.rs index f2cdc8789..7f7c6b9e0 100644 --- a/crates/simplexpr/src/lib.rs +++ b/crates/simplexpr/src/lib.rs @@ -1,9 +1,3 @@ -#![feature(box_patterns)] -#![feature(pattern)] -#![feature(try_blocks)] -#![feature(unwrap_infallible)] -#![feature(never_type)] - pub mod ast; pub mod dynval; pub mod error; diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 94ea84892..4edcc952d 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -1,5 +1,3 @@ -use std::str::pattern::Pattern; - use eww_shared_util::{Span, Spanned}; use once_cell::sync::Lazy; use regex::{Regex, RegexSet}; @@ -133,10 +131,6 @@ impl<'s> Lexer<'s> { &self.source[self.pos..] } - pub fn continues_with(&self, pat: impl Pattern<'s>) -> bool { - self.remaining().starts_with(pat) - } - pub fn next_token(&mut self) -> Option, LexicalError>> { loop { if self.failed || self.pos >= self.source.len() { diff --git a/crates/yuck/rust-toolchain b/crates/yuck/rust-toolchain deleted file mode 100644 index bf867e0ae..000000000 --- a/crates/yuck/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index 1c77dda84..8686ab7ac 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -61,7 +61,7 @@ impl FromAstElementContent for PollScriptVar { const ELEMENT_NAME: &'static str = "defpoll"; fn from_tail>(_span: Span, mut iter: AstIterator) -> DiagResult { - let result: DiagResult<_> = try { + let result: DiagResult<_> = (move || { let (name_span, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; let initial_value = Some(attrs.primitive_optional("initial")?.unwrap_or_else(|| DynVal::from_string(String::new()))); @@ -73,15 +73,15 @@ impl FromAstElementContent for PollScriptVar { attrs.ast_optional::("run-while")?.unwrap_or_else(|| SimplExpr::Literal(DynVal::from(true))); iter.expect_done()?; - Self { + Ok(Self { name_span, name: VarName(name), run_while_expr, command: VarSource::Shell(script_span, script.to_string()), initial_value, interval, - } - }; + }) + })(); result.note(r#"Expected format: `(defpoll name :interval "10s" "echo 'a shell script'")`"#) } } @@ -98,14 +98,14 @@ impl FromAstElementContent for ListenScriptVar { const ELEMENT_NAME: &'static str = "deflisten"; fn from_tail>(_span: Span, mut iter: AstIterator) -> DiagResult { - let result: DiagResult<_> = try { + let result: DiagResult<_> = (move || { let (name_span, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; let initial_value = attrs.primitive_optional("initial")?.unwrap_or_else(|| DynVal::from_string(String::new())); let (command_span, script) = iter.expect_literal()?; iter.expect_done()?; - Self { name_span, name: VarName(name), command: script.to_string(), initial_value, command_span } - }; + Ok(Self { name_span, name: VarName(name), command: script.to_string(), initial_value, command_span }) + })(); result.note(r#"Expected format: `(deflisten name :initial "0" "tail -f /tmp/example")`"#) } } diff --git a/crates/yuck/src/config/var_definition.rs b/crates/yuck/src/config/var_definition.rs index be4ab8d2e..95f7c8633 100644 --- a/crates/yuck/src/config/var_definition.rs +++ b/crates/yuck/src/config/var_definition.rs @@ -17,12 +17,12 @@ impl FromAstElementContent for VarDefinition { const ELEMENT_NAME: &'static str = "defvar"; fn from_tail>(span: Span, mut iter: AstIterator) -> DiagResult { - let result: DiagResult<_> = try { + let result = (move || { let (_, name) = iter.expect_symbol()?; let (_, initial_value) = iter.expect_literal()?; iter.expect_done()?; - Self { name: VarName(name), initial_value, span } - }; + Ok(Self { name: VarName(name), initial_value, span }) + })(); result.note(r#"Expected format: `(defvar name "initial-value")`"#) } } diff --git a/crates/yuck/src/config/window_geometry.rs b/crates/yuck/src/config/window_geometry.rs index 07b221d62..9101af0d7 100644 --- a/crates/yuck/src/config/window_geometry.rs +++ b/crates/yuck/src/config/window_geometry.rs @@ -99,9 +99,9 @@ impl std::str::FromStr for AnchorPoint { Ok(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::CENTER }) } else { let (first, second) = s.split_once(' ').ok_or_else(|| AnchorPointParseError::WrongFormat(s.to_string()))?; - let x_y_result: Result<_, EnumParseError> = try { - AnchorPoint { x: AnchorAlignment::from_x_alignment(first)?, y: AnchorAlignment::from_y_alignment(second)? } - }; + let x_y_result: Result<_, EnumParseError> = (move || { + Ok(AnchorPoint { x: AnchorAlignment::from_x_alignment(first)?, y: AnchorAlignment::from_y_alignment(second)? }) + })(); x_y_result.or_else(|_| { Ok(AnchorPoint { x: AnchorAlignment::from_x_alignment(second)?, y: AnchorAlignment::from_y_alignment(first)? }) }) diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 1fdbc680b..9328dbb7f 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -199,22 +199,26 @@ impl ToDiagnostic for simplexpr::eval::EvalError { notes.push(format!("Hint: If you meant to use the literal value \"{}\", surround the value in quotes", name)); gen_diagnostic!(self).with_notes(notes) } - EvalError::Spanned(span, box EvalError::JaqParseError(simplexpr::eval::JaqParseError(Some(err)))) => { - let span = span.new_relative(err.span().start, err.span().end).shifted(1); - let mut diag = gen_diagnostic!(self, span); - - if let Some(label) = err.label() { - diag = diag.with_label(span_to_secondary_label(span).with_message(label)); - } - - let expected: Vec<_> = err.expected().filter_map(|x| x.clone()).sorted().collect(); - if !expected.is_empty() { - let label = format!("Expected one of {} here", expected.join(", ")); - diag = diag.with_label(span_to_primary_label(span).with_message(label)); + EvalError::Spanned(span, err) => { + if let EvalError::JaqParseError(err) = err.as_ref() { + if let Some(ref err) = err.as_ref().0 { + let span = span.new_relative(err.span().start, err.span().end).shifted(1); + let mut diag = gen_diagnostic!(self, span); + + if let Some(label) = err.label() { + diag = diag.with_label(span_to_secondary_label(span).with_message(label)); + } + + let expected: Vec<_> = err.expected().filter_map(|x| x.clone()).sorted().collect(); + if !expected.is_empty() { + let label = format!("Expected one of {} here", expected.join(", ")); + diag = diag.with_label(span_to_primary_label(span).with_message(label)); + } + return diag; + } } - diag + return err.as_ref().to_diagnostic().with_label(span_to_primary_label(*span)); } - EvalError::Spanned(span, error) => error.as_ref().to_diagnostic().with_label(span_to_primary_label(*span)), _ => gen_diagnostic!(self, self.span()), } } diff --git a/crates/yuck/src/lib.rs b/crates/yuck/src/lib.rs index 1fda23710..a754a7382 100644 --- a/crates/yuck/src/lib.rs +++ b/crates/yuck/src/lib.rs @@ -1,5 +1,4 @@ #![allow(clippy::comparison_chain)] -#![feature(try_blocks, box_patterns)] pub mod ast_error; pub mod config; diff --git a/docs/src/eww.md b/docs/src/eww.md index 4a25bd4f8..f9c938586 100644 --- a/docs/src/eww.md +++ b/docs/src/eww.md @@ -13,11 +13,10 @@ Configured in yuck and themed using CSS, it is easy to customize and provides al ### Prerequisites * rustc -* cargo (nightly toolchain) +* cargo Rather than with your system package manager, -I recommend installing it using [rustup](https://rustup.rs/), -as this makes it easy to use the nightly toolchain necessary to build eww. +I **strongly** recommend installing it using [rustup](https://rustup.rs/). Additionally, eww requires some dynamic libraries to be available on your system. The exact names of the packages that provide these may differ depending on your distribution. diff --git a/docs/src/troubleshooting.md b/docs/src/troubleshooting.md index d1db06bda..7053bd4bf 100644 --- a/docs/src/troubleshooting.md +++ b/docs/src/troubleshooting.md @@ -4,7 +4,7 @@ Here you will find help if something doesn't work. If the issue isn't listed her ## Eww does not compile -1. Make sure that you are compiling eww using the nightly Rust toolchain. +1. Make sure that you are compiling eww using a recent version of rust (run `rustup update` to be sure you have the latest version available) 2. Make sure you have all the necessary dependencies. If there are compile-errors, the compiler will tell you what you're missing. ## Eww does not work on Wayland diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f04a481a4..6343bc489 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-02-16" +channel = "1.76.0" components = [ "rust-src" ] profile = "default"