wzrd

An ICCCM & EWMH compliant X11 reparenting, dynamic window manager, written in Rust
git clone git://git.deurzen.net/wzrd
Log | Files | Refs | LICENSE

workspace.rs (21357B)


      1 use crate::change::Change;
      2 use crate::change::Direction;
      3 use crate::client::Client;
      4 use crate::cycle::Cycle;
      5 use crate::cycle::InsertPos;
      6 use crate::cycle::Selector;
      7 use crate::decoration::Decoration;
      8 use crate::error::StateChangeError;
      9 use crate::identify::Ident;
     10 use crate::identify::Identify;
     11 use crate::identify::Index;
     12 use crate::layout::Layout;
     13 use crate::placement::Placement;
     14 use crate::placement::PlacementMethod;
     15 use crate::placement::PlacementRegion;
     16 use crate::placement::PlacementTarget;
     17 use crate::util::BuildIdHasher;
     18 use crate::zone::ZoneId;
     19 use crate::zone::ZoneManager;
     20 
     21 use winsys::geometry::Edge;
     22 use winsys::geometry::Pos;
     23 use winsys::geometry::Region;
     24 use winsys::input::Grip;
     25 use winsys::window::Window;
     26 
     27 use std::cell::Cell;
     28 use std::cell::RefCell;
     29 use std::collections::HashMap;
     30 use std::collections::HashSet;
     31 use std::collections::VecDeque;
     32 
     33 #[derive(Clone, Copy)]
     34 pub enum ClientSelector {
     35     AtActive,
     36     AtMaster,
     37     AtIndex(Index),
     38     AtIdent(Window),
     39     First,
     40     Last,
     41 }
     42 
     43 #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
     44 pub enum BufferKind {
     45     Move,
     46     Resize,
     47 }
     48 
     49 #[derive(Debug, Clone, PartialEq, Eq)]
     50 pub struct Buffer {
     51     kind: BufferKind,
     52     handle: Window,
     53     window: Cell<Option<Window>>,
     54     grip: Cell<Option<Grip>>,
     55     grip_pos: Cell<Option<Pos>>,
     56     window_region: Cell<Option<Region>>,
     57 }
     58 
     59 impl Buffer {
     60     pub fn new(
     61         kind: BufferKind,
     62         handle: Window,
     63     ) -> Self {
     64         Self {
     65             kind,
     66             handle,
     67             window: Cell::new(None),
     68             grip: Cell::new(None),
     69             grip_pos: Cell::new(None),
     70             window_region: Cell::new(None),
     71         }
     72     }
     73 
     74     #[inline(always)]
     75     pub fn set(
     76         &self,
     77         window: Window,
     78         grip: Grip,
     79         pos: Pos,
     80         region: Region,
     81     ) {
     82         self.window.set(Some(window));
     83         self.grip.set(Some(grip));
     84         self.grip_pos.set(Some(pos));
     85         self.window_region.set(Some(region));
     86     }
     87 
     88     #[inline(always)]
     89     pub fn unset(&self) {
     90         self.window.set(None);
     91         self.grip.set(None);
     92         self.grip_pos.set(None);
     93         self.window_region.set(None);
     94     }
     95 
     96     #[inline(always)]
     97     pub fn is_occupied(&self) -> bool {
     98         self.window.get().is_some()
     99     }
    100 
    101     #[inline(always)]
    102     pub fn handle(&self) -> Window {
    103         self.handle
    104     }
    105 
    106     #[inline(always)]
    107     pub fn window(&self) -> Option<Window> {
    108         self.window.get()
    109     }
    110 
    111     #[inline(always)]
    112     pub fn grip(&self) -> Option<Grip> {
    113         self.grip.get()
    114     }
    115 
    116     #[inline(always)]
    117     pub fn grip_pos(&self) -> Option<Pos> {
    118         self.grip_pos.get()
    119     }
    120 
    121     #[inline(always)]
    122     pub fn set_grip_pos(
    123         &self,
    124         pos: Pos,
    125     ) {
    126         self.grip_pos.set(Some(pos));
    127     }
    128 
    129     #[inline(always)]
    130     pub fn window_region(&self) -> Option<Region> {
    131         self.window_region.get()
    132     }
    133 
    134     #[inline(always)]
    135     pub fn set_window_region(
    136         &self,
    137         region: Region,
    138     ) {
    139         self.window_region.set(Some(region));
    140     }
    141 }
    142 
    143 #[derive(Debug, Clone)]
    144 pub struct Scratchpad {
    145     command: String,
    146     client: Cell<Option<Window>>,
    147     active: Cell<bool>,
    148 }
    149 
    150 #[derive(Debug, Clone)]
    151 pub struct Workspace {
    152     number: Ident,
    153     name: String,
    154     root_zone: ZoneId,
    155     focus_zones: RefCell<Cycle<ZoneId>>,
    156     spawn_zones: RefCell<Cycle<ZoneId>>,
    157     clients: RefCell<Cycle<Window>>,
    158     icons: RefCell<Cycle<Window>>,
    159 }
    160 
    161 impl Workspace {
    162     pub fn new(
    163         name: impl Into<String>,
    164         number: Ident,
    165         root_zone: ZoneId,
    166     ) -> Self {
    167         Self {
    168             number,
    169             name: name.into(),
    170             root_zone,
    171             focus_zones: RefCell::new(Cycle::new(vec![root_zone], true)),
    172             spawn_zones: RefCell::new(Cycle::new(vec![root_zone], true)),
    173             clients: RefCell::new(Cycle::new(Vec::new(), true)),
    174             icons: RefCell::new(Cycle::new(Vec::new(), true)),
    175         }
    176     }
    177 
    178     #[inline(always)]
    179     pub fn number(&self) -> Ident {
    180         self.number
    181     }
    182 
    183     #[inline(always)]
    184     pub fn name(&self) -> &str {
    185         &self.name
    186     }
    187 
    188     #[inline(always)]
    189     pub fn root_zone(&self) -> ZoneId {
    190         self.root_zone
    191     }
    192 
    193     #[inline(always)]
    194     pub fn len(&self) -> usize {
    195         self.clients.borrow().len()
    196     }
    197 
    198     #[inline(always)]
    199     pub fn contains(
    200         &self,
    201         window: Window,
    202     ) -> bool {
    203         self.clients.borrow().contains(&window)
    204     }
    205 
    206     #[inline(always)]
    207     pub fn is_empty(&self) -> bool {
    208         self.clients.borrow().is_empty()
    209     }
    210 
    211     #[inline(always)]
    212     pub fn clients(&self) -> Vec<Window> {
    213         self.clients
    214             .borrow()
    215             .iter()
    216             .cloned()
    217             .collect::<Vec<Window>>()
    218     }
    219 
    220     #[inline(always)]
    221     pub fn on_each_client<F>(
    222         &self,
    223         client_map: &HashMap<Window, Client, BuildIdHasher>,
    224         func: F,
    225     ) where
    226         F: Fn(&Client),
    227     {
    228         self.clients
    229             .borrow()
    230             .iter()
    231             .for_each(|window| func(&client_map[window]));
    232     }
    233 
    234     #[inline(always)]
    235     pub fn on_each_client_mut<F>(
    236         &self,
    237         client_map: &HashMap<Window, Client, BuildIdHasher>,
    238         mut func: F,
    239     ) where
    240         F: FnMut(&Client),
    241     {
    242         self.clients
    243             .borrow()
    244             .iter()
    245             .for_each(|window| func(&client_map[window]));
    246     }
    247 
    248     #[inline(always)]
    249     pub fn stack(&self) -> VecDeque<Window> {
    250         self.clients.borrow().stack().clone()
    251     }
    252 
    253     #[inline(always)]
    254     pub fn stack_after_focus(&self) -> Vec<Window> {
    255         self.clients.borrow().stack_after_focus()
    256     }
    257 
    258     #[inline(always)]
    259     pub fn active_focus_zone(&self) -> Option<ZoneId> {
    260         self.focus_zones.borrow().active_element().copied()
    261     }
    262 
    263     #[inline(always)]
    264     pub fn active_spawn_zone(&self) -> Option<ZoneId> {
    265         self.spawn_zones.borrow().active_element().copied()
    266     }
    267 
    268     #[inline(always)]
    269     pub fn focused_client(&self) -> Option<Window> {
    270         self.clients.borrow().active_element().copied()
    271     }
    272 
    273     #[inline(always)]
    274     pub fn get_client_for(
    275         &self,
    276         sel: ClientSelector,
    277         zone_manager: &ZoneManager,
    278     ) -> Option<Window> {
    279         self.clients
    280             .borrow()
    281             .get_for(&match sel {
    282                 ClientSelector::AtActive => Selector::AtActive,
    283                 ClientSelector::AtMaster => {
    284                     self.focus_zones.borrow().active_element().map(|&id| {
    285                         let cycle = zone_manager.nearest_cycle(id);
    286                         let cycle = zone_manager.zone(cycle);
    287 
    288                         Selector::AtIndex(std::cmp::min(
    289                             cycle.data().unwrap().main_count as usize,
    290                             self.clients.borrow().len(),
    291                         ))
    292                     })?
    293                 },
    294                 ClientSelector::AtIndex(index) => Selector::AtIndex(index),
    295                 ClientSelector::AtIdent(window) => Selector::AtIdent(window),
    296                 ClientSelector::First => Selector::First,
    297                 ClientSelector::Last => Selector::Last,
    298             })
    299             .cloned()
    300     }
    301 
    302     #[inline(always)]
    303     pub fn next_client(
    304         &self,
    305         dir: Direction,
    306     ) -> Option<Window> {
    307         self.clients.borrow().next_element(dir).copied()
    308     }
    309 
    310     #[inline(always)]
    311     pub fn add_zone(
    312         &self,
    313         id: ZoneId,
    314         insert: &InsertPos,
    315     ) {
    316         self.focus_zones.borrow_mut().insert_at(insert, id);
    317         self.spawn_zones.borrow_mut().insert_at(insert, id);
    318     }
    319 
    320     #[inline(always)]
    321     pub fn add_client(
    322         &self,
    323         window: Window,
    324         insert: &InsertPos,
    325     ) {
    326         self.clients.borrow_mut().insert_at(insert, window);
    327     }
    328 
    329     #[inline(always)]
    330     pub fn replace_client(
    331         &self,
    332         window: Window,
    333         replacement: Window,
    334     ) {
    335         self.clients
    336             .borrow_mut()
    337             .remove_for(&Selector::AtIdent(replacement));
    338 
    339         self.clients
    340             .borrow_mut()
    341             .insert_at(&InsertPos::BeforeIdent(window), replacement);
    342 
    343         self.clients
    344             .borrow_mut()
    345             .remove_for(&Selector::AtIdent(window));
    346     }
    347 
    348     #[inline(always)]
    349     pub fn activate_zone(
    350         &self,
    351         id: ZoneId,
    352     ) -> Option<ZoneId> {
    353         let prev_active = self.focus_zones.borrow().active_element()?.to_owned();
    354 
    355         self.focus_zones
    356             .borrow_mut()
    357             .activate_for(&Selector::AtIdent(id));
    358 
    359         Some(prev_active)
    360     }
    361 
    362     #[inline(always)]
    363     pub fn focus_client(
    364         &self,
    365         window: Window,
    366     ) -> Option<Window> {
    367         let prev_active = self.clients.borrow().active_element()?.to_owned();
    368 
    369         self.clients
    370             .borrow_mut()
    371             .activate_for(&Selector::AtIdent(window));
    372 
    373         Some(prev_active)
    374     }
    375 
    376     #[inline(always)]
    377     pub fn remove_zone(
    378         &self,
    379         id: ZoneId,
    380     ) {
    381         self.focus_zones
    382             .borrow_mut()
    383             .remove_for(&Selector::AtIdent(id));
    384 
    385         self.spawn_zones
    386             .borrow_mut()
    387             .remove_for(&Selector::AtIdent(id));
    388     }
    389 
    390     #[inline(always)]
    391     pub fn remove_client(
    392         &self,
    393         window: Window,
    394     ) -> Option<Window> {
    395         self.clients
    396             .borrow_mut()
    397             .remove_for(&Selector::AtIdent(window))
    398     }
    399 
    400     #[inline(always)]
    401     pub fn remove_focused_client(&self) -> Option<Window> {
    402         self.clients.borrow_mut().remove_for(&Selector::AtActive)
    403     }
    404 
    405     pub fn arrange<F>(
    406         &self,
    407         zone_manager: &ZoneManager,
    408         client_map: &HashMap<Window, Client, BuildIdHasher>,
    409         screen_region: Region,
    410         ignore_filter: F,
    411     ) -> Vec<Placement>
    412     where
    413         F: Fn(&Client) -> bool,
    414     {
    415         if self.clients.borrow().is_empty() {
    416             return Vec::with_capacity(0);
    417         }
    418 
    419         zone_manager.zone(self.root_zone).set_region(screen_region);
    420 
    421         let (to_ignore_ids, to_ignore_clients): (HashSet<_>, Vec<_>) = self
    422             .clients
    423             .borrow()
    424             .iter()
    425             .chain(self.icons.borrow().iter())
    426             .map(|window| &client_map[window])
    427             .filter(|&client| ignore_filter(client))
    428             .map(|client| (client.zone(), client))
    429             .unzip();
    430 
    431         zone_manager
    432             .arrange(self.root_zone, &to_ignore_ids)
    433             .into_iter()
    434             .chain(to_ignore_clients.into_iter().map(|client| {
    435                 let (method, region, decoration) =
    436                     if client.is_fullscreen() && !client.is_contained() {
    437                         (
    438                             PlacementMethod::Tile,
    439                             PlacementRegion::NewRegion(screen_region),
    440                             Decoration::NO_DECORATION,
    441                         )
    442                     } else if client.is_iconified() {
    443                         (
    444                             PlacementMethod::Tile,
    445                             PlacementRegion::NoRegion,
    446                             Decoration::NO_DECORATION,
    447                         )
    448                     } else {
    449                         (
    450                             PlacementMethod::Free,
    451                             PlacementRegion::FreeRegion,
    452                             Decoration::FREE_DECORATION,
    453                         )
    454                     };
    455 
    456                 Placement {
    457                     method,
    458                     kind: PlacementTarget::Client(client.window()),
    459                     zone: client.zone(),
    460                     region,
    461                     decoration,
    462                 }
    463             }))
    464             .collect()
    465     }
    466 
    467     #[inline(always)]
    468     pub fn cycle_zones(
    469         &self,
    470         dir: Direction,
    471         zone_manager: &ZoneManager,
    472     ) -> Option<(ZoneId, ZoneId)> {
    473         if self.spawn_zones.borrow().len() < 2 {
    474             return None;
    475         }
    476 
    477         let prev_active = self.spawn_zones.borrow().active_element()?.to_owned();
    478         let mut now_active = self.spawn_zones.borrow_mut().cycle_active(dir)?.to_owned();
    479 
    480         loop {
    481             if zone_manager.is_cycle(now_active) {
    482                 return Some((prev_active, now_active));
    483             }
    484 
    485             now_active = self.spawn_zones.borrow_mut().cycle_active(dir)?.to_owned();
    486         }
    487     }
    488 
    489     #[inline(always)]
    490     pub fn cycle_focus(
    491         &self,
    492         dir: Direction,
    493         client_map: &HashMap<Window, Client, BuildIdHasher>,
    494         zone_manager: &ZoneManager,
    495     ) -> Option<(Window, Window)> {
    496         if self.clients.borrow().len() < 2 {
    497             return None;
    498         }
    499 
    500         let prev_active = self.clients.borrow().active_element()?.to_owned();
    501         let id = client_map[&prev_active].zone();
    502         let config = zone_manager.active_layoutconfig(id);
    503 
    504         if let Some(config) = config {
    505             if !config.wraps && self.clients.borrow().next_will_wrap(dir) {
    506                 return None;
    507             }
    508         }
    509 
    510         let now_active = self.clients.borrow_mut().cycle_active(dir)?.to_owned();
    511 
    512         if prev_active != now_active {
    513             Some((prev_active, now_active))
    514         } else {
    515             None
    516         }
    517     }
    518 
    519     #[inline(always)]
    520     pub fn drag_focus(
    521         &self,
    522         dir: Direction,
    523     ) -> Option<Window> {
    524         self.clients.borrow_mut().drag_active(dir).copied()
    525     }
    526 
    527     #[inline(always)]
    528     pub fn rotate_clients(
    529         &self,
    530         dir: Direction,
    531     ) -> Option<(Window, Window)> {
    532         if self.clients.borrow().len() < 2 {
    533             return None;
    534         }
    535 
    536         let prev_active = self.clients.borrow().active_element()?.to_owned();
    537         self.clients.borrow_mut().rotate(dir);
    538         let now_active = self.clients.borrow().active_element()?.to_owned();
    539 
    540         if prev_active != now_active {
    541             Some((prev_active, now_active))
    542         } else {
    543             None
    544         }
    545     }
    546 
    547     #[inline(always)]
    548     pub fn copy_prev_layout_data(
    549         &self,
    550         zone_manager: &mut ZoneManager,
    551     ) -> Result<(), StateChangeError> {
    552         let &id = self
    553             .focus_zones
    554             .borrow()
    555             .active_element()
    556             .ok_or(StateChangeError::EarlyStop)?;
    557 
    558         let prev_data = zone_manager
    559             .active_prev_data(id)
    560             .ok_or(StateChangeError::EarlyStop)?
    561             .to_owned();
    562 
    563         let data = zone_manager
    564             .active_data_mut(id)
    565             .ok_or(StateChangeError::EarlyStop)?;
    566 
    567         Ok(*data = prev_data)
    568     }
    569 
    570     #[inline(always)]
    571     pub fn reset_layout_data(
    572         &self,
    573         zone_manager: &mut ZoneManager,
    574     ) -> Result<(), StateChangeError> {
    575         let &id = self
    576             .focus_zones
    577             .borrow()
    578             .active_element()
    579             .ok_or(StateChangeError::EarlyStop)?;
    580 
    581         let default_data = zone_manager
    582             .active_default_data(id)
    583             .ok_or(StateChangeError::EarlyStop)?;
    584 
    585         let data = zone_manager
    586             .active_data_mut(id)
    587             .ok_or(StateChangeError::EarlyStop)?;
    588 
    589         Ok(*data = default_data)
    590     }
    591 
    592     #[inline(always)]
    593     pub fn change_gap_size(
    594         &self,
    595         change: Change<u32>,
    596         zone_manager: &mut ZoneManager,
    597     ) -> Result<(), StateChangeError> {
    598         let &id = self
    599             .focus_zones
    600             .borrow()
    601             .active_element()
    602             .ok_or(StateChangeError::EarlyStop)?;
    603 
    604         let data = zone_manager
    605             .active_data_mut(id)
    606             .ok_or(StateChangeError::EarlyStop)?;
    607 
    608         let new_gap_size = match change {
    609             Change::Inc(delta) => std::cmp::min(data.gap_size + delta, Layout::MAX_GAP_SIZE),
    610             Change::Dec(delta) => std::cmp::max(data.gap_size as i32 - delta as i32, 0) as u32,
    611         };
    612 
    613         if new_gap_size == data.gap_size {
    614             return Err(StateChangeError::LimitReached);
    615         }
    616 
    617         Ok(data.gap_size = new_gap_size)
    618     }
    619 
    620     #[inline(always)]
    621     pub fn reset_gap_size(
    622         &self,
    623         zone_manager: &mut ZoneManager,
    624     ) -> Result<(), StateChangeError> {
    625         let &id = self
    626             .focus_zones
    627             .borrow()
    628             .active_element()
    629             .ok_or(StateChangeError::EarlyStop)?;
    630 
    631         let default_data = zone_manager
    632             .active_default_data(id)
    633             .ok_or(StateChangeError::EarlyStop)?;
    634 
    635         let data = zone_manager
    636             .active_data_mut(id)
    637             .ok_or(StateChangeError::EarlyStop)?;
    638 
    639         Ok(data.gap_size = default_data.gap_size)
    640     }
    641 
    642     #[inline(always)]
    643     pub fn change_main_count(
    644         &self,
    645         change: Change<u32>,
    646         zone_manager: &mut ZoneManager,
    647     ) -> Result<(), StateChangeError> {
    648         let &id = self
    649             .focus_zones
    650             .borrow()
    651             .active_element()
    652             .ok_or(StateChangeError::EarlyStop)?;
    653 
    654         let data = zone_manager
    655             .active_data_mut(id)
    656             .ok_or(StateChangeError::EarlyStop)?;
    657 
    658         let new_main_count = match change {
    659             Change::Inc(delta) => std::cmp::min(data.main_count + delta, Layout::MAX_MAIN_COUNT),
    660             Change::Dec(delta) => std::cmp::max(data.main_count - delta, 0),
    661         };
    662 
    663         if data.main_count == new_main_count {
    664             Err(StateChangeError::LimitReached)
    665         } else {
    666             Ok(data.main_count = new_main_count)
    667         }
    668     }
    669 
    670     #[inline(always)]
    671     pub fn change_main_factor(
    672         &self,
    673         change: Change<f32>,
    674         zone_manager: &mut ZoneManager,
    675     ) -> Result<(), StateChangeError> {
    676         let &id = self
    677             .focus_zones
    678             .borrow()
    679             .active_element()
    680             .ok_or(StateChangeError::EarlyStop)?;
    681 
    682         let data = zone_manager
    683             .active_data_mut(id)
    684             .ok_or(StateChangeError::EarlyStop)?;
    685 
    686         match change {
    687             Change::Inc(delta) => data.main_factor += delta,
    688             Change::Dec(delta) => data.main_factor -= delta,
    689         }
    690 
    691         if data.main_factor < 0.05f32 {
    692             data.main_factor = 0.05f32;
    693         } else if data.main_factor > 0.95f32 {
    694             data.main_factor = 0.95f32;
    695         }
    696 
    697         Ok(())
    698     }
    699 
    700     #[inline(always)]
    701     pub fn change_margin(
    702         &self,
    703         edge: Edge,
    704         change: Change<i32>,
    705         zone_manager: &mut ZoneManager,
    706     ) -> Result<(), StateChangeError> {
    707         let &id = self
    708             .focus_zones
    709             .borrow()
    710             .active_element()
    711             .ok_or(StateChangeError::EarlyStop)?;
    712 
    713         let data = zone_manager
    714             .active_data_mut(id)
    715             .ok_or(StateChangeError::EarlyStop)?;
    716 
    717         let delta_change = match change {
    718             Change::Inc(delta) => delta,
    719             Change::Dec(delta) => -delta,
    720         };
    721 
    722         let (edge_value, edge_max) = match edge {
    723             Edge::Left => (&mut data.margin.left, Layout::MAX_MARGIN.left),
    724             Edge::Right => (&mut data.margin.right, Layout::MAX_MARGIN.right),
    725             Edge::Top => (&mut data.margin.top, Layout::MAX_MARGIN.top),
    726             Edge::Bottom => (&mut data.margin.bottom, Layout::MAX_MARGIN.bottom),
    727         };
    728 
    729         let edge_changed = *edge_value + delta_change;
    730         let edge_changed = std::cmp::max(edge_changed, 0);
    731         let edge_changed = std::cmp::min(edge_changed, edge_max);
    732 
    733         if *edge_value == edge_changed {
    734             Err(StateChangeError::LimitReached)
    735         } else {
    736             Ok(*edge_value = edge_changed)
    737         }
    738     }
    739 
    740     #[inline(always)]
    741     pub fn reset_margin(
    742         &self,
    743         zone_manager: &mut ZoneManager,
    744     ) -> Result<(), StateChangeError> {
    745         let &id = self
    746             .focus_zones
    747             .borrow()
    748             .active_element()
    749             .ok_or(StateChangeError::EarlyStop)?;
    750 
    751         let default_data = zone_manager
    752             .active_default_data(id)
    753             .ok_or(StateChangeError::EarlyStop)?;
    754 
    755         let data = zone_manager
    756             .active_data_mut(id)
    757             .ok_or(StateChangeError::EarlyStop)?;
    758 
    759         Ok(data.margin = default_data.margin)
    760     }
    761 
    762     #[inline(always)]
    763     pub fn focused_icon(&self) -> Option<Window> {
    764         self.icons.borrow().active_element().copied()
    765     }
    766 
    767     #[inline(always)]
    768     pub fn icon_to_client(
    769         &self,
    770         window: Window,
    771     ) {
    772         if let Some(icon) = self.remove_icon(window) {
    773             self.add_client(icon, &InsertPos::Back);
    774         }
    775     }
    776 
    777     #[inline(always)]
    778     pub fn client_to_icon(
    779         &self,
    780         window: Window,
    781     ) {
    782         if let Some(client) = self.remove_client(window) {
    783             self.add_icon(client);
    784         }
    785     }
    786 
    787     #[inline(always)]
    788     pub fn add_icon(
    789         &self,
    790         window: Window,
    791     ) {
    792         self.icons.borrow_mut().insert_at(&InsertPos::Back, window);
    793     }
    794 
    795     #[inline(always)]
    796     pub fn remove_icon(
    797         &self,
    798         window: Window,
    799     ) -> Option<Window> {
    800         self.icons
    801             .borrow_mut()
    802             .remove_for(&Selector::AtIdent(window))
    803     }
    804 }
    805 
    806 impl Identify for Workspace {
    807     #[inline(always)]
    808     fn id(&self) -> Ident {
    809         self.number
    810     }
    811 }
    812 
    813 impl PartialEq for Workspace {
    814     fn eq(
    815         &self,
    816         other: &Self,
    817     ) -> bool {
    818         self.number == other.number
    819     }
    820 }