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 }