client.rs (22408B)
1 use crate::change::Toggle; 2 use crate::compare::MatchMethod; 3 use crate::decoration::Color; 4 use crate::decoration::Decoration; 5 use crate::identify::Ident; 6 use crate::identify::Identify; 7 use crate::placement::PlacementClass; 8 use crate::zone::ZoneId; 9 10 use winsys::connection::Pid; 11 use winsys::geometry::Extents; 12 use winsys::geometry::Pos; 13 use winsys::geometry::Region; 14 use winsys::hints::SizeHints; 15 use winsys::window::Window; 16 use winsys::window::WindowType; 17 18 use std::cell::Cell; 19 use std::cell::RefCell; 20 use std::time::SystemTime; 21 22 #[derive(Clone, Copy, Debug)] 23 pub enum OutsideState { 24 Focused, 25 FocusedDisowned, 26 FocusedSticky, 27 Unfocused, 28 UnfocusedDisowned, 29 UnfocusedSticky, 30 Urgent, 31 } 32 33 impl std::ops::Not for OutsideState { 34 type Output = Self; 35 36 fn not(self) -> Self::Output { 37 match self { 38 Self::Focused => Self::Unfocused, 39 Self::FocusedDisowned => Self::UnfocusedDisowned, 40 Self::FocusedSticky => Self::UnfocusedSticky, 41 Self::Unfocused => Self::Focused, 42 Self::UnfocusedDisowned => Self::FocusedDisowned, 43 Self::UnfocusedSticky => Self::FocusedSticky, 44 other => other, 45 } 46 } 47 } 48 49 pub struct Client { 50 zone: ZoneId, 51 window: Window, 52 frame: Window, 53 name: RefCell<String>, 54 class: RefCell<String>, 55 instance: RefCell<String>, 56 context: Cell<usize>, 57 workspace: Cell<usize>, 58 window_type: WindowType, 59 active_region: Cell<Region>, 60 previous_region: Cell<Region>, 61 inner_region: Cell<Region>, 62 free_region: Cell<Region>, 63 tile_region: Cell<Region>, 64 decoration: Cell<Decoration>, 65 size_hints: Cell<Option<SizeHints>>, 66 warp_pos: Cell<Option<Pos>>, 67 parent: Option<Window>, 68 children: RefCell<Vec<Window>>, 69 leader: Option<Window>, 70 producer: Cell<Option<Window>>, 71 consumers: RefCell<Vec<Window>>, 72 focused: Cell<bool>, 73 mapped: Cell<bool>, 74 managed: Cell<bool>, 75 urgent: Cell<bool>, 76 floating: Cell<bool>, 77 fullscreen: Cell<bool>, 78 contained: Cell<bool>, 79 invincible: Cell<bool>, 80 sticky: Cell<bool>, 81 iconifyable: Cell<bool>, 82 iconified: Cell<bool>, 83 disowned: Cell<bool>, 84 consuming: Cell<bool>, 85 producing: Cell<bool>, 86 outside_state: Cell<OutsideState>, 87 pid: Option<Pid>, 88 ppid: Option<Pid>, 89 last_focused: Cell<SystemTime>, 90 managed_since: SystemTime, 91 expected_unmap_count: Cell<u8>, 92 } 93 94 impl Identify for Window { 95 #[inline(always)] 96 fn id(&self) -> Ident { 97 *self 98 } 99 } 100 101 impl Identify for Client { 102 #[inline(always)] 103 fn id(&self) -> Ident { 104 self.window 105 } 106 } 107 108 impl Client { 109 pub fn new( 110 zone: ZoneId, 111 window: Window, 112 frame: Window, 113 name: impl Into<String>, 114 class: impl Into<String>, 115 instance: impl Into<String>, 116 window_type: WindowType, 117 pid: Option<Pid>, 118 ppid: Option<Pid>, 119 ) -> Self { 120 Self { 121 zone, 122 window, 123 frame, 124 name: RefCell::new(name.into()), 125 class: RefCell::new(class.into()), 126 instance: RefCell::new(instance.into()), 127 context: Cell::new(0), 128 workspace: Cell::new(0), 129 window_type, 130 active_region: Cell::new(Default::default()), 131 previous_region: Cell::new(Default::default()), 132 inner_region: Cell::new(Default::default()), 133 free_region: Cell::new(Default::default()), 134 tile_region: Cell::new(Default::default()), 135 decoration: Cell::new(Default::default()), 136 size_hints: Cell::new(None), 137 warp_pos: Cell::new(None), 138 parent: None, 139 children: RefCell::new(Vec::new()), 140 leader: None, 141 producer: Cell::new(None), 142 consumers: RefCell::new(Vec::new()), 143 focused: Cell::new(false), 144 mapped: Cell::new(false), 145 managed: Cell::new(true), 146 urgent: Cell::new(false), 147 floating: Cell::new(false), 148 fullscreen: Cell::new(false), 149 contained: Cell::new(false), 150 invincible: Cell::new(false), 151 sticky: Cell::new(false), 152 iconifyable: Cell::new(true), 153 iconified: Cell::new(false), 154 disowned: Cell::new(false), 155 consuming: Cell::new(false), 156 producing: Cell::new(true), 157 outside_state: Cell::new(OutsideState::Unfocused), 158 pid, 159 ppid, 160 last_focused: Cell::new(SystemTime::now()), 161 managed_since: SystemTime::now(), 162 expected_unmap_count: Cell::new(0), 163 } 164 } 165 166 #[inline] 167 pub fn zone(&self) -> ZoneId { 168 self.zone 169 } 170 171 #[inline] 172 pub fn windows(&self) -> (Window, Window) { 173 (self.window, self.frame) 174 } 175 176 #[inline] 177 pub fn window(&self) -> Window { 178 self.window 179 } 180 181 #[inline] 182 pub fn frame(&self) -> Window { 183 self.frame 184 } 185 186 #[inline] 187 pub fn set_name( 188 &self, 189 name: impl Into<String>, 190 ) { 191 self.name.replace(name.into()); 192 } 193 194 #[inline] 195 pub fn name(&self) -> String { 196 self.name.borrow().to_owned() 197 } 198 199 #[inline] 200 pub fn name_matches( 201 &self, 202 match_method: MatchMethod<&'static str>, 203 ) -> bool { 204 match match_method { 205 MatchMethod::Equals(comp) => &*self.name.borrow() == comp, 206 MatchMethod::Contains(comp) => (&*self.name.borrow()).contains(comp), 207 } 208 } 209 210 #[inline] 211 pub fn set_class( 212 &self, 213 class: impl Into<String>, 214 ) { 215 self.class.replace(class.into()); 216 } 217 218 #[inline] 219 pub fn class(&self) -> String { 220 self.class.borrow().to_owned() 221 } 222 223 #[inline] 224 pub fn class_matches( 225 &self, 226 match_method: MatchMethod<&'static str>, 227 ) -> bool { 228 match match_method { 229 MatchMethod::Equals(comp) => &*self.class.borrow() == comp, 230 MatchMethod::Contains(comp) => (&*self.class.borrow()).contains(comp), 231 } 232 } 233 234 #[inline] 235 pub fn set_instance( 236 &self, 237 instance: impl Into<String>, 238 ) { 239 self.instance.replace(instance.into()); 240 } 241 242 #[inline] 243 pub fn instance(&self) -> String { 244 self.instance.borrow().to_owned() 245 } 246 247 #[inline] 248 pub fn instance_matches( 249 &self, 250 match_method: MatchMethod<&'static str>, 251 ) -> bool { 252 match match_method { 253 MatchMethod::Equals(comp) => &*self.instance.borrow() == comp, 254 MatchMethod::Contains(comp) => (&*self.instance.borrow()).contains(comp), 255 } 256 } 257 258 #[inline] 259 pub fn set_context( 260 &self, 261 context: usize, 262 ) { 263 self.context.set(context); 264 } 265 266 #[inline] 267 pub fn context(&self) -> usize { 268 self.context.get() 269 } 270 271 #[inline] 272 pub fn set_workspace( 273 &self, 274 workspace: usize, 275 ) { 276 self.workspace.set(workspace); 277 } 278 279 #[inline] 280 pub fn workspace(&self) -> usize { 281 self.workspace.get() 282 } 283 284 #[inline] 285 pub fn window_type(&self) -> WindowType { 286 self.window_type 287 } 288 289 #[inline] 290 fn set_active_region( 291 &self, 292 active_region: Region, 293 ) { 294 self.set_inner_region(active_region); 295 self.previous_region 296 .set(self.active_region.replace(active_region)); 297 } 298 299 #[inline] 300 pub fn active_region(&self) -> Region { 301 self.active_region.get() 302 } 303 304 #[inline] 305 pub fn previous_region(&self) -> Region { 306 self.previous_region.get() 307 } 308 309 #[inline] 310 fn set_inner_region( 311 &self, 312 active_region: Region, 313 ) { 314 self.inner_region 315 .set(if let Some(frame) = self.decoration.get().frame { 316 let mut inner_region = active_region - frame.extents; 317 318 inner_region.pos.x = frame.extents.left; 319 inner_region.pos.y = frame.extents.top; 320 321 inner_region.dim.w = active_region.dim.w - frame.extents.left - frame.extents.right; 322 inner_region.dim.h = active_region.dim.h - frame.extents.top - frame.extents.bottom; 323 324 inner_region 325 } else { 326 let mut inner_region = active_region; 327 328 inner_region.pos.x = 0; 329 inner_region.pos.y = 0; 330 331 inner_region 332 }); 333 } 334 335 #[inline] 336 pub fn set_region( 337 &self, 338 region: PlacementClass<Region>, 339 ) { 340 match region { 341 PlacementClass::Free(region) => { 342 self.free_region.set(region); 343 self.set_active_region(region); 344 }, 345 PlacementClass::Tile(region) => { 346 self.tile_region.set(region); 347 self.set_active_region(region); 348 }, 349 } 350 } 351 352 #[inline] 353 pub fn free_region(&self) -> Region { 354 self.free_region.get() 355 } 356 357 #[inline] 358 pub fn tile_region(&self) -> Region { 359 self.tile_region.get() 360 } 361 362 #[inline] 363 pub fn inner_region(&self) -> Region { 364 self.inner_region.get() 365 } 366 367 #[inline] 368 pub fn set_decoration( 369 &self, 370 decoration: Decoration, 371 ) { 372 self.decoration.set(decoration); 373 } 374 375 #[inline] 376 pub fn decoration(&self) -> Decoration { 377 self.decoration.get().to_owned() 378 } 379 380 #[inline(always)] 381 pub fn decoration_colors(&self) -> (Option<(u32, Color)>, Option<Color>) { 382 let outside_state = self.outside_state(); 383 let decoration = self.decoration.get(); 384 385 match outside_state { 386 OutsideState::Focused => ( 387 decoration 388 .border 389 .map(|border| (border.width, border.colors.focused)), 390 decoration.frame.map(|frame| frame.colors.focused), 391 ), 392 OutsideState::FocusedDisowned => ( 393 decoration 394 .border 395 .map(|border| (border.width, border.colors.fdisowned)), 396 decoration.frame.map(|frame| frame.colors.fdisowned), 397 ), 398 OutsideState::FocusedSticky => ( 399 decoration 400 .border 401 .map(|border| (border.width, border.colors.fsticky)), 402 decoration.frame.map(|frame| frame.colors.fsticky), 403 ), 404 OutsideState::Unfocused => ( 405 decoration 406 .border 407 .map(|border| (border.width, border.colors.unfocused)), 408 decoration.frame.map(|frame| frame.colors.unfocused), 409 ), 410 OutsideState::UnfocusedDisowned => ( 411 decoration 412 .border 413 .map(|border| (border.width, border.colors.udisowned)), 414 decoration.frame.map(|frame| frame.colors.udisowned), 415 ), 416 OutsideState::UnfocusedSticky => ( 417 decoration 418 .border 419 .map(|border| (border.width, border.colors.usticky)), 420 decoration.frame.map(|frame| frame.colors.usticky), 421 ), 422 OutsideState::Urgent => ( 423 decoration 424 .border 425 .map(|border| (border.width, border.colors.urgent)), 426 decoration.frame.map(|frame| frame.colors.urgent), 427 ), 428 } 429 } 430 431 #[inline] 432 pub fn frame_extents(&self) -> Extents { 433 Extents { 434 left: 0, 435 right: 0, 436 top: 0, 437 bottom: 0, 438 } + self.decoration.get().to_owned() 439 } 440 441 #[inline] 442 pub fn set_size_hints( 443 &self, 444 size_hints: Option<SizeHints>, 445 ) { 446 self.size_hints.set(size_hints); 447 } 448 449 #[inline] 450 pub fn size_hints(&self) -> Option<SizeHints> { 451 self.size_hints.get() 452 } 453 454 #[inline] 455 pub fn set_warp_pos( 456 &self, 457 pointer_pos: Pos, 458 ) { 459 self.warp_pos.set(Some(pointer_pos)); 460 } 461 462 #[inline] 463 pub fn unset_warp_pos(&self) { 464 self.warp_pos.set(None); 465 } 466 467 #[inline] 468 pub fn warp_pos(&self) -> Option<Pos> { 469 self.warp_pos.get().to_owned() 470 } 471 472 #[inline] 473 pub fn set_parent( 474 &mut self, 475 parent: Window, 476 ) { 477 self.parent = Some(parent); 478 } 479 480 #[inline] 481 pub fn parent(&self) -> Option<Window> { 482 self.parent 483 } 484 485 #[inline] 486 pub fn add_child( 487 &self, 488 child: Window, 489 ) { 490 self.children.borrow_mut().push(child); 491 } 492 493 #[inline] 494 pub fn remove_child( 495 &self, 496 child: Window, 497 ) { 498 let mut children = self.children.borrow_mut(); 499 if let Some(index) = children.iter().rposition(|&c| c == child) { 500 children.remove(index); 501 } 502 } 503 504 #[inline] 505 pub fn set_leader( 506 &mut self, 507 leader: Window, 508 ) { 509 self.leader = Some(leader); 510 } 511 512 #[inline] 513 pub fn leader(&self) -> Option<Window> { 514 self.leader 515 } 516 517 #[inline] 518 pub fn set_producer( 519 &self, 520 producer: Window, 521 ) { 522 self.producer.set(Some(producer)); 523 } 524 525 #[inline] 526 pub fn unset_producer(&self) { 527 self.producer.set(None); 528 } 529 530 #[inline] 531 pub fn producer(&self) -> Option<Window> { 532 self.producer.get() 533 } 534 535 #[inline] 536 pub fn add_consumer( 537 &self, 538 consumer: Window, 539 ) { 540 self.consumers.borrow_mut().push(consumer); 541 } 542 543 #[inline] 544 pub fn remove_consumer( 545 &self, 546 consumer: Window, 547 ) { 548 let mut consumers = self.consumers.borrow_mut(); 549 if let Some(index) = consumers.iter().rposition(|&c| c == consumer) { 550 consumers.remove(index); 551 } 552 } 553 554 #[inline] 555 pub fn consumer_len(&self) -> usize { 556 self.consumers.borrow().len() 557 } 558 559 #[inline] 560 pub fn is_consuming(&self) -> bool { 561 self.producer.get().is_some() 562 } 563 564 #[inline] 565 pub fn set_focused( 566 &self, 567 toggle: Toggle, 568 ) { 569 if Toggle::from(self.focused.get()) != toggle { 570 self.focused.set(toggle.eval(self.focused.get())); 571 self.outside_state.set(!self.outside_state.get()); 572 } 573 } 574 575 #[inline] 576 pub fn is_focused(&self) -> bool { 577 self.focused.get() 578 } 579 580 #[inline] 581 pub fn set_mapped( 582 &self, 583 toggle: Toggle, 584 ) { 585 self.mapped.set(toggle.eval(self.mapped.get())); 586 } 587 588 #[inline] 589 pub fn is_mapped(&self) -> bool { 590 self.mapped.get() 591 } 592 593 #[inline] 594 pub fn set_managed( 595 &self, 596 toggle: Toggle, 597 ) { 598 self.managed.set(toggle.eval(self.managed.get())); 599 } 600 601 #[inline] 602 pub fn is_managed(&self) -> bool { 603 self.managed.get() 604 } 605 606 #[inline] 607 pub fn set_urgent( 608 &self, 609 toggle: Toggle, 610 ) { 611 let urgent = toggle.eval(self.urgent.get()); 612 self.urgent.set(urgent); 613 614 if urgent { 615 self.outside_state.set(OutsideState::Urgent); 616 } 617 } 618 619 #[inline] 620 pub fn is_urgent(&self) -> bool { 621 self.urgent.get() 622 } 623 624 #[inline] 625 pub fn is_free(&self) -> bool { 626 self.floating.get() && (!self.fullscreen.get() || self.contained.get()) 627 || self.disowned.get() 628 || !self.managed.get() 629 } 630 631 #[inline] 632 pub fn set_floating( 633 &self, 634 toggle: Toggle, 635 ) { 636 self.floating.set(toggle.eval(self.floating.get())); 637 } 638 639 #[inline] 640 pub fn is_floating(&self) -> bool { 641 self.floating.get() 642 } 643 644 #[inline] 645 pub fn set_fullscreen( 646 &self, 647 toggle: Toggle, 648 ) { 649 self.fullscreen.set(toggle.eval(self.fullscreen.get())); 650 } 651 652 #[inline] 653 pub fn is_fullscreen(&self) -> bool { 654 self.fullscreen.get() 655 } 656 657 #[inline] 658 pub fn set_contained( 659 &self, 660 toggle: Toggle, 661 ) { 662 self.contained.set(toggle.eval(self.contained.get())); 663 } 664 665 #[inline] 666 pub fn is_contained(&self) -> bool { 667 self.contained.get() 668 } 669 670 #[inline] 671 pub fn set_invincible( 672 &self, 673 toggle: Toggle, 674 ) { 675 self.invincible.set(toggle.eval(self.invincible.get())); 676 } 677 678 #[inline] 679 pub fn is_invincible(&self) -> bool { 680 self.invincible.get() 681 } 682 683 #[inline] 684 pub fn set_iconifyable( 685 &self, 686 toggle: Toggle, 687 ) { 688 self.iconifyable.set(toggle.eval(self.iconifyable.get())); 689 } 690 691 #[inline] 692 pub fn is_iconifyable(&self) -> bool { 693 self.iconifyable.get() 694 } 695 696 #[inline] 697 pub fn set_producing( 698 &self, 699 toggle: Toggle, 700 ) { 701 self.producing.set(toggle.eval(self.producing.get())); 702 } 703 704 #[inline] 705 pub fn is_producing(&self) -> bool { 706 self.producing.get() 707 } 708 709 #[inline] 710 pub fn set_iconified( 711 &self, 712 toggle: Toggle, 713 ) { 714 self.iconified.set(toggle.eval(self.iconified.get())); 715 } 716 717 #[inline] 718 pub fn is_iconified(&self) -> bool { 719 self.iconified.get() 720 } 721 722 #[inline] 723 pub fn set_sticky( 724 &self, 725 toggle: Toggle, 726 ) { 727 let sticky = toggle.eval(self.sticky.get()); 728 self.sticky.set(sticky); 729 730 self.outside_state.set(match self.outside_state.get() { 731 OutsideState::Focused if sticky => OutsideState::FocusedSticky, 732 OutsideState::Unfocused if sticky => OutsideState::UnfocusedSticky, 733 OutsideState::FocusedSticky if !sticky => OutsideState::Focused, 734 OutsideState::UnfocusedSticky if !sticky => OutsideState::Unfocused, 735 _ => return, 736 }); 737 } 738 739 #[inline] 740 pub fn is_sticky(&self) -> bool { 741 self.sticky.get() 742 } 743 744 #[inline] 745 pub fn set_disowned( 746 &self, 747 toggle: Toggle, 748 ) { 749 let disowned = toggle.eval(self.disowned.get()); 750 self.disowned.set(disowned); 751 752 self.outside_state.set(match self.outside_state.get() { 753 OutsideState::Focused if disowned => OutsideState::FocusedDisowned, 754 OutsideState::Unfocused if disowned => OutsideState::UnfocusedDisowned, 755 OutsideState::FocusedDisowned if !disowned => OutsideState::Focused, 756 OutsideState::UnfocusedDisowned if !disowned => OutsideState::Unfocused, 757 _ => return, 758 }); 759 } 760 761 #[inline] 762 pub fn is_disowned(&self) -> bool { 763 self.disowned.get() 764 } 765 766 #[inline] 767 pub fn outside_state(&self) -> OutsideState { 768 if self.urgent.get() { 769 OutsideState::Urgent 770 } else { 771 self.outside_state.get() 772 } 773 } 774 775 #[inline] 776 pub fn pid(&self) -> Option<Pid> { 777 self.pid 778 } 779 780 #[inline] 781 pub fn ppid(&self) -> Option<Pid> { 782 self.ppid 783 } 784 785 #[inline] 786 pub fn last_focused(&self) -> SystemTime { 787 self.last_focused.get() 788 } 789 790 #[inline] 791 pub fn managed_since(&self) -> SystemTime { 792 self.managed_since 793 } 794 795 #[inline] 796 pub fn expect_unmap(&self) { 797 self.expected_unmap_count 798 .set(self.expected_unmap_count.get() + 1); 799 } 800 801 #[inline] 802 pub fn consume_unmap_if_expecting(&self) -> bool { 803 let expected_unmap_count = self.expected_unmap_count.get(); 804 let expecting = expected_unmap_count > 0; 805 806 if expecting { 807 self.expected_unmap_count.set(expected_unmap_count - 1); 808 } 809 810 expecting 811 } 812 813 #[inline] 814 pub fn is_expecting_unmap(&self) -> bool { 815 self.expected_unmap_count.get() > 0 816 } 817 } 818 819 impl PartialEq for Client { 820 fn eq( 821 &self, 822 other: &Self, 823 ) -> bool { 824 self.window == other.window 825 } 826 } 827 828 pub struct Hex32(pub u32); 829 830 impl std::fmt::Debug for Hex32 { 831 fn fmt( 832 &self, 833 f: &mut std::fmt::Formatter<'_>, 834 ) -> std::fmt::Result { 835 write!(f, "{:#0x}", &self.0) 836 } 837 } 838 839 impl std::fmt::Debug for Client { 840 fn fmt( 841 &self, 842 f: &mut std::fmt::Formatter<'_>, 843 ) -> std::fmt::Result { 844 f.debug_struct("Client") 845 .field("window", &Hex32(self.window)) 846 .field("frame", &Hex32(self.frame)) 847 .field("name", &self.name) 848 .field("class", &self.class) 849 .field("instance", &self.instance) 850 .field("context", &self.context) 851 .field("workspace", &self.workspace) 852 .field("window_type", &self.window_type) 853 .field("active_region", &self.active_region) 854 .field("previous_region", &self.previous_region) 855 .field("inner_region", &self.inner_region) 856 .field("free_region", &self.free_region) 857 .field("tile_region", &self.tile_region) 858 .field("decoration", &self.decoration) 859 .field("size_hints", &self.size_hints) 860 .field("warp_pos", &self.warp_pos) 861 .field("parent", &self.parent.map(Hex32)) 862 .field( 863 "children", 864 &self 865 .children 866 .borrow() 867 .iter() 868 .map(|&child| Hex32(child)) 869 .collect::<Vec<Hex32>>(), 870 ) 871 .field("leader", &self.leader) 872 .field("producer", &self.producer) 873 .field("consumers", &self.consumers) 874 .field("focused", &self.focused) 875 .field("mapped", &self.mapped) 876 .field("managed", &self.managed) 877 .field("contained", &self.contained) 878 .field("floating", &self.floating) 879 .field("fullscreen", &self.fullscreen) 880 .field("iconified", &self.iconified) 881 .field("disowned", &self.disowned) 882 .field("sticky", &self.sticky) 883 .field("invincible", &self.invincible) 884 .field("urgent", &self.urgent) 885 .field("consuming", &self.consuming) 886 .field("pid", &self.pid) 887 .field("ppid", &self.ppid) 888 .field("last_focused", &self.last_focused) 889 .field("managed_since", &self.managed_since) 890 .field("expected_unmap_count", &self.expected_unmap_count) 891 .finish() 892 } 893 }