wzrd

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

zone.rs (19903B)


      1 use crate::change::Disposition;
      2 use crate::cycle::Cycle;
      3 use crate::cycle::InsertPos;
      4 use crate::cycle::Selector;
      5 use crate::decoration::Border;
      6 use crate::decoration::Decoration;
      7 use crate::error::StateChangeError;
      8 use crate::identify::Ident;
      9 use crate::identify::Identify;
     10 use crate::layout::Apply;
     11 use crate::layout::Layout;
     12 use crate::layout::LayoutConfig;
     13 use crate::layout::LayoutData;
     14 use crate::layout::LayoutKind;
     15 use crate::placement::Placement;
     16 use crate::placement::PlacementMethod;
     17 use crate::placement::PlacementRegion;
     18 use crate::placement::PlacementTarget;
     19 
     20 use winsys::geometry::Region;
     21 use winsys::window::Window;
     22 
     23 use std::cell::Cell;
     24 use std::collections::HashMap;
     25 use std::collections::HashSet;
     26 use std::sync::atomic;
     27 use std::vec::Vec;
     28 
     29 pub type ZoneId = u32;
     30 
     31 static INSTANCE_COUNT: atomic::AtomicU32 = atomic::AtomicU32::new(1);
     32 
     33 #[derive(Debug, PartialEq)]
     34 pub enum ZoneContent {
     35     Client(Window),
     36     Tab(Cycle<ZoneId>),
     37     Layout(Layout, Cycle<ZoneId>),
     38 }
     39 
     40 #[derive(Debug)]
     41 pub struct Zone {
     42     id: ZoneId,
     43     parent: Cell<Option<ZoneId>>,
     44     method: Cell<PlacementMethod>,
     45     content: ZoneContent,
     46     region: Cell<Region>,
     47     decoration: Cell<Decoration>,
     48     is_visible: Cell<bool>,
     49 }
     50 
     51 impl Zone {
     52     fn next_id() -> ZoneId {
     53         INSTANCE_COUNT.fetch_add(1, atomic::Ordering::Relaxed)
     54     }
     55 
     56     fn new(
     57         parent: Option<ZoneId>,
     58         content: ZoneContent,
     59         region: Region,
     60     ) -> (ZoneId, Self) {
     61         let id = Self::next_id();
     62 
     63         (id, Self {
     64             id,
     65             parent: Cell::new(parent),
     66             method: Cell::new(PlacementMethod::Free),
     67             content,
     68             region: Cell::new(region),
     69             decoration: Cell::new(Decoration::NO_DECORATION),
     70             is_visible: Cell::new(true),
     71         })
     72     }
     73 
     74     pub fn set_content(
     75         &mut self,
     76         content: ZoneContent,
     77     ) {
     78         self.content = content;
     79     }
     80 
     81     fn set_kind(
     82         &mut self,
     83         kind: LayoutKind,
     84     ) -> Result<LayoutKind, StateChangeError> {
     85         match self.content {
     86             ZoneContent::Layout(ref mut layout, _) => layout.set_kind(kind),
     87             _ => Err(StateChangeError::InvalidCaller),
     88         }
     89     }
     90 
     91     pub fn prev_kind(&self) -> Result<LayoutKind, StateChangeError> {
     92         match &self.content {
     93             ZoneContent::Layout(layout, _) => Ok(layout.prev_kind()),
     94             _ => Err(StateChangeError::InvalidCaller),
     95         }
     96     }
     97 
     98     pub fn kind(&self) -> Result<LayoutKind, StateChangeError> {
     99         match &self.content {
    100             ZoneContent::Layout(layout, _) => Ok(layout.kind()),
    101             _ => Err(StateChangeError::InvalidCaller),
    102         }
    103     }
    104 
    105     pub fn set_region(
    106         &self,
    107         region: Region,
    108     ) {
    109         self.region.set(region);
    110     }
    111 
    112     pub fn set_method(
    113         &self,
    114         method: PlacementMethod,
    115     ) {
    116         self.method.set(method);
    117     }
    118 
    119     pub fn default_data(&self) -> Option<LayoutData> {
    120         match &self.content {
    121             ZoneContent::Layout(layout, _) => Some(layout.default_data()),
    122             _ => None,
    123         }
    124     }
    125 
    126     pub fn data(&self) -> Option<&LayoutData> {
    127         match self.content {
    128             ZoneContent::Layout(ref layout, _) => Some(layout.data()),
    129             _ => None,
    130         }
    131     }
    132 
    133     pub fn data_mut(&mut self) -> Option<&mut LayoutData> {
    134         match self.content {
    135             ZoneContent::Layout(ref mut layout, _) => Some(layout.data_mut()),
    136             _ => None,
    137         }
    138     }
    139 
    140     pub fn prev_data(&self) -> Option<&LayoutData> {
    141         match self.content {
    142             ZoneContent::Layout(ref layout, _) => Some(layout.prev_data()),
    143             _ => None,
    144         }
    145     }
    146 
    147     pub fn config(&self) -> Option<LayoutConfig> {
    148         match self.content {
    149             ZoneContent::Layout(ref layout, _) => Some(layout.config()),
    150             _ => None,
    151         }
    152     }
    153 
    154     pub fn method(&self) -> PlacementMethod {
    155         self.method.get()
    156     }
    157 }
    158 
    159 enum ZoneChange {
    160     Visible(bool),
    161     Region(Region),
    162     Decoration(Decoration),
    163     Method(PlacementMethod),
    164 }
    165 
    166 pub struct ZoneManager {
    167     zone_map: HashMap<ZoneId, Zone>,
    168     persistent_data_copy: bool,
    169 }
    170 
    171 impl ZoneManager {
    172     pub fn new() -> Self {
    173         Self {
    174             zone_map: HashMap::new(),
    175             persistent_data_copy: true,
    176         }
    177     }
    178 
    179     pub fn new_zone(
    180         &mut self,
    181         parent: Option<ZoneId>,
    182         content: ZoneContent,
    183     ) -> ZoneId {
    184         let (id, zone) = Zone::new(parent, content, Region::new(0, 0, 0, 0));
    185         let parent = parent.and_then(|p| self.zone_map.get_mut(&p));
    186 
    187         if let Some(parent) = parent {
    188             match &mut parent.content {
    189                 ZoneContent::Tab(zones) | ZoneContent::Layout(_, zones) => {
    190                     zones.insert_at(&InsertPos::AfterActive, id)
    191                 },
    192                 _ => unreachable!("attempted to insert into non-cycle"),
    193             }
    194         }
    195 
    196         self.zone_map.insert(id, zone);
    197         id
    198     }
    199 
    200     pub fn remove_zone(
    201         &mut self,
    202         id: ZoneId,
    203     ) {
    204         let cycle = self.nearest_cycle(id);
    205         let cycle = self.zone_map.get_mut(&cycle).unwrap();
    206 
    207         match &mut cycle.content {
    208             ZoneContent::Tab(zones) | ZoneContent::Layout(_, zones) => {
    209                 zones.remove_for(&Selector::AtIdent(id));
    210             },
    211             _ => {},
    212         }
    213     }
    214 
    215     pub fn activate_zone(
    216         &self,
    217         id: ZoneId,
    218     ) {
    219         if let Some(cycle_id) = self.next_cycle(id) {
    220             let cycle = self.zone(cycle_id);
    221 
    222             match cycle.content {
    223                 ZoneContent::Tab(ref zones) | ZoneContent::Layout(_, ref zones) => {
    224                     zones.activate_for(&Selector::AtIdent(id));
    225                     self.activate_zone(cycle_id);
    226                 },
    227                 _ => {},
    228             }
    229         }
    230     }
    231 
    232     pub fn set_kind(
    233         &mut self,
    234         id: ZoneId,
    235         kind: LayoutKind,
    236     ) -> Result<LayoutKind, StateChangeError> {
    237         let persistent_data_copy = self.persistent_data_copy;
    238         let cycle = self.nearest_cycle(id);
    239         let cycle = self.zone_mut(cycle);
    240 
    241         let prev_kind = cycle.set_kind(kind)?;
    242 
    243         if persistent_data_copy {
    244             let prev_data = *cycle.prev_data().unwrap();
    245             let data = cycle.data_mut().unwrap();
    246             *data = prev_data;
    247         }
    248 
    249         Ok(prev_kind)
    250     }
    251 
    252     pub fn set_prev_kind(
    253         &mut self,
    254         id: ZoneId,
    255     ) -> Result<LayoutKind, StateChangeError> {
    256         let persistent_data_copy = self.persistent_data_copy;
    257         let cycle = self.nearest_cycle(id);
    258         let cycle = self.zone_mut(cycle);
    259 
    260         let kind = cycle.prev_kind()?;
    261         let prev_kind = cycle.set_kind(kind)?;
    262 
    263         if persistent_data_copy {
    264             let prev_data = *cycle.prev_data().unwrap();
    265             let data = cycle.data_mut().unwrap();
    266             *data = prev_data;
    267         }
    268 
    269         Ok(prev_kind)
    270     }
    271 
    272     pub fn active_default_data(
    273         &mut self,
    274         id: ZoneId,
    275     ) -> Option<LayoutData> {
    276         let cycle = self.nearest_cycle(id);
    277         let cycle = self.zone(cycle);
    278 
    279         cycle.default_data()
    280     }
    281 
    282     pub fn active_prev_data(
    283         &mut self,
    284         id: ZoneId,
    285     ) -> Option<&LayoutData> {
    286         let cycle = self.nearest_cycle(id);
    287         let cycle = self.zone_mut(cycle);
    288 
    289         cycle.prev_data()
    290     }
    291 
    292     pub fn active_data_mut(
    293         &mut self,
    294         id: ZoneId,
    295     ) -> Option<&mut LayoutData> {
    296         let cycle = self.nearest_cycle(id);
    297         let cycle = self.zone_mut(cycle);
    298 
    299         cycle.data_mut()
    300     }
    301 
    302     pub fn active_layoutconfig(
    303         &self,
    304         id: ZoneId,
    305     ) -> Option<LayoutConfig> {
    306         let cycle = self.nearest_cycle(id);
    307         let cycle = self.zone(cycle);
    308 
    309         cycle.config()
    310     }
    311 
    312     pub fn zone_checked(
    313         &self,
    314         id: ZoneId,
    315     ) -> Option<&Zone> {
    316         self.zone_map.get(&id)
    317     }
    318 
    319     pub fn zone(
    320         &self,
    321         id: ZoneId,
    322     ) -> &Zone {
    323         self.zone_map.get(&id).unwrap()
    324     }
    325 
    326     pub fn zone_checked_mut(
    327         &mut self,
    328         id: ZoneId,
    329     ) -> Option<&mut Zone> {
    330         self.zone_map.get_mut(&id)
    331     }
    332 
    333     pub fn zone_mut(
    334         &mut self,
    335         id: ZoneId,
    336     ) -> &mut Zone {
    337         self.zone_map.get_mut(&id).unwrap()
    338     }
    339 
    340     pub fn parent_id(
    341         &self,
    342         id: ZoneId,
    343     ) -> Option<ZoneId> {
    344         self.zone_map.get(&id).and_then(|zone| zone.parent.get())
    345     }
    346 
    347     pub fn cycle_config(
    348         &self,
    349         id: ZoneId,
    350     ) -> Option<(ZoneId, Option<LayoutConfig>)> {
    351         let cycle = self.next_cycle(id)?;
    352         let zone = self.zone(cycle);
    353 
    354         Some((cycle, zone.config()))
    355     }
    356 
    357     pub fn is_cycle(
    358         &self,
    359         id: ZoneId,
    360     ) -> bool {
    361         let zone = self.zone_map.get(&id).unwrap();
    362 
    363         match zone.content {
    364             ZoneContent::Tab(_) | ZoneContent::Layout(..) => true,
    365             _ => false,
    366         }
    367     }
    368 
    369     pub fn nearest_cycle(
    370         &self,
    371         id: ZoneId,
    372     ) -> ZoneId {
    373         let mut next = id;
    374 
    375         loop {
    376             let zone = self.zone_map.get(&next).unwrap();
    377 
    378             match zone.content {
    379                 ZoneContent::Tab(_) | ZoneContent::Layout(..) => {
    380                     return next;
    381                 },
    382                 _ => {},
    383             }
    384 
    385             if let Some(parent) = zone.parent.get() {
    386                 next = parent;
    387             } else {
    388                 unreachable!("no nearest cycle found");
    389             }
    390         }
    391     }
    392 
    393     pub fn next_cycle(
    394         &self,
    395         mut id: ZoneId,
    396     ) -> Option<ZoneId> {
    397         while let Some(next_id) = self.parent_id(id) {
    398             let zone = self.zone_map.get(&next_id).unwrap();
    399 
    400             match zone.content {
    401                 ZoneContent::Tab(_) | ZoneContent::Layout(..) => {
    402                     return Some(next_id);
    403                 },
    404                 _ => id = next_id,
    405             }
    406         }
    407 
    408         None
    409     }
    410 
    411     pub fn is_within_persisent(
    412         &self,
    413         mut id: ZoneId,
    414     ) -> bool {
    415         while let Some(next_id) = self.parent_id(id) {
    416             let zone = self.zone_map.get(&next_id).unwrap();
    417 
    418             match zone.content {
    419                 ZoneContent::Tab(_) => {
    420                     return true;
    421                 },
    422                 ZoneContent::Layout(ref layout, _) => {
    423                     if layout.config().persistent {
    424                         return true;
    425                     }
    426                 },
    427                 _ => {},
    428             }
    429 
    430             id = next_id;
    431         }
    432 
    433         false
    434     }
    435 
    436     fn gather_subzones(
    437         &self,
    438         zone: ZoneId,
    439         recurse: bool,
    440     ) -> Vec<ZoneId> {
    441         if let Some(zone) = self.zone_map.get(&zone) {
    442             match &zone.content {
    443                 ZoneContent::Client(_) => {},
    444                 ZoneContent::Tab(zones) | ZoneContent::Layout(_, zones) => {
    445                     let mut zones = zones.as_vec();
    446 
    447                     if recurse {
    448                         let mut subzones = Vec::new();
    449 
    450                         zones.iter().for_each(|&zone| {
    451                             subzones.extend(self.gather_subzones(zone, recurse));
    452                         });
    453 
    454                         zones.extend(subzones);
    455                     }
    456 
    457                     return zones;
    458                 },
    459             }
    460         }
    461 
    462         Vec::with_capacity(0)
    463     }
    464 
    465     /// Arrange a zone and all of its subzones within the region
    466     /// of the supplied zone
    467     pub fn arrange(
    468         &self,
    469         zone: ZoneId,
    470         to_ignore: &HashSet<ZoneId>,
    471     ) -> Vec<Placement> {
    472         let cycle = self.nearest_cycle(zone);
    473         let zone = self.zone_map.get(&cycle).unwrap();
    474         let region = zone.region.get();
    475         let decoration = zone.decoration.get();
    476 
    477         let method = match &zone.content {
    478             ZoneContent::Tab(_) => PlacementMethod::Tile,
    479             ZoneContent::Layout(layout, _) => layout.config().method,
    480             _ => unreachable!("attempting to derive method from non-cycle"),
    481         };
    482 
    483         self.arrange_subzones(cycle, region, decoration, method, to_ignore)
    484     }
    485 
    486     fn arrange_subzones(
    487         &self,
    488         id: ZoneId,
    489         region: Region,
    490         decoration: Decoration,
    491         method: PlacementMethod,
    492         to_ignore: &HashSet<ZoneId>,
    493     ) -> Vec<Placement> {
    494         let zone = self.zone_map.get(&id).unwrap();
    495         let content = &zone.content;
    496 
    497         let mut zone_changes: Vec<(ZoneId, ZoneChange)> = Vec::new();
    498 
    499         let placements = match &content {
    500             ZoneContent::Client(window) => {
    501                 return vec![Placement {
    502                     method,
    503                     kind: PlacementTarget::Client(*window),
    504                     zone: id,
    505                     region: if method == PlacementMethod::Free {
    506                         PlacementRegion::FreeRegion
    507                     } else {
    508                         PlacementRegion::NewRegion(region)
    509                     },
    510                     decoration,
    511                 }];
    512             },
    513             ZoneContent::Tab(zones) => {
    514                 let mut placements = vec![Placement {
    515                     method,
    516                     kind: PlacementTarget::Tab(zones.len()),
    517                     zone: id,
    518                     region: if method == PlacementMethod::Free {
    519                         PlacementRegion::FreeRegion
    520                     } else {
    521                         zone_changes.push((id, ZoneChange::Region(region)));
    522                         PlacementRegion::NewRegion(region)
    523                     },
    524                     decoration,
    525                 }];
    526 
    527                 let mut region = region;
    528                 Layout::adjust_for_border(&mut region, 1, &Zone::MIN_ZONE_DIM);
    529 
    530                 let active_element = zones.active_element().copied();
    531                 let zones: Vec<ZoneId> = zones
    532                     .iter()
    533                     .filter(|&id| !to_ignore.contains(id))
    534                     .copied()
    535                     .collect();
    536 
    537                 zones.into_iter().for_each(|id| {
    538                     let is_active_element = Some(id) == active_element;
    539                     let subzones = self.gather_subzones(id, !is_active_element);
    540                     let method = PlacementMethod::Tile;
    541 
    542                     subzones.into_iter().for_each(|id| {
    543                         zone_changes.push((id, ZoneChange::Visible(is_active_element)));
    544                         zone_changes.push((id, ZoneChange::Region(region)));
    545                         zone_changes.push((id, ZoneChange::Method(method)));
    546                     });
    547 
    548                     placements.extend(self.arrange_subzones(
    549                         id,
    550                         region,
    551                         Decoration {
    552                             frame: None,
    553                             border: Some(Border {
    554                                 width: 1,
    555                                 colors: Default::default(),
    556                             }),
    557                         },
    558                         method,
    559                         to_ignore,
    560                     ));
    561                 });
    562 
    563                 placements
    564             },
    565             ZoneContent::Layout(layout, zones) => {
    566                 let active_element = zones.active_element();
    567                 let mut subplacements = Vec::new();
    568                 let mut placements = vec![Placement {
    569                     method,
    570                     kind: PlacementTarget::Layout,
    571                     zone: id,
    572                     region: if method == PlacementMethod::Free {
    573                         PlacementRegion::FreeRegion
    574                     } else {
    575                         PlacementRegion::NewRegion(region)
    576                     },
    577                     decoration,
    578                 }];
    579 
    580                 let zones: Vec<ZoneId> = zones
    581                     .iter()
    582                     .filter(|&id| layout.config().single || !to_ignore.contains(id))
    583                     .copied()
    584                     .collect();
    585 
    586                 let (method, application) = layout.apply(
    587                     region,
    588                     zones.iter().map(|id| Some(id) == active_element).collect(),
    589                 );
    590 
    591                 zones.into_iter().zip(application.into_iter()).for_each(
    592                     |(id, (disposition, is_visible))| {
    593                         let (region, decoration) = match disposition {
    594                             Disposition::Unchanged(decoration) => {
    595                                 let zone = self.zone_map.get(&id).unwrap();
    596                                 (zone.region.get(), decoration)
    597                             },
    598                             Disposition::Changed(region, decoration) => (region, decoration),
    599                         };
    600 
    601                         if !is_visible {
    602                             let subzones = self.gather_subzones(id, true);
    603 
    604                             placements.push(Placement {
    605                                 method,
    606                                 kind: PlacementTarget::from_zone_content(&self.zone(id).content),
    607                                 zone: id,
    608                                 region: PlacementRegion::NoRegion,
    609                                 decoration,
    610                             });
    611 
    612                             zone_changes.extend(
    613                                 subzones
    614                                     .into_iter()
    615                                     .map(|id| {
    616                                         placements.push(Placement {
    617                                             method,
    618                                             kind: PlacementTarget::from_zone_content(
    619                                                 &self.zone(id).content,
    620                                             ),
    621                                             zone: id,
    622                                             region: PlacementRegion::NoRegion,
    623                                             decoration,
    624                                         });
    625 
    626                                         (id, ZoneChange::Visible(false))
    627                                     })
    628                                     .collect::<Vec<(ZoneId, ZoneChange)>>(),
    629                             );
    630                         } else {
    631                             subplacements.push((id, region, decoration));
    632                         }
    633                     },
    634                 );
    635 
    636                 subplacements
    637                     .into_iter()
    638                     .for_each(|(id, region, decoration)| {
    639                         placements.extend(
    640                             self.arrange_subzones(id, region, decoration, method, to_ignore),
    641                         );
    642                     });
    643 
    644                 placements
    645             },
    646         };
    647 
    648         {
    649             let zone = self.zone_map.get(&id).unwrap();
    650             zone.region.set(region);
    651             zone.decoration.set(decoration);
    652             zone.method.set(method);
    653         }
    654 
    655         zone_changes.into_iter().for_each(|(id, change)| {
    656             let zone = self.zone_map.get(&id).unwrap();
    657 
    658             match change {
    659                 ZoneChange::Visible(is_visible) => {
    660                     zone.is_visible.set(is_visible);
    661                 },
    662                 ZoneChange::Region(region) => {
    663                     zone.region.set(region);
    664                 },
    665                 ZoneChange::Decoration(decoration) => {
    666                     zone.decoration.set(decoration);
    667                 },
    668                 ZoneChange::Method(method) => {
    669                     zone.method.set(method);
    670                 },
    671             };
    672         });
    673 
    674         placements
    675     }
    676 }
    677 
    678 impl PartialEq<Self> for Zone {
    679     fn eq(
    680         &self,
    681         other: &Self,
    682     ) -> bool {
    683         self.id == other.id
    684     }
    685 }
    686 
    687 impl Identify for Zone {
    688     fn id(&self) -> Ident {
    689         self.id
    690     }
    691 }