From b1c66b9117cf8a10350cdb857a5267a1a72ad914 Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Mon, 10 Oct 2022 20:15:24 +0100 Subject: [PATCH] feat: wlroots-agnostic support for `launcher` module --- src/main.rs | 1 - src/modules/focused.rs | 4 +- src/modules/launcher/item.rs | 89 +++-------- src/modules/launcher/mod.rs | 238 +++++++++++++---------------- src/modules/launcher/open_state.rs | 30 +--- src/wayland/client.rs | 43 +++--- src/wayland/toplevel.rs | 14 +- 7 files changed, 172 insertions(+), 247 deletions(-) diff --git a/src/main.rs b/src/main.rs index 08a95ca..113f1e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,6 @@ mod wayland; use crate::bar::create_bar; use crate::config::{Config, MonitorConfig}; use crate::style::load_css; -use crate::sway::get_client; use color_eyre::eyre::Result; use color_eyre::Report; use dirs::config_dir; diff --git a/src/modules/focused.rs b/src/modules/focused.rs index 4d5c8d6..2f4a6bc 100644 --- a/src/modules/focused.rs +++ b/src/modules/focused.rs @@ -47,10 +47,10 @@ impl Module for FocusedModule { .expect("Failed to get read lock on toplevels") .clone(); - toplevels.into_iter().find(|top| top.active) + toplevels.into_iter().find(|(top, _)| top.active) }); - if let Some(top) = focused { + if let Some((top, _)) = focused { tx.try_send(ModuleUpdateEvent::Update(( top.title.clone(), top.app_id diff --git a/src/modules/launcher/item.rs b/src/modules/launcher/item.rs index 99964a2..7986982 100644 --- a/src/modules/launcher/item.rs +++ b/src/modules/launcher/item.rs @@ -4,22 +4,20 @@ use crate::icon::get_icon; use crate::modules::launcher::{ItemEvent, LauncherUpdate}; use crate::modules::ModuleUpdateEvent; use crate::popup::Popup; -use crate::sway::node::{get_node_id, is_node_xwayland}; use gtk::prelude::*; use gtk::{Button, IconTheme, Image}; use std::rc::Rc; use std::sync::RwLock; -use swayipc_async::Node; use tokio::sync::mpsc::Sender; +use crate::wayland::ToplevelInfo; #[derive(Debug, Clone)] pub struct Item { pub app_id: String, pub favorite: bool, pub open_state: OpenState, - pub windows: Collection, - pub name: Option, - pub is_xwayland: bool, + pub windows: Collection, + pub name: String, } impl Item { @@ -29,21 +27,18 @@ impl Item { favorite, open_state, windows: Collection::new(), - name: None, - is_xwayland: false, + name: String::new(), } } /// Merges the provided node into this launcher item - pub fn merge_node(&mut self, node: Node) -> Window { + pub fn merge_toplevel(&mut self, node: ToplevelInfo) -> Window { let id = node.id; if self.windows.is_empty() { - self.name = node.name.clone(); + self.name = node.title.clone(); } - self.is_xwayland = self.is_xwayland || is_node_xwayland(&node); - let window: Window = node.into(); self.windows.insert(id, window.clone()); @@ -52,16 +47,12 @@ impl Item { window } - pub fn unmerge_node(&mut self, node: &Node) { + pub fn unmerge_toplevel(&mut self, node: &ToplevelInfo) { self.windows.remove(&node.id); self.recalculate_open_state(); } - pub fn get_name(&self) -> &str { - self.name.as_ref().unwrap_or(&self.app_id) - } - - pub fn set_window_name(&mut self, window_id: i64, name: Option) { + pub fn set_window_name(&mut self, window_id: usize, name: String) { if let Some(window) = self.windows.get_mut(&window_id) { if let OpenState::Open { focused: true, .. } = window.open_state { self.name = name.clone(); @@ -71,23 +62,7 @@ impl Item { } } - pub fn set_unfocused(&mut self) { - let focused = self - .windows - .iter_mut() - .find(|window| window.open_state.is_focused()); - - if let Some(focused) = focused { - focused.open_state = OpenState::Open { - focused: false, - urgent: focused.open_state.is_urgent(), - }; - - self.recalculate_open_state(); - } - } - - pub fn set_window_focused(&mut self, window_id: i64, focused: bool) { + pub fn set_window_focused(&mut self, window_id: usize, focused: bool) { if let Some(window) = self.windows.get_mut(&window_id) { window.open_state = OpenState::merge_states(&[&window.open_state, &OpenState::focused(focused)]); @@ -96,15 +71,6 @@ impl Item { } } - pub fn set_window_urgent(&mut self, window_id: i64, urgent: bool) { - if let Some(window) = self.windows.get_mut(&window_id) { - window.open_state = - OpenState::merge_states(&[&window.open_state, &OpenState::urgent(urgent)]); - - self.recalculate_open_state(); - } - } - /// Sets this item's open state /// to the merged result of its windows' open states fn recalculate_open_state(&mut self) { @@ -119,16 +85,14 @@ impl Item { } } -impl From for Item { - fn from(node: Node) -> Self { - let app_id = get_node_id(&node).to_string(); - let open_state = OpenState::from_node(&node); - let name = node.name.clone(); - - let is_xwayland = is_node_xwayland(&node); +impl From for Item { + fn from(toplevel: ToplevelInfo) -> Self { + let open_state = OpenState::from_toplevel(&toplevel); + let name = toplevel.title.clone(); + let app_id = toplevel.app_id.clone(); let mut windows = Collection::new(); - windows.insert(node.id, node.into()); + windows.insert(toplevel.id, toplevel.into()); Self { app_id, @@ -136,25 +100,24 @@ impl From for Item { open_state, windows, name, - is_xwayland, } } } #[derive(Clone, Debug)] pub struct Window { - pub id: i64, - pub name: Option, + pub id: usize, + pub name: String, pub open_state: OpenState, } -impl From for Window { - fn from(node: Node) -> Self { - let open_state = OpenState::from_node(&node); +impl From for Window { + fn from(node: ToplevelInfo) -> Self { + let open_state = OpenState::from_toplevel(&node); Self { id: node.id, - name: node.name, + name: node.title, open_state, } } @@ -183,7 +146,7 @@ impl ItemButton { let mut button = Button::builder(); if show_names { - button = button.label(item.get_name()); + button = button.label(&item.name); } if show_icons { @@ -208,9 +171,6 @@ impl ItemButton { if item.open_state.is_focused() { style_context.add_class("focused"); } - if item.open_state.is_urgent() { - style_context.add_class("urgent"); - } { let app_id = item.app_id.clone(); @@ -274,7 +234,6 @@ impl ItemButton { if !open { self.set_focused(false); - self.set_urgent(false); } } @@ -282,10 +241,6 @@ impl ItemButton { self.update_class("focused", focused); } - pub fn set_urgent(&self, urgent: bool) { - self.update_class("urgent", urgent); - } - /// Adds or removes a class to the button based on `toggle`. fn update_class(&self, class: &str, toggle: bool) { let style_context = self.button.style_context(); diff --git a/src/modules/launcher/mod.rs b/src/modules/launcher/mod.rs index a14b563..61d8486 100644 --- a/src/modules/launcher/mod.rs +++ b/src/modules/launcher/mod.rs @@ -1,14 +1,13 @@ mod item; mod open_state; +use self::item::{Item, ItemButton, Window}; +use self::open_state::OpenState; use crate::collection::Collection; use crate::icon::find_desktop_file; -use crate::modules::launcher::item::{Item, ItemButton, Window}; -use crate::modules::launcher::open_state::OpenState; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; -use crate::sway::get_sub_client; -use crate::sway::node::{get_node_id, get_open_windows}; -use crate::{await_sync, get_client}; +use crate::wayland; +use crate::wayland::ToplevelChange; use color_eyre::{Help, Report}; use glib::Continue; use gtk::prelude::*; @@ -16,7 +15,6 @@ use gtk::{Button, IconTheme, Orientation}; use serde::Deserialize; use std::process::{Command, Stdio}; use std::sync::{Arc, Mutex}; -use swayipc_async::WindowChange; use tokio::spawn; use tokio::sync::mpsc; use tokio::sync::mpsc::{Receiver, Sender}; @@ -47,13 +45,11 @@ pub enum LauncherUpdate { /// Removes item with `app_id` RemoveItem(String), /// Removes window from item with `app_id`. - RemoveWindow(String, i64), + RemoveWindow(String, usize), /// Sets title for `app_id` - Title(String, i64, Option), - /// Focuses first `app_id`, unfocuses second `app_id` (if present) - Focus(String, Option), - /// Marks the item with `app_id` as urgent or not urgent - Urgent(String, bool), + Title(String, usize, String), + /// Marks the item with `app_id` as focused or not focused + Focus(String, bool), /// Declares the item with `app_id` has been hovered over Hover(String), } @@ -61,7 +57,7 @@ pub enum LauncherUpdate { #[derive(Debug)] pub enum ItemEvent { FocusItem(String), - FocusWindow(i64), + FocusWindow(usize), OpenItem(String), } @@ -100,69 +96,73 @@ impl Module for LauncherModule { let items = Arc::new(Mutex::new(items)); - let open_windows = await_sync(async { - let sway = get_client().await; - let mut sway = sway.lock().await; - get_open_windows(&mut sway).await - })?; - { - let mut items = items.lock().expect("Failed to get lock on items"); - for window in open_windows { - let id = get_node_id(&window).to_string(); + let items = Arc::clone(&items); + let tx = tx.clone(); + spawn(async move { + let wl = wayland::get_client().await; + let open_windows = wl + .toplevels + .read() + .expect("Failed to get read lock on toplevels"); - let item = items.get_mut(&id); - match item { - Some(item) => { - item.merge_node(window); - } - None => { - items.insert(id, window.into()); + let mut items = items.lock().expect("Failed to get lock on items"); + + for (window, _) in open_windows.clone().into_iter() { + let item = items.get_mut(&window.app_id); + match item { + Some(item) => { + item.merge_toplevel(window); + } + None => { + items.insert(window.app_id.clone(), window.into()); + } } } - } - let items = items.iter(); - for item in items { - tx.try_send(ModuleUpdateEvent::Update(LauncherUpdate::AddItem( - item.clone(), - )))?; - } + let items = items.iter(); + for item in items { + tx.try_send(ModuleUpdateEvent::Update(LauncherUpdate::AddItem( + item.clone(), + )))?; + } + + Ok::<(), Report>(()) + }); } let items2 = Arc::clone(&items); spawn(async move { let items = items2; - let mut srx = { - let sway = get_sub_client(); - sway.subscribe_window() + let mut wlrx = { + let wl = wayland::get_client().await; + wl.subscribe_toplevels() }; - while let Ok(event) = srx.recv().await { + let send_update = |update: LauncherUpdate| tx.send(ModuleUpdateEvent::Update(update)); + + while let Ok(event) = wlrx.recv().await { trace!("event: {:?}", event); - let window = event.container; - let id = get_node_id(&window).to_string(); - - let send_update = - |update: LauncherUpdate| tx.send(ModuleUpdateEvent::Update(update)); + let window = event.toplevel; + let app_id = window.app_id.clone(); let items = || items.lock().expect("Failed to get lock on items"); match event.change { - WindowChange::New => { + ToplevelChange::New => { let new_item = { let mut items = items(); - match items.get_mut(&id) { + match items.get_mut(&app_id) { None => { let item: Item = window.into(); - items.insert(id.clone(), item.clone()); + items.insert(app_id.clone(), item.clone()); ItemOrWindow::Item(item) } Some(item) => { - let window = item.merge_node(window); + let window = item.merge_toplevel(window); ItemOrWindow::Window(window) } } @@ -173,19 +173,19 @@ impl Module for LauncherModule { send_update(LauncherUpdate::AddItem(item)).await } ItemOrWindow::Window(window) => { - send_update(LauncherUpdate::AddWindow(id, window)).await + send_update(LauncherUpdate::AddWindow(app_id, window)).await } }?; } - WindowChange::Close => { + ToplevelChange::Close => { let remove_item = { let mut items = items(); - match items.get_mut(&id) { + match items.get_mut(&app_id) { Some(item) => { - item.unmerge_node(&window); + item.unmerge_toplevel(&window); if item.windows.is_empty() { - items.remove(&id); + items.remove(&app_id); Some(ItemOrWindowId::Item) } else { Some(ItemOrWindowId::Window) @@ -197,58 +197,47 @@ impl Module for LauncherModule { match remove_item { Some(ItemOrWindowId::Item) => { - send_update(LauncherUpdate::RemoveItem(id)).await?; + send_update(LauncherUpdate::RemoveItem(app_id)).await?; } Some(ItemOrWindowId::Window) => { - send_update(LauncherUpdate::RemoveWindow(id, window.id)).await?; + send_update(LauncherUpdate::RemoveWindow(app_id, window.id)) + .await?; } None => {} }; } - WindowChange::Focus => { - let prev_id = { - let mut items = items(); + ToplevelChange::Focus(focused) => { + let update_title = if focused { + if let Some(item) = items().get_mut(&app_id) { + item.set_window_focused(window.id, true); - let prev_focused = - items.iter_mut().find(|item| item.open_state.is_focused()); - if let Some(prev_focused) = prev_focused { - prev_focused.set_unfocused(); - Some(prev_focused.app_id.to_string()) + // might be switching focus between windows of same app + if item.windows.len() > 1 { + item.set_window_name(window.id, window.title.clone()); + true + } else { + false + } } else { - None + false } + } else { + false }; - let mut update_title = false; - if let Some(item) = items().get_mut(&id) { - item.set_window_focused(window.id, true); - - // might be switching focus between windows of same app - if item.windows.len() > 1 { - item.set_window_name(window.id, window.name.clone()); - update_title = true; - } - } - - send_update(LauncherUpdate::Focus(id.clone(), prev_id)).await?; + send_update(LauncherUpdate::Focus(app_id.clone(), focused)).await?; if update_title { - send_update(LauncherUpdate::Title(id, window.id, window.name)).await?; + send_update(LauncherUpdate::Title(app_id, window.id, window.title)) + .await?; } } - WindowChange::Title => { - if let Some(item) = items().get_mut(&id) { - item.set_window_name(window.id, window.name.clone()); + ToplevelChange::Title(title) => { + if let Some(item) = items().get_mut(&app_id) { + item.set_window_name(window.id, title.clone()); } - send_update(LauncherUpdate::Title(id, window.id, window.name)).await?; - } - WindowChange::Urgent => { - if let Some(item) = items().get_mut(&id) { - item.set_window_urgent(window.id, window.urgent); - } - - send_update(LauncherUpdate::Urgent(id, window.urgent)).await?; + send_update(LauncherUpdate::Title(app_id, window.id, title)).await?; } _ => {} } @@ -259,8 +248,6 @@ impl Module for LauncherModule { // listen to ui events spawn(async move { - let sway = get_client().await; - while let Some(event) = rx.recv().await { trace!("{:?}", event); @@ -287,25 +274,26 @@ impl Module for LauncherModule { }, ); } else { - let selector = { - let items = items.lock().expect("Failed to get lock on items"); + let wl = wayland::get_client().await; + let items = items.lock().expect("Failed to get lock on items"); - match event { - ItemEvent::FocusItem(app_id) => items.get(&app_id).map(|item| { - if item.is_xwayland { - format!("[class={}]", app_id) - } else { - format!("[app_id={}]", app_id) - } - }), - ItemEvent::FocusWindow(con_id) => Some(format!("[con_id={}]", con_id)), - ItemEvent::OpenItem(_) => unreachable!(), - } + let id = match event { + ItemEvent::FocusItem(app_id) => items + .get(&app_id) + .and_then(|item| item.windows.first().map(|win| win.id)), + ItemEvent::FocusWindow(id) => Some(id), + ItemEvent::OpenItem(_) => unreachable!(), }; - if let Some(selector) = selector { - let mut sway = sway.lock().await; - sway.run_command(format!("{} focus", selector)).await?; + if let Some(id) = id { + let toplevels = wl + .toplevels + .read() + .expect("Failed to get read lock on toplevels"); + let seat = wl.seats.first().unwrap(); + if let Some((_top, handle)) = toplevels.get(&id) { + handle.activate(seat); + }; } } } @@ -391,20 +379,11 @@ impl Module for LauncherModule { menu_state.num_windows -= 1; } } - LauncherUpdate::Focus(new, prev) => { - debug!( - "Changing focus to item with id {} (removing from {:?})", - new, prev - ); + LauncherUpdate::Focus(app_id, focus) => { + debug!("Changing focus to {} on item with id {}", focus, app_id); - if let Some(prev) = prev { - if let Some(button) = buttons.get(&prev) { - button.set_focused(false); - } - } - - if let Some(button) = buttons.get(&new) { - button.set_focused(true); + if let Some(button) = buttons.get(&app_id) { + button.set_focused(focus); } } LauncherUpdate::Title(app_id, _, name) => { @@ -412,17 +391,10 @@ impl Module for LauncherModule { if show_names { if let Some(button) = buttons.get(&app_id) { - button.button.set_label(&name.unwrap_or_default()); + button.button.set_label(&name); } } } - LauncherUpdate::Urgent(app_id, urgent) => { - debug!("Updating urgency for item with id {}: {}", app_id, urgent); - - if let Some(button) = buttons.get(&app_id) { - button.set_urgent(urgent); - } - } LauncherUpdate::Hover(_) => {} }; @@ -444,7 +416,7 @@ impl Module for LauncherModule { ) -> Option { let container = gtk::Box::new(Orientation::Vertical, 0); - let mut buttons = Collection::>::new(); + let mut buttons = Collection::>::new(); { let container = container.clone(); @@ -458,7 +430,7 @@ impl Module for LauncherModule { .into_iter() .map(|win| { let button = Button::builder() - .label(win.name.as_ref().unwrap_or(&String::new())) + .label(&win.name) .height_request(40) .width_request(100) .build(); @@ -484,7 +456,7 @@ impl Module for LauncherModule { LauncherUpdate::AddWindow(app_id, win) => { if let Some(buttons) = buttons.get_mut(&app_id) { let button = Button::builder() - .label(win.name.as_ref().unwrap_or(&String::new())) + .label(&win.name) .height_request(40) .width_request(100) .build(); @@ -512,9 +484,7 @@ impl Module for LauncherModule { LauncherUpdate::Title(app_id, win_id, title) => { if let Some(buttons) = buttons.get_mut(&app_id) { if let Some(button) = buttons.get(&win_id) { - if let Some(title) = title { - button.set_label(&title); - } + button.set_label(&title); } } } diff --git a/src/modules/launcher/open_state.rs b/src/modules/launcher/open_state.rs index cabd7c1..e355059 100644 --- a/src/modules/launcher/open_state.rs +++ b/src/modules/launcher/open_state.rs @@ -1,35 +1,23 @@ -use swayipc_async::Node; +use crate::wayland::ToplevelInfo; /// Open state for a launcher item, or item window. #[derive(Debug, Clone, Eq, PartialEq, Copy)] pub enum OpenState { Closed, - Open { focused: bool, urgent: bool }, + Open { focused: bool }, } impl OpenState { /// Creates from `SwayNode` - pub const fn from_node(node: &Node) -> Self { + pub const fn from_toplevel(toplevel: &ToplevelInfo) -> Self { Self::Open { - focused: node.focused, - urgent: node.urgent, + focused: toplevel.active, } } /// Creates open with focused pub const fn focused(focused: bool) -> Self { - Self::Open { - focused, - urgent: false, - } - } - - /// Creates open with urgent - pub const fn urgent(urgent: bool) -> Self { - Self::Open { - focused: false, - urgent, - } + Self::Open { focused } } /// Checks if open @@ -39,12 +27,7 @@ impl OpenState { /// Checks if open with focus pub const fn is_focused(self) -> bool { - matches!(self, Self::Open { focused: true, .. }) - } - - /// check if open with urgent - pub const fn is_urgent(self) -> bool { - matches!(self, Self::Open { urgent: true, .. }) + matches!(self, Self::Open { focused: true }) } /// Merges states together to produce a single state. @@ -56,7 +39,6 @@ impl OpenState { if merged.is_open() || current.is_open() { Self::Open { focused: merged.is_focused() || current.is_focused(), - urgent: merged.is_urgent() || current.is_urgent(), } } else { Self::Closed diff --git a/src/wayland/client.rs b/src/wayland/client.rs index fb6207b..36d05f4 100644 --- a/src/wayland/client.rs +++ b/src/wayland/client.rs @@ -1,21 +1,27 @@ -use std::sync::{Arc, RwLock}; use super::{Env, ToplevelHandler}; +use crate::collection::Collection; +use crate::wayland::toplevel::{ToplevelEvent, ToplevelInfo}; use crate::wayland::toplevel_manager::listen_for_toplevels; +use crate::wayland::ToplevelChange; use smithay_client_toolkit::environment::Environment; use smithay_client_toolkit::output::{with_output_info, OutputInfo}; use smithay_client_toolkit::reexports::calloop; use smithay_client_toolkit::{new_default_environment, WaylandSource}; +use std::sync::{Arc, RwLock}; +use std::time::Duration; use tokio::sync::{broadcast, oneshot}; use tokio::task::spawn_blocking; -use tracing::{trace}; -use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1; -use crate::collection::Collection; -use crate::wayland::toplevel::{ToplevelEvent, ToplevelInfo}; -use crate::wayland::ToplevelChange; +use tracing::trace; +use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::{ + zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1, + zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1, +}; +use wayland_client::protocol::wl_seat::WlSeat; pub struct WaylandClient { pub outputs: Vec, - pub toplevels: Arc>>, + pub seats: Vec, + pub toplevels: Arc>>, toplevel_tx: broadcast::Sender, _toplevel_rx: broadcast::Receiver, } @@ -23,6 +29,7 @@ pub struct WaylandClient { impl WaylandClient { pub(super) async fn new() -> Self { let (output_tx, output_rx) = oneshot::channel(); + let (seat_tx, seat_rx) = oneshot::channel(); let (toplevel_tx, toplevel_rx) = broadcast::channel(32); let toplevel_tx2 = toplevel_tx.clone(); @@ -41,21 +48,24 @@ impl WaylandClient { .send(outputs) .expect("Failed to send outputs out of task"); + let seats = env.get_all_seats(); + seat_tx.send(seats.into_iter().map(|seat| seat.detach()).collect::>()).expect("Failed to send seats out of task"); + let _toplevel_manager = env.require_global::(); - let _listener = listen_for_toplevels(env, move |_handle, event, _ddata| { + let _listener = listen_for_toplevels(env, move |handle, event, _ddata| { trace!("Received toplevel event: {:?}", event); if event.change != ToplevelChange::Close { toplevels2 .write() .expect("Failed to get write lock on toplevels") - .insert(event.toplevel.app_id.clone(), event.toplevel.clone()); + .insert(event.toplevel.id, (event.toplevel.clone(), handle)); } else { toplevels2 .write() .expect("Failed to get write lock on toplevels") - .remove(&event.toplevel.app_id); + .remove(&event.toplevel.id); } toplevel_tx2 @@ -69,7 +79,9 @@ impl WaylandClient { .unwrap(); loop { - event_loop.dispatch(None, &mut ()).unwrap(); + // TODO: Avoid need for duration here - can we force some event when sending requests? + event_loop.dispatch(Duration::from_millis(50), &mut ()).unwrap(); + event_loop. } }); @@ -77,16 +89,11 @@ impl WaylandClient { .await .expect("Failed to receive outputs from task"); - // spawn(async move { - // println!("start"); - // while let Ok(ev) = toplevel_rx.recv().await { - // println!("recv {:?}", ev) - // } - // println!("stop"); - // }); + let seats = seat_rx.await.expect("Failed to receive seats from task"); Self { outputs, + seats, toplevels, toplevel_tx, _toplevel_rx: toplevel_rx, diff --git a/src/wayland/toplevel.rs b/src/wayland/toplevel.rs index e3d4485..0117ebb 100644 --- a/src/wayland/toplevel.rs +++ b/src/wayland/toplevel.rs @@ -1,13 +1,18 @@ use std::collections::HashSet; use std::sync::{Arc, RwLock}; +use std::sync::atomic::{AtomicUsize, Ordering}; use wayland_client::{DispatchData, Main}; use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::zwlr_foreign_toplevel_handle_v1::{Event, ZwlrForeignToplevelHandleV1}; const STATE_ACTIVE: u32 = 2; const STATE_FULLSCREEN: u32 = 3; +static COUNTER: AtomicUsize = AtomicUsize::new(1); +fn get_id() -> usize { COUNTER.fetch_add(1, Ordering::Relaxed) } + #[derive(Debug, Clone, Default)] pub struct ToplevelInfo { + pub id: usize, pub app_id: String, pub title: String, pub active: bool, @@ -16,6 +21,13 @@ pub struct ToplevelInfo { ready: bool, } +impl ToplevelInfo { + fn new() -> Self { + let id = get_id(); + Self { id, ..Default::default() } + } +} + pub struct Toplevel; #[derive(Debug, Clone)] @@ -112,7 +124,7 @@ impl Toplevel { where F: FnMut(ToplevelEvent, DispatchData) + 'static, { - let inner = Arc::new(RwLock::new(ToplevelInfo::default())); + let inner = Arc::new(RwLock::new(ToplevelInfo::new())); handle.quick_assign(move |_handle, event, ddata| { let mut inner = inner