use crate::LOGGER; use crate::backend::window_manager_hypr::{HyprlandComms, WindowManagerEvent}; use crate::backend::{Backend, BackendChannels}; use crate::ui::action_centre::ActionCentre; use crossbeam_channel::TryRecvError; use glib::ControlFlow; use glib::prelude::Cast; use gtk::gdk::Display; use gtk::prelude::{BoxExt, ButtonExt, DisplayExt, GtkWindowExt, ListModelExt, WidgetExt}; use gtk::{Align, Application, ApplicationWindow, Button, Orientation, gdk}; use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell}; use jmb_logger_rs::*; use mpris::PlayerFinder; use std::collections::HashMap; use std::sync::Mutex; use std::time::Duration; mod action_centre; mod systray; mod title_panel; mod utilities_panel; mod widgets; mod workspaces_panel; pub fn create_bars(app: &Application, backend: Backend) { let display = Display::default().expect("Couldn't find GDK Display"); let app_clone = app.clone(); let bars: Mutex> = Mutex::new(HashMap::new()); let ac_channels = backend.subscribe(); let action_centre = ActionCentre::create_action_centre(app.clone(), ac_channels); for monitor in 0..display.monitors().n_items() { debug!(&LOGGER, "Opening bar {monitor}"); let channels = backend.subscribe(); bars.lock().expect("Couldn't get active bars").insert( monitor, create_bar( app_clone.clone(), &display, monitor, action_centre.clone(), channels, ), ); } display .monitors() .connect_items_changed(move |_list, pos, removed, added| { if removed == 1 { debug!(&LOGGER, "Detected Monitor disconnect {pos}"); if let Some(bar) = bars.lock().expect("Couldn't get active bars").remove(&pos) { bar.close(); } } else if added == 1 { debug!(&LOGGER, "Detected new monitor: {pos}"); let channels = backend.subscribe(); bars.lock().expect("Couldn't get active bars").insert( pos, create_bar( app_clone.clone(), &display, pos, action_centre.clone(), channels, ), ); } }); } fn create_bar( app: Application, display: &Display, monitor_idx: u32, action_centre: ActionCentre, mut channels: BackendChannels, ) -> ApplicationWindow { // Take the Receiver and replace it with none so that it can be only used in one place let hypr_rx = channels .take_hypr_rx() .expect("Tried taking unavailable Hyprland Channel Receiver"); let player_opt = if let Ok(player_finder) = PlayerFinder::new() { player_finder.find_active().ok() } else { None }; let window = ApplicationWindow::builder() .application(&app) .title("JmbBar") .default_width(500) .default_height(30) .css_classes(vec!["bar"]) .build(); let monitors = display.monitors(); window.init_layer_shell(); window.set_layer(Layer::Top); window.set_anchor(Edge::Top, true); window.set_anchor(Edge::Left, true); window.set_anchor(Edge::Right, true); window.set_margin(Edge::Top, 20); if let Some(monitor) = monitors.item(monitor_idx) && let Ok(monitor) = monitor.downcast::() { window.set_monitor(Some(&monitor)); } window.set_exclusive_zone(30); let mut buttons: HashMap = HashMap::new(); let (workspaces, submap) = workspaces_panel::build( &mut buttons, window.monitor().and_then(|monitor| monitor_index(&monitor)), ); let title_button: Option