1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-03 19:51:03 +02:00

feat: logging support and proper error handling

This commit is contained in:
Jake Stanger 2022-08-21 23:36:07 +01:00
parent 917838c98c
commit ab8f7ecfc8
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61
28 changed files with 1056 additions and 388 deletions

View file

@ -3,6 +3,8 @@ use crate::icon::{find_desktop_file, get_icon};
use crate::modules::launcher::popup::Popup;
use crate::modules::launcher::FocusEvent;
use crate::sway::SwayNode;
use crate::Report;
use color_eyre::Help;
use gtk::prelude::*;
use gtk::{Button, IconTheme, Image};
use std::process::{Command, Stdio};
@ -10,6 +12,7 @@ use std::rc::Rc;
use std::sync::{Arc, Mutex, RwLock};
use tokio::spawn;
use tokio::sync::mpsc;
use tracing::error;
#[derive(Debug, Clone)]
pub struct LauncherItem {
@ -26,12 +29,42 @@ pub struct LauncherWindow {
pub name: Option<String>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum OpenState {
Closed,
Open,
Focused,
Urgent,
}
impl OpenState {
pub const fn from_node(node: &SwayNode) -> Self {
if node.focused {
Self::Urgent
} else if node.urgent {
Self::Focused
} else {
Self::Open
}
}
pub fn highest_of(a: &Self, b: &Self) -> Self {
if a == &Self::Urgent || b == &Self::Urgent {
Self::Urgent
} else if a == &Self::Focused || b == &Self::Focused {
Self::Focused
} else if a == &Self::Open || b == &Self::Open {
Self::Open
} else {
Self::Closed
}
}
}
#[derive(Debug, Clone)]
pub struct State {
pub is_xwayland: bool,
pub open: bool,
pub focused: bool,
pub urgent: bool,
pub open_state: OpenState,
}
#[derive(Debug, Clone)]
@ -49,9 +82,7 @@ impl LauncherItem {
button.style_context().add_class("item");
let state = State {
open: false,
focused: false,
urgent: false,
open_state: OpenState::Closed,
is_xwayland: false,
};
@ -80,9 +111,7 @@ impl LauncherItem {
));
let state = State {
open: true,
focused: node.focused,
urgent: node.urgent,
open_state: OpenState::from_node(node),
is_xwayland: node.is_xwayland(),
};
@ -101,10 +130,14 @@ impl LauncherItem {
fn configure_button(&self, config: &ButtonConfig) {
let button = &self.button;
let windows = self.windows.lock().unwrap();
let windows = self.windows.lock().expect("Failed to get lock on windows");
let name = if windows.len() == 1 {
windows.first().unwrap().name.as_ref()
windows
.first()
.expect("Failed to get first window")
.name
.as_ref()
} else {
Some(&self.app_id)
};
@ -129,21 +162,31 @@ impl LauncherItem {
let (focus_tx, mut focus_rx) = mpsc::channel(32);
button.connect_clicked(move |_| {
let state = state.read().unwrap();
if state.open {
focus_tx.try_send(()).unwrap();
let state = state.read().expect("Failed to get read lock on state");
if state.open_state == OpenState::Open {
focus_tx.try_send(()).expect("Failed to send focus event");
} else {
// attempt to find desktop file and launch
match find_desktop_file(&app_id) {
Some(file) => {
Command::new("gtk-launch")
.arg(file.file_name().unwrap())
if let Err(err) = Command::new("gtk-launch")
.arg(
file.file_name()
.expect("File segment missing from path to desktop file"),
)
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.unwrap();
{
error!(
"{:?}",
Report::new(err)
.wrap_err("Failed to run gtk-launch command.")
.suggestion("Perhaps the desktop file is invalid?")
);
}
}
None => (),
None => error!("Could not find desktop file for {}", app_id),
}
}
});
@ -153,15 +196,15 @@ impl LauncherItem {
spawn(async move {
while focus_rx.recv().await == Some(()) {
let state = state.read().unwrap();
let state = state.read().expect("Failed to get read lock on state");
if state.is_xwayland {
tx_click
.try_send(FocusEvent::Class(app_id.clone()))
.unwrap();
.expect("Failed to send focus event");
} else {
tx_click
.try_send(FocusEvent::AppId(app_id.clone()))
.unwrap();
.expect("Failed to send focus event");
}
}
});
@ -172,7 +215,7 @@ impl LauncherItem {
let tx_hover = config.tx.clone();
button.connect_enter_notify_event(move |button, _| {
let windows = windows.lock().unwrap();
let windows = windows.lock().expect("Failed to get lock on windows");
if windows.len() > 1 {
popup.set_windows(windows.as_slice(), &tx_hover);
popup.show(button);
@ -196,7 +239,7 @@ impl LauncherItem {
let style = button.style_context();
style.add_class("launcher-item");
self.update_button_classes(&self.state.read().unwrap());
self.update_button_classes(&self.state.read().expect("Failed to get read lock on state"));
button.show_all();
}
@ -223,19 +266,19 @@ impl LauncherItem {
style.remove_class("favorite");
}
if state.open {
if state.open_state == OpenState::Open {
style.add_class("open");
} else {
style.remove_class("open");
}
if state.focused {
if state.open_state == OpenState::Focused {
style.add_class("focused");
} else {
style.remove_class("focused");
}
if state.urgent {
if state.open_state == OpenState::Urgent {
style.add_class("urgent");
} else {
style.remove_class("urgent");

View file

@ -2,19 +2,20 @@ mod item;
mod popup;
use crate::collection::Collection;
use crate::modules::launcher::item::{ButtonConfig, LauncherItem, LauncherWindow};
use crate::modules::launcher::item::{ButtonConfig, LauncherItem, LauncherWindow, OpenState};
use crate::modules::launcher::popup::Popup;
use crate::modules::{Module, ModuleInfo};
use crate::sway::node::get_open_windows;
use crate::sway::{SwayNode, WindowEvent};
use crate::sway::{SwayClient, SwayNode, WindowEvent};
use color_eyre::{Report, Result};
use gtk::prelude::*;
use gtk::{IconTheme, Orientation};
use ksway::{Client, IpcEvent};
use ksway::IpcEvent;
use serde::Deserialize;
use std::rc::Rc;
use tokio::spawn;
use tokio::sync::mpsc;
use tokio::task::spawn_blocking;
use tracing::error;
#[derive(Debug, Deserialize, Clone)]
pub struct LauncherModule {
@ -72,15 +73,17 @@ impl Launcher {
let id = window.get_id().to_string();
if let Some(item) = self.items.get_mut(&id) {
let mut state = item.state.write().unwrap();
state.open = true;
state.focused = window.focused || state.focused;
state.urgent = window.urgent || state.urgent;
let mut state = item
.state
.write()
.expect("Failed to get write lock on state");
let new_open_state = OpenState::from_node(&window);
state.open_state = OpenState::highest_of(&state.open_state, &new_open_state);
state.is_xwayland = window.is_xwayland();
item.update_button_classes(&state);
let mut windows = item.windows.lock().unwrap();
let mut windows = item.windows.lock().expect("Failed to get lock on windows");
windows.insert(
window.id,
@ -107,13 +110,13 @@ impl Launcher {
let remove = if let Some(item) = item {
let windows = Rc::clone(&item.windows);
let mut windows = windows.lock().unwrap();
let mut windows = windows.lock().expect("Failed to get lock on windows");
windows.remove(&window.id);
if windows.is_empty() {
let mut state = item.state.write().unwrap();
state.open = false;
let mut state = item.state.write().expect("Failed to get lock on windows");
state.open_state = OpenState::Closed;
item.update_button_classes(&state);
if item.favorite {
@ -137,20 +140,30 @@ impl Launcher {
fn set_window_focused(&mut self, window: &SwayNode) {
let id = window.get_id().to_string();
let currently_focused = self
.items
.iter_mut()
.find(|item| item.state.read().unwrap().focused);
let currently_focused = self.items.iter_mut().find(|item| {
item.state
.read()
.expect("Failed to get read lock on state")
.open_state
== OpenState::Focused
});
if let Some(currently_focused) = currently_focused {
let mut state = currently_focused.state.write().unwrap();
state.focused = false;
let mut state = currently_focused
.state
.write()
.expect("Failed to get write lock on state");
state.open_state = OpenState::Open;
currently_focused.update_button_classes(&state);
}
let item = self.items.get_mut(&id);
if let Some(item) = item {
let mut state = item.state.write().unwrap();
state.focused = true;
let mut state = item
.state
.write()
.expect("Failed to get write lock on state");
state.open_state = OpenState::Focused;
item.update_button_classes(&state);
}
}
@ -160,11 +173,15 @@ impl Launcher {
let item = self.items.get_mut(&id);
if let (Some(item), Some(name)) = (item, window.name) {
let mut windows = item.windows.lock().unwrap();
let mut windows = item.windows.lock().expect("Failed to get lock on windows");
if windows.len() == 1 {
item.set_title(&name, &self.button_config);
} else if let Some(window) = windows.get_mut(&window.id) {
window.name = Some(name);
} else {
windows.get_mut(&window.id).unwrap().name = Some(name);
// This should never happen
// But makes more sense to wipe title than keep old one in case of error
item.set_title("", &self.button_config);
}
}
}
@ -174,22 +191,26 @@ impl Launcher {
let item = self.items.get_mut(&id);
if let Some(item) = item {
let mut state = item.state.write().unwrap();
state.urgent = window.urgent;
let mut state = item
.state
.write()
.expect("Failed to get write lock on state");
state.open_state =
OpenState::highest_of(&state.open_state, &OpenState::from_node(window));
item.update_button_classes(&state);
}
}
}
impl Module<gtk::Box> for LauncherModule {
fn into_widget(self, info: &ModuleInfo) -> gtk::Box {
fn into_widget(self, info: &ModuleInfo) -> Result<gtk::Box> {
let icon_theme = IconTheme::new();
if let Some(theme) = self.icon_theme {
icon_theme.set_custom_theme(Some(&theme));
}
let mut sway = Client::connect().unwrap();
let mut sway = SwayClient::connect()?;
let popup = Popup::new(
"popup-launcher",
@ -216,22 +237,29 @@ impl Module<gtk::Box> for LauncherModule {
button_config,
);
let open_windows = get_open_windows(&mut sway);
let open_windows = sway.get_open_windows()?;
for window in open_windows {
launcher.add_window(window);
}
let srx = sway.subscribe(vec![IpcEvent::Window]).unwrap();
let srx = sway.subscribe(vec![IpcEvent::Window])?;
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
spawn_blocking(move || loop {
while let Ok((_, payload)) = srx.try_recv() {
let payload: WindowEvent = serde_json::from_slice(&payload).unwrap();
tx.send(payload).unwrap();
match serde_json::from_slice::<WindowEvent>(&payload) {
Ok(payload) => {
tx.send(payload)
.expect("Failed to send window event payload");
}
Err(err) => error!("{:?}", err),
}
}
if let Err(err) = sway.poll() {
error!("{:?}", err);
}
sway.poll().unwrap();
});
{
@ -250,7 +278,7 @@ impl Module<gtk::Box> for LauncherModule {
}
spawn(async move {
let mut sway = Client::connect().unwrap();
let mut sway = SwayClient::connect()?;
while let Some(event) = ui_rx.recv().await {
let selector = match event {
FocusEvent::AppId(app_id) => format!("[app_id={}]", app_id),
@ -258,10 +286,12 @@ impl Module<gtk::Box> for LauncherModule {
FocusEvent::ConId(id) => format!("[con_id={}]", id),
};
sway.run(format!("{} focus", selector)).unwrap();
sway.run(format!("{} focus", selector))?;
}
Ok::<(), Report>(())
});
container
Ok(container)
}
}

View file

@ -25,7 +25,8 @@ impl Popup {
let window = self.window.clone();
let tx = tx.clone();
button.connect_clicked(move |_| {
tx.try_send(FocusEvent::ConId(con_id)).unwrap();
tx.try_send(FocusEvent::ConId(con_id))
.expect("Failed to send focus event");
window.hide();
});