xconnection.rs (99963B)
1 use crate::connection::Connection; 2 use crate::connection::Pid; 3 use crate::event::Event; 4 use crate::event::PropertyKind; 5 use crate::event::StackMode; 6 use crate::event::ToggleAction; 7 use crate::geometry::Corner; 8 use crate::geometry::Dim; 9 use crate::geometry::Edge; 10 use crate::geometry::Extents; 11 use crate::geometry::Pos; 12 use crate::geometry::Ratio; 13 use crate::geometry::Region; 14 use crate::geometry::Strut; 15 use crate::hints::Hints; 16 use crate::hints::SizeHints; 17 use crate::input::Button; 18 use crate::input::Modifier; 19 use crate::input::Grip; 20 use crate::input::Key; 21 use crate::input::KeyEvent; 22 use crate::input::MouseEventKind; 23 use crate::input::MouseInputTarget; 24 use crate::input::KeyInput; 25 use crate::input::MouseEvent; 26 use crate::input::MouseInput; 27 use crate::screen::Screen; 28 use crate::window::IcccmWindowState; 29 use crate::window::Window; 30 use crate::window::WindowState; 31 use crate::window::WindowType; 32 use crate::Result; 33 34 use std::cell::Cell; 35 use std::cell::RefCell; 36 use std::collections::HashMap; 37 use std::collections::HashSet; 38 use std::convert::TryFrom; 39 use std::str::FromStr; 40 41 use x11rb::connection; 42 use x11rb::cursor::Handle as CursorHandle; 43 use x11rb::errors::ReplyError; 44 use x11rb::properties; 45 use x11rb::protocol; 46 use x11rb::protocol::randr; 47 use x11rb::protocol::xproto; 48 use x11rb::protocol::xproto::ConnectionExt; 49 use x11rb::protocol::xproto::EventMask; 50 use x11rb::protocol::xproto::ModMask; 51 use x11rb::protocol::xproto::CLIENT_MESSAGE_EVENT; 52 use x11rb::protocol::ErrorKind; 53 use x11rb::protocol::Event as XEvent; 54 use x11rb::resource_manager::Database; 55 use x11rb::wrapper::ConnectionExt as _; 56 57 use anyhow::anyhow; 58 use strum::*; 59 60 type Atom = u32; 61 62 x11rb::atom_manager! { 63 pub Atoms: AtomsCookie { 64 Any, 65 ATOM, 66 CARDINAL, 67 WINDOW, 68 STRING, 69 UTF8_STRING, 70 71 // ICCCM client properties 72 WM_NAME, 73 WM_CLASS, 74 WM_CLIENT_MACHINE, 75 WM_PROTOCOLS, 76 WM_NORMAL_HINTS, 77 WM_DELETE_WINDOW, 78 WM_WINDOW_ROLE, 79 WM_CLIENT_LEADER, 80 WM_TRANSIENT_FOR, 81 WM_TAKE_FOCUS, 82 83 // ICCCM window manager properties 84 WM_STATE, 85 WM_ICON_SIZE, 86 87 // EWMH root properties 88 _NET_SUPPORTED, 89 _NET_CLIENT_LIST, 90 _NET_CLIENT_LIST_STACKING, 91 _NET_NUMBER_OF_DESKTOPS, 92 _NET_DESKTOP_GEOMETRY, 93 _NET_DESKTOP_VIEWPORT, 94 _NET_CURRENT_DESKTOP, 95 _NET_DESKTOP_NAMES, 96 _NET_ACTIVE_WINDOW, 97 _NET_WORKAREA, 98 _NET_SUPPORTING_WM_CHECK, 99 _NET_VIRTUAL_ROOTS, 100 _NET_DESKTOP_LAYOUT, 101 _NET_SHOWING_DESKTOP, 102 103 // EWMH root messages 104 _NET_CLOSE_WINDOW, 105 _NET_MOVERESIZE_WINDOW, 106 _NET_WM_MOVERESIZE, 107 _NET_REQUEST_FRAME_EXTENTS, 108 109 // EWMH application properties 110 _NET_WM_NAME, 111 _NET_WM_VISIBLE_NAME, 112 _NET_WM_ICON_NAME, 113 _NET_WM_VISIBLE_ICON_NAME, 114 _NET_WM_DESKTOP, 115 _NET_WM_WINDOW_TYPE, 116 _NET_WM_STATE, 117 _NET_WM_ALLOWED_ACTIONS, 118 _NET_WM_STRUT, 119 _NET_WM_STRUT_PARTIAL, 120 _NET_WM_ICON_GEOMETRY, 121 _NET_WM_ICON, 122 _NET_WM_PID, 123 _NET_WM_HANDLED_ICONS, 124 _NET_WM_USER_TIME, 125 _NET_WM_USER_TIME_WINDOW, 126 _NET_FRAME_EXTENTS, 127 _NET_WM_OPAQUE_REGION, 128 _NET_WM_BYPASS_COMPOSITOR, 129 130 // EWMH window states 131 _NET_WM_STATE_MODAL, 132 _NET_WM_STATE_STICKY, 133 _NET_WM_STATE_MAXIMIZED_VERT, 134 _NET_WM_STATE_MAXIMIZED_HORZ, 135 _NET_WM_STATE_SHADED, 136 _NET_WM_STATE_SKIP_TASKBAR, 137 _NET_WM_STATE_SKIP_PAGER, 138 _NET_WM_STATE_HIDDEN, 139 _NET_WM_STATE_FULLSCREEN, 140 _NET_WM_STATE_ABOVE, 141 _NET_WM_STATE_BELOW, 142 _NET_WM_STATE_DEMANDS_ATTENTION, 143 _NET_WM_STATE_FOCUSED, 144 145 // EWMH window types 146 _NET_WM_WINDOW_TYPE_DESKTOP, 147 _NET_WM_WINDOW_TYPE_DOCK, 148 _NET_WM_WINDOW_TYPE_TOOLBAR, 149 _NET_WM_WINDOW_TYPE_MENU, 150 _NET_WM_WINDOW_TYPE_UTILITY, 151 _NET_WM_WINDOW_TYPE_SPLASH, 152 _NET_WM_WINDOW_TYPE_DIALOG, 153 _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, 154 _NET_WM_WINDOW_TYPE_POPUP_MENU, 155 _NET_WM_WINDOW_TYPE_TOOLTIP, 156 _NET_WM_WINDOW_TYPE_NOTIFICATION, 157 _NET_WM_WINDOW_TYPE_COMBO, 158 _NET_WM_WINDOW_TYPE_DND, 159 _NET_WM_WINDOW_TYPE_NORMAL, 160 161 // EWMH protocols 162 _NET_WM_PING, 163 _NET_WM_SYNC_REQUEST, 164 _NET_WM_FULLSCREEN_MONITORS, 165 166 // System tray protocols 167 _NET_SYSTEM_TRAY_ORIENTATION, 168 _NET_SYSTEM_TRAY_OPCODE, 169 _NET_SYSTEM_TRAY_ORIENTATION_HORZ, 170 _NET_SYSTEM_TRAY_S0, 171 _XEMBED, 172 _XEMBED_INFO, 173 } 174 } 175 176 pub struct XConnection<'conn, Conn: connection::Connection> { 177 conn: &'conn Conn, 178 atoms: Atoms, 179 type_map: HashMap<Atom, WindowType>, 180 state_map: HashMap<Atom, WindowState>, 181 screen: xproto::Screen, 182 check_window: Window, 183 background_gc: xproto::Gcontext, 184 database: Option<Database>, 185 confined_to: Cell<Option<Window>>, 186 keys: RefCell<HashMap<u8, Key>>, 187 keycodes: RefCell<HashMap<Key, u8>>, 188 root_event_mask: EventMask, 189 window_event_mask: EventMask, 190 frame_event_mask: EventMask, 191 mouse_event_mask: EventMask, 192 regrab_event_mask: EventMask, 193 } 194 195 impl<'conn, Conn: connection::Connection> XConnection<'conn, Conn> { 196 pub fn new( 197 conn: &'conn Conn, 198 screen_num: usize, 199 ) -> Result<Self> { 200 let screen = conn.setup().roots[screen_num].clone(); 201 let root = screen.root; 202 203 let aux = xproto::ChangeWindowAttributesAux::default() 204 .event_mask(EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY); 205 206 let res = conn.change_window_attributes(screen.root, &aux)?.check(); 207 if let Err(ReplyError::X11Error(err)) = res { 208 if err.error_kind == ErrorKind::Access { 209 return Err(anyhow!("another window manager is already running")); 210 } else { 211 return Err(anyhow!("unable to set up window manager")); 212 } 213 } 214 215 let atoms = Atoms::new(conn)?.reply()?; 216 let check_window = conn.generate_id()?; 217 218 let type_map: HashMap<Atom, WindowType> = map!( 219 atoms._NET_WM_WINDOW_TYPE_DESKTOP => WindowType::Desktop, 220 atoms._NET_WM_WINDOW_TYPE_DOCK => WindowType::Dock, 221 atoms._NET_WM_WINDOW_TYPE_TOOLBAR => WindowType::Toolbar, 222 atoms._NET_WM_WINDOW_TYPE_MENU => WindowType::Menu, 223 atoms._NET_WM_WINDOW_TYPE_UTILITY => WindowType::Utility, 224 atoms._NET_WM_WINDOW_TYPE_SPLASH => WindowType::Splash, 225 atoms._NET_WM_WINDOW_TYPE_DIALOG => WindowType::Dialog, 226 atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU => WindowType::DropdownMenu, 227 atoms._NET_WM_WINDOW_TYPE_POPUP_MENU => WindowType::PopupMenu, 228 atoms._NET_WM_WINDOW_TYPE_TOOLTIP => WindowType::Tooltip, 229 atoms._NET_WM_WINDOW_TYPE_NOTIFICATION => WindowType::Notification, 230 atoms._NET_WM_WINDOW_TYPE_COMBO => WindowType::Combo, 231 atoms._NET_WM_WINDOW_TYPE_DND => WindowType::Dnd, 232 atoms._NET_WM_WINDOW_TYPE_NORMAL => WindowType::Normal, 233 ); 234 235 let state_map: HashMap<Atom, WindowState> = map!( 236 atoms._NET_WM_STATE_MODAL => WindowState::Modal, 237 atoms._NET_WM_STATE_STICKY => WindowState::Sticky, 238 atoms._NET_WM_STATE_MAXIMIZED_VERT => WindowState::MaximizedVert, 239 atoms._NET_WM_STATE_MAXIMIZED_HORZ => WindowState::MaximizedHorz, 240 atoms._NET_WM_STATE_SHADED => WindowState::Shaded, 241 atoms._NET_WM_STATE_SKIP_TASKBAR => WindowState::SkipTaskbar, 242 atoms._NET_WM_STATE_SKIP_PAGER => WindowState::SkipPager, 243 atoms._NET_WM_STATE_HIDDEN => WindowState::Hidden, 244 atoms._NET_WM_STATE_FULLSCREEN => WindowState::Fullscreen, 245 atoms._NET_WM_STATE_ABOVE => WindowState::Above, 246 atoms._NET_WM_STATE_BELOW => WindowState::Below, 247 atoms._NET_WM_STATE_DEMANDS_ATTENTION => WindowState::DemandsAttention, 248 ); 249 250 conn.create_window( 251 x11rb::COPY_DEPTH_FROM_PARENT, 252 check_window, 253 root, 254 -1, 255 -1, 256 1, 257 1, 258 0, 259 xproto::WindowClass::INPUT_ONLY, 260 x11rb::COPY_FROM_PARENT, 261 &xproto::CreateWindowAux::default().override_redirect(1), 262 )?; 263 264 conn.map_window(check_window)?; 265 conn.configure_window( 266 check_window, 267 &xproto::ConfigureWindowAux::default().stack_mode(xproto::StackMode::BELOW), 268 )?; 269 270 randr::select_input( 271 conn, 272 check_window, 273 randr::NotifyMask::OUTPUT_CHANGE 274 | randr::NotifyMask::CRTC_CHANGE 275 | randr::NotifyMask::SCREEN_CHANGE, 276 )?; 277 278 let background_gc = conn.generate_id()?; 279 conn.create_gc(background_gc, screen.root, &xproto::CreateGCAux::default())?; 280 281 let database = Database::new_from_default(conn).ok(); 282 if let Some(ref database) = database { 283 drop( 284 CursorHandle::new(conn, screen_num, &database).map(|cookie| { 285 cookie.reply().map(|reply| { 286 let aux = xproto::ChangeWindowAttributesAux::default() 287 .cursor(reply.load_cursor(conn, "left_ptr").ok()); 288 289 drop(conn.change_window_attributes(screen.root, &aux)); 290 }) 291 }), 292 ); 293 } 294 295 let keys = RefCell::new(HashMap::new()); 296 let keycodes = RefCell::new(HashMap::new()); 297 298 let root_event_mask: EventMask = EventMask::PROPERTY_CHANGE 299 | EventMask::SUBSTRUCTURE_REDIRECT 300 | EventMask::STRUCTURE_NOTIFY 301 | EventMask::BUTTON_PRESS 302 | EventMask::POINTER_MOTION 303 | EventMask::FOCUS_CHANGE; 304 305 let window_event_mask: EventMask = 306 EventMask::PROPERTY_CHANGE | EventMask::STRUCTURE_NOTIFY | EventMask::FOCUS_CHANGE; 307 308 let frame_event_mask: EventMask = EventMask::STRUCTURE_NOTIFY 309 | EventMask::SUBSTRUCTURE_REDIRECT 310 | EventMask::SUBSTRUCTURE_NOTIFY 311 | EventMask::BUTTON_PRESS 312 | EventMask::BUTTON_RELEASE 313 | EventMask::POINTER_MOTION; 314 315 let mouse_event_mask: EventMask = 316 EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE | EventMask::BUTTON_MOTION; 317 318 let regrab_event_mask: EventMask = EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE; 319 320 Self::init(Self { 321 conn, 322 atoms, 323 type_map, 324 state_map, 325 screen, 326 check_window, 327 background_gc, 328 database, 329 confined_to: Cell::new(None), 330 keys, 331 keycodes, 332 root_event_mask, 333 window_event_mask, 334 frame_event_mask, 335 mouse_event_mask, 336 regrab_event_mask, 337 }) 338 } 339 340 #[inline] 341 fn init(connection: Self) -> Result<Self> { 342 Ok(connection) 343 } 344 345 pub fn window_is_any_of_types( 346 &self, 347 window: Window, 348 types: &[Atom], 349 ) -> bool { 350 self.conn 351 .get_property( 352 false, 353 window, 354 self.atoms._NET_WM_WINDOW_TYPE, 355 self.atoms.ATOM, 356 0, 357 std::u32::MAX, 358 ) 359 .map_or(false, |cookie| { 360 cookie.reply().map_or(false, |reply| { 361 reply.value32().map_or(false, |mut window_types| { 362 window_types.any(|type_| types.contains(&type_)) 363 }) 364 }) 365 }) 366 } 367 368 pub fn window_is_any_of_states( 369 &self, 370 window: Window, 371 states: &[Atom], 372 ) -> bool { 373 self.conn 374 .get_property( 375 false, 376 window, 377 self.atoms._NET_WM_STATE, 378 self.atoms.ATOM, 379 0, 380 std::u32::MAX, 381 ) 382 .map_or(false, |cookie| { 383 cookie.reply().map_or(false, |reply| { 384 reply.value32().map_or(false, |mut window_states| { 385 window_states.any(|state| states.contains(&state)) 386 }) 387 }) 388 }) 389 } 390 391 pub fn window_has_any_of_protocols( 392 &self, 393 window: Window, 394 protocols: &[Atom], 395 ) -> bool { 396 self.conn 397 .get_property( 398 false, 399 window, 400 self.atoms.WM_PROTOCOLS, 401 self.atoms.ATOM, 402 0, 403 std::u32::MAX, 404 ) 405 .map_or(false, |cookie| { 406 cookie.reply().map_or(false, |reply| { 407 reply.value32().map_or(false, |mut window_protocols| { 408 window_protocols.any(|protocol| protocols.contains(&protocol)) 409 }) 410 }) 411 }) 412 } 413 414 #[inline] 415 fn send_client_message( 416 &self, 417 window: Window, 418 atom: Atom, 419 type_: Atom, 420 ) -> Result<()> { 421 let data = [atom, x11rb::CURRENT_TIME, 0, 0, 0]; 422 423 let event = xproto::ClientMessageEvent { 424 response_type: CLIENT_MESSAGE_EVENT, 425 format: 32, 426 sequence: 0, 427 window, 428 type_, 429 data: data.into(), 430 }; 431 432 self.conn 433 .send_event(false, window, EventMask::NO_EVENT, &event)?; 434 435 Ok(()) 436 } 437 438 #[inline] 439 fn send_protocol_client_message( 440 &self, 441 window: Window, 442 atom: Atom, 443 ) -> Result<()> { 444 self.send_client_message(window, atom, self.atoms.WM_PROTOCOLS) 445 } 446 447 #[inline] 448 fn get_window_state_from_atom( 449 &self, 450 atom: Atom, 451 ) -> Option<WindowState> { 452 self.state_map.get(&atom).copied() 453 } 454 455 #[inline] 456 fn get_atom_from_window_state( 457 &self, 458 state: WindowState, 459 ) -> Atom { 460 match state { 461 WindowState::Modal => self.atoms._NET_WM_STATE_MODAL, 462 WindowState::Sticky => self.atoms._NET_WM_STATE_STICKY, 463 WindowState::MaximizedVert => self.atoms._NET_WM_STATE_MAXIMIZED_VERT, 464 WindowState::MaximizedHorz => self.atoms._NET_WM_STATE_MAXIMIZED_HORZ, 465 WindowState::Shaded => self.atoms._NET_WM_STATE_SHADED, 466 WindowState::SkipTaskbar => self.atoms._NET_WM_STATE_SKIP_TASKBAR, 467 WindowState::SkipPager => self.atoms._NET_WM_STATE_SKIP_PAGER, 468 WindowState::Hidden => self.atoms._NET_WM_STATE_HIDDEN, 469 WindowState::Fullscreen => self.atoms._NET_WM_STATE_FULLSCREEN, 470 WindowState::Above => self.atoms._NET_WM_STATE_ABOVE, 471 WindowState::Below => self.atoms._NET_WM_STATE_BELOW, 472 WindowState::DemandsAttention => self.atoms._NET_WM_STATE_DEMANDS_ATTENTION, 473 } 474 } 475 476 #[inline] 477 fn get_window_type_from_atom( 478 &self, 479 atom: Atom, 480 ) -> Option<WindowType> { 481 self.type_map.get(&atom).copied() 482 } 483 484 #[inline] 485 fn get_atom_from_window_type( 486 &self, 487 type_: WindowType, 488 ) -> Atom { 489 match type_ { 490 WindowType::Desktop => self.atoms._NET_WM_WINDOW_TYPE_DESKTOP, 491 WindowType::Dock => self.atoms._NET_WM_WINDOW_TYPE_DOCK, 492 WindowType::Toolbar => self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, 493 WindowType::Menu => self.atoms._NET_WM_WINDOW_TYPE_MENU, 494 WindowType::Utility => self.atoms._NET_WM_WINDOW_TYPE_UTILITY, 495 WindowType::Splash => self.atoms._NET_WM_WINDOW_TYPE_SPLASH, 496 WindowType::Dialog => self.atoms._NET_WM_WINDOW_TYPE_DIALOG, 497 WindowType::DropdownMenu => self.atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU, 498 WindowType::PopupMenu => self.atoms._NET_WM_WINDOW_TYPE_POPUP_MENU, 499 WindowType::Tooltip => self.atoms._NET_WM_WINDOW_TYPE_TOOLTIP, 500 WindowType::Notification => self.atoms._NET_WM_WINDOW_TYPE_NOTIFICATION, 501 WindowType::Combo => self.atoms._NET_WM_WINDOW_TYPE_COMBO, 502 WindowType::Dnd => self.atoms._NET_WM_WINDOW_TYPE_DND, 503 WindowType::Normal => self.atoms._NET_WM_WINDOW_TYPE_NORMAL, 504 } 505 } 506 507 fn get_key( 508 &self, 509 keycode: u8, 510 ) -> Key { 511 if let Some(&key) = self.keys.borrow().get(&keycode) { 512 return key; 513 } 514 515 let key = match keycode { 516 9 => Key::Escape, 517 10 => Key::One, 518 11 => Key::Two, 519 12 => Key::Three, 520 13 => Key::Four, 521 14 => Key::Five, 522 15 => Key::Six, 523 16 => Key::Seven, 524 17 => Key::Eight, 525 18 => Key::Nine, 526 19 => Key::Zero, 527 20 => Key::Minus, 528 21 => Key::Equal, 529 22 => Key::Backspace, 530 23 => Key::Tab, 531 24 => Key::Q, 532 25 => Key::W, 533 26 => Key::E, 534 27 => Key::R, 535 28 => Key::T, 536 29 => Key::Y, 537 30 => Key::U, 538 31 => Key::I, 539 32 => Key::O, 540 33 => Key::P, 541 34 => Key::LeftBracket, 542 35 => Key::RightBracket, 543 36 => Key::Return, 544 37 => Key::Control, 545 38 => Key::A, 546 39 => Key::S, 547 40 => Key::D, 548 41 => Key::F, 549 42 => Key::G, 550 43 => Key::H, 551 44 => Key::J, 552 45 => Key::K, 553 46 => Key::L, 554 47 => Key::SemiColon, 555 48 => Key::Apostrophe, 556 49 => Key::Tilde, 557 50 => Key::Shift, 558 51 => Key::BackSlash, 559 52 => Key::Z, 560 53 => Key::X, 561 54 => Key::C, 562 55 => Key::V, 563 56 => Key::B, 564 57 => Key::N, 565 58 => Key::M, 566 59 => Key::Comma, 567 60 => Key::Period, 568 61 => Key::Slash, 569 62 => Key::RightShift, 570 63 => Key::Multiply, 571 64 => Key::Alt, 572 65 => Key::Space, 573 66 => Key::CapsLock, 574 67 => Key::F1, 575 68 => Key::F2, 576 69 => Key::F3, 577 70 => Key::F4, 578 71 => Key::F5, 579 72 => Key::F6, 580 73 => Key::F7, 581 74 => Key::F8, 582 75 => Key::F9, 583 76 => Key::F10, 584 77 => Key::Numlock, 585 78 => Key::ScrollLock, 586 79 => Key::NumPad7, 587 80 => Key::NumPad8, 588 81 => Key::NumPad9, 589 82 => Key::Subtract, 590 83 => Key::NumPad4, 591 84 => Key::NumPad5, 592 85 => Key::NumPad6, 593 86 => Key::Add, 594 87 => Key::NumPad1, 595 88 => Key::NumPad2, 596 89 => Key::NumPad3, 597 90 => Key::NumPad0, 598 94 => Key::Less, 599 95 => Key::F11, 600 96 => Key::F12, 601 105 => Key::RightContol, 602 106 => Key::Divide, 603 107 => Key::PrintScreen, 604 108 => Key::RightAlt, 605 110 => Key::Home, 606 111 => Key::Up, 607 112 => Key::PageUp, 608 113 => Key::Left, 609 114 => Key::Right, 610 115 => Key::End, 611 116 => Key::Down, 612 117 => Key::PageDown, 613 118 => Key::Insert, 614 119 => Key::Delete, 615 121 => Key::VolumeMute, 616 122 => Key::VolumeDown, 617 123 => Key::VolumeUp, 618 127 => Key::Pause, 619 128 => Key::LaunchAppA, 620 129 => Key::Decimal, 621 133 => Key::Super, 622 134 => Key::RightSuper, 623 135 => Key::Menu, 624 146 => Key::Help, 625 156 => Key::LaunchApp1, 626 157 => Key::LaunchApp2, 627 163 => Key::LaunchMail, 628 164 => Key::BrowserFavorites, 629 166 => Key::BrowserBack, 630 167 => Key::BrowserForward, 631 171 => Key::NextTrack, 632 172 => Key::PlayPause, 633 173 => Key::PreviousTrack, 634 174 => Key::StopMedia, 635 180 => Key::BrowserHome, 636 182 => Key::BrowserStop, 637 187 => Key::LeftParenthesis, 638 188 => Key::RightParenthesis, 639 192 => Key::LaunchApp5, 640 193 => Key::LaunchApp6, 641 194 => Key::LaunchApp7, 642 195 => Key::LaunchApp8, 643 196 => Key::LaunchApp9, 644 210 => Key::LaunchApp3, 645 211 => Key::LaunchApp4, 646 212 => Key::LaunchAppB, 647 225 => Key::BrowserSearch, 648 234 => Key::SelectMedia, 649 _ => return Key::Any, 650 }; 651 652 self.keys.borrow_mut().insert(keycode, key); 653 self.keycodes.borrow_mut().insert(key, keycode); 654 655 key 656 } 657 658 fn get_keycode( 659 &self, 660 key: Key, 661 ) -> u8 { 662 if let Some(&keycode) = self.keycodes.borrow().get(&key) { 663 return keycode; 664 } 665 666 let keycode = match key { 667 Key::Backspace => 22, 668 Key::Tab => 23, 669 Key::Return => 36, 670 Key::Shift => 50, 671 Key::Control => 37, 672 Key::Alt => 64, 673 Key::Super => 133, 674 Key::Menu => 135, 675 Key::Pause => 127, 676 Key::CapsLock => 66, 677 Key::Escape => 9, 678 Key::Space => 65, 679 Key::ExclamationMark => 10, 680 Key::QuotationMark => 48, 681 Key::QuestionMark => 61, 682 Key::NumberSign => 12, 683 Key::DollarSign => 13, 684 Key::PercentSign => 14, 685 Key::AtSign => 11, 686 Key::Ampersand => 16, 687 Key::Apostrophe => 48, 688 Key::LeftParenthesis => 187, 689 Key::RightParenthesis => 188, 690 Key::LeftBracket => 34, 691 Key::RightBracket => 35, 692 Key::LeftBrace => 34, 693 Key::RightBrace => 35, 694 Key::Underscore => 20, 695 Key::Grave => 49, 696 Key::Bar => 51, 697 Key::Tilde => 49, 698 Key::QuoteLeft => 49, 699 Key::Asterisk => 17, 700 Key::Plus => 21, 701 Key::Comma => 59, 702 Key::Minus => 20, 703 Key::Period => 60, 704 Key::Slash => 61, 705 Key::BackSlash => 51, 706 Key::Colon => 47, 707 Key::SemiColon => 47, 708 Key::Less => 94, 709 Key::Equal => 21, 710 Key::Greater => 60, 711 Key::PageUp => 112, 712 Key::PageDown => 117, 713 Key::End => 115, 714 Key::Home => 110, 715 Key::Left => 113, 716 Key::Up => 111, 717 Key::Right => 114, 718 Key::Down => 116, 719 Key::Print => 107, 720 Key::PrintScreen => 107, 721 Key::Insert => 118, 722 Key::Delete => 119, 723 Key::Help => 146, 724 Key::Zero => 19, 725 Key::One => 10, 726 Key::Two => 11, 727 Key::Three => 12, 728 Key::Four => 13, 729 Key::Five => 14, 730 Key::Six => 15, 731 Key::Seven => 16, 732 Key::Eight => 17, 733 Key::Nine => 18, 734 Key::A => 38, 735 Key::B => 56, 736 Key::C => 54, 737 Key::D => 40, 738 Key::E => 26, 739 Key::F => 41, 740 Key::G => 42, 741 Key::H => 43, 742 Key::I => 31, 743 Key::J => 44, 744 Key::K => 45, 745 Key::L => 46, 746 Key::M => 58, 747 Key::N => 57, 748 Key::O => 32, 749 Key::P => 33, 750 Key::Q => 24, 751 Key::R => 27, 752 Key::S => 39, 753 Key::T => 28, 754 Key::U => 30, 755 Key::V => 55, 756 Key::W => 25, 757 Key::X => 53, 758 Key::Y => 29, 759 Key::Z => 52, 760 Key::NumPad0 => 90, 761 Key::NumPad1 => 87, 762 Key::NumPad2 => 88, 763 Key::NumPad3 => 89, 764 Key::NumPad4 => 83, 765 Key::NumPad5 => 84, 766 Key::NumPad6 => 85, 767 Key::NumPad7 => 79, 768 Key::NumPad8 => 80, 769 Key::NumPad9 => 81, 770 Key::Multiply => 63, 771 Key::Add => 86, 772 Key::Subtract => 82, 773 Key::Decimal => 129, 774 Key::Divide => 106, 775 Key::F1 => 67, 776 Key::F2 => 68, 777 Key::F3 => 69, 778 Key::F4 => 70, 779 Key::F5 => 71, 780 Key::F6 => 72, 781 Key::F7 => 73, 782 Key::F8 => 74, 783 Key::F9 => 75, 784 Key::F10 => 76, 785 Key::F11 => 95, 786 Key::F12 => 96, 787 Key::Numlock => 77, 788 Key::ScrollLock => 78, 789 Key::LeftShift => 50, 790 Key::RightShift => 62, 791 Key::LeftControl => 37, 792 Key::RightContol => 105, 793 Key::LeftAlt => 64, 794 Key::RightAlt => 108, 795 Key::LeftSuper => 133, 796 Key::RightSuper => 134, 797 Key::BrowserBack => 166, 798 Key::BrowserForward => 167, 799 Key::BrowserStop => 182, 800 Key::BrowserSearch => 225, 801 Key::BrowserFavorites => 164, 802 Key::BrowserHome => 180, 803 Key::VolumeMute => 121, 804 Key::VolumeDown => 122, 805 Key::VolumeUp => 123, 806 Key::NextTrack => 171, 807 Key::PreviousTrack => 173, 808 Key::StopMedia => 174, 809 Key::PlayPause => 172, 810 Key::LaunchMail => 163, 811 Key::SelectMedia => 234, 812 Key::LaunchAppA => 128, 813 Key::LaunchAppB => 212, 814 Key::LaunchApp1 => 156, 815 Key::LaunchApp2 => 157, 816 Key::LaunchApp3 => 210, 817 Key::LaunchApp4 => 211, 818 Key::LaunchApp5 => 192, 819 Key::LaunchApp6 => 193, 820 Key::LaunchApp7 => 194, 821 Key::LaunchApp8 => 195, 822 Key::LaunchApp9 => 196, 823 _ => return 0, 824 }; 825 826 self.keys.borrow_mut().insert(keycode, key); 827 self.keycodes.borrow_mut().insert(key, keycode); 828 829 keycode 830 } 831 832 fn set_window_state_atom( 833 &self, 834 window: Window, 835 state_atom: Atom, 836 on: bool, 837 ) { 838 if on { 839 if self.window_is_any_of_states(window, &[state_atom]) { 840 return; 841 } 842 843 drop(self.conn.change_property32( 844 xproto::PropMode::APPEND, 845 window, 846 self.atoms._NET_WM_STATE, 847 xproto::AtomEnum::ATOM, 848 &[state_atom], 849 )); 850 } else { 851 let mut states = self 852 .conn 853 .get_property( 854 false, 855 window, 856 self.atoms._NET_WM_STATE, 857 self.atoms.ATOM, 858 0, 859 std::u32::MAX, 860 ) 861 .map_or(Vec::with_capacity(0), |cookie| { 862 cookie.reply().map_or(Vec::with_capacity(0), |reply| { 863 reply 864 .value32() 865 .map_or(Vec::with_capacity(0), |window_states| { 866 window_states.collect() 867 }) 868 }) 869 }); 870 871 states.retain(|&state| state != state_atom); 872 873 drop(self.conn.change_property32( 874 xproto::PropMode::REPLACE, 875 window, 876 self.atoms._NET_WM_STATE, 877 xproto::AtomEnum::ATOM, 878 &states, 879 )); 880 } 881 } 882 883 #[inline] 884 fn on_button_press( 885 &self, 886 event: &xproto::ButtonPressEvent, 887 ) -> Option<Event> { 888 let window = event.event; 889 let window = if window == self.screen.root || window == x11rb::NONE { 890 if event.child == x11rb::NONE { 891 None 892 } else { 893 Some(event.child) 894 } 895 } else { 896 Some(window) 897 }; 898 899 Some(Event::Mouse { 900 event: MouseEvent { 901 kind: MouseEventKind::Press, 902 input: MouseInput { 903 target: MouseInputTarget::Global, 904 button: { 905 if let Ok(button) = Button::try_from(event.detail) { 906 button 907 } else { 908 return None; 909 } 910 }, 911 modifiers: { 912 let mut modifiers = HashSet::new(); 913 914 if event.state & u16::from(xproto::ModMask::CONTROL) > 0 { 915 modifiers.insert(Modifier::Ctrl); 916 } 917 918 if event.state & u16::from(xproto::ModMask::SHIFT) > 0 { 919 modifiers.insert(Modifier::Shift); 920 } 921 922 if event.state & u16::from(xproto::ModMask::M1) > 0 { 923 modifiers.insert(Modifier::Alt); 924 } 925 926 if event.state & u16::from(xproto::ModMask::M4) > 0 { 927 modifiers.insert(Modifier::Super); 928 } 929 930 modifiers 931 }, 932 }, 933 window: { 934 if window == Some(self.screen.root) { 935 None 936 } else { 937 window 938 } 939 }, 940 root_rpos: Pos { 941 x: event.root_x as i32, 942 y: event.root_y as i32, 943 }, 944 }, 945 on_root: window == Some(self.screen.root), 946 }) 947 } 948 949 #[inline] 950 fn on_button_release( 951 &self, 952 event: &xproto::ButtonReleaseEvent, 953 ) -> Option<Event> { 954 let window = event.event; 955 let window = if window == self.screen.root || window == x11rb::NONE { 956 if event.child == x11rb::NONE { 957 None 958 } else { 959 Some(event.child) 960 } 961 } else { 962 None 963 }; 964 965 Some(Event::Mouse { 966 event: MouseEvent { 967 kind: MouseEventKind::Release, 968 input: MouseInput { 969 target: MouseInputTarget::Global, 970 button: { 971 if let Ok(button) = Button::try_from(event.detail) { 972 button 973 } else { 974 return None; 975 } 976 }, 977 modifiers: { 978 let mut modifiers = HashSet::new(); 979 980 if event.state & u16::from(xproto::ModMask::CONTROL) > 0 { 981 modifiers.insert(Modifier::Ctrl); 982 } 983 984 if event.state & u16::from(xproto::ModMask::SHIFT) > 0 { 985 modifiers.insert(Modifier::Shift); 986 } 987 988 if event.state & u16::from(xproto::ModMask::M1) > 0 { 989 modifiers.insert(Modifier::Alt); 990 } 991 992 if event.state & u16::from(xproto::ModMask::M4) > 0 { 993 modifiers.insert(Modifier::Super); 994 } 995 996 modifiers 997 }, 998 }, 999 window: { 1000 if window == Some(self.screen.root) { 1001 None 1002 } else { 1003 window 1004 } 1005 }, 1006 root_rpos: Pos { 1007 x: event.root_x as i32, 1008 y: event.root_y as i32, 1009 }, 1010 }, 1011 on_root: window == Some(self.screen.root), 1012 }) 1013 } 1014 1015 #[inline] 1016 fn on_motion_notify( 1017 &self, 1018 event: &xproto::MotionNotifyEvent, 1019 ) -> Option<Event> { 1020 let window = event.event; 1021 let window = if window == self.screen.root || window == x11rb::NONE { 1022 if event.child == x11rb::NONE { 1023 None 1024 } else { 1025 Some(event.child) 1026 } 1027 } else { 1028 None 1029 }; 1030 1031 Some(Event::Mouse { 1032 event: MouseEvent { 1033 kind: MouseEventKind::Motion, 1034 input: MouseInput { 1035 target: MouseInputTarget::Global, 1036 button: Button::Left, 1037 modifiers: { 1038 let mut modifiers = HashSet::new(); 1039 1040 if event.state & u16::from(xproto::ModMask::CONTROL) > 0 { 1041 modifiers.insert(Modifier::Ctrl); 1042 } 1043 1044 if event.state & u16::from(xproto::ModMask::SHIFT) > 0 { 1045 modifiers.insert(Modifier::Shift); 1046 } 1047 1048 if event.state & u16::from(xproto::ModMask::M1) > 0 { 1049 modifiers.insert(Modifier::Alt); 1050 } 1051 1052 if event.state & u16::from(xproto::ModMask::M4) > 0 { 1053 modifiers.insert(Modifier::Super); 1054 } 1055 1056 modifiers 1057 }, 1058 }, 1059 window: { 1060 if window == Some(self.screen.root) { 1061 None 1062 } else { 1063 window 1064 } 1065 }, 1066 root_rpos: Pos { 1067 x: event.root_x as i32, 1068 y: event.root_y as i32, 1069 }, 1070 }, 1071 on_root: window == Some(self.screen.root), 1072 }) 1073 } 1074 1075 #[inline] 1076 fn on_key_press( 1077 &self, 1078 event: &xproto::KeyPressEvent, 1079 ) -> Option<Event> { 1080 Some(Event::Key { 1081 event: KeyEvent { 1082 input: KeyInput { 1083 key: self.get_key(event.detail), 1084 modifiers: { 1085 let mut modifiers = HashSet::new(); 1086 1087 if event.state & u16::from(xproto::ModMask::CONTROL) > 0 { 1088 modifiers.insert(Modifier::Ctrl); 1089 } 1090 1091 if event.state & u16::from(xproto::ModMask::SHIFT) > 0 { 1092 modifiers.insert(Modifier::Shift); 1093 } 1094 1095 if event.state & u16::from(xproto::ModMask::M1) > 0 { 1096 modifiers.insert(Modifier::Alt); 1097 } 1098 1099 if event.state & u16::from(xproto::ModMask::M4) > 0 { 1100 modifiers.insert(Modifier::Super); 1101 } 1102 1103 modifiers 1104 }, 1105 }, 1106 window: { 1107 let window = event.event; 1108 1109 if window == self.screen.root || window == x11rb::NONE { 1110 Some(event.child) 1111 } else { 1112 None 1113 } 1114 }, 1115 } 1116 }) 1117 } 1118 1119 #[inline] 1120 fn on_map_request( 1121 &self, 1122 event: &xproto::MapRequestEvent, 1123 ) -> Option<Event> { 1124 Some(Event::MapRequest { 1125 window: event.window, 1126 ignore: !self.must_manage_window(event.window), 1127 }) 1128 } 1129 1130 #[inline] 1131 fn on_map_notify( 1132 &self, 1133 event: &xproto::MapNotifyEvent, 1134 ) -> Option<Event> { 1135 Some(Event::Map { 1136 window: event.window, 1137 ignore: !self.must_manage_window(event.window), 1138 }) 1139 } 1140 1141 #[inline] 1142 fn on_enter_notify( 1143 &self, 1144 event: &xproto::EnterNotifyEvent, 1145 ) -> Option<Event> { 1146 Some(Event::Enter { 1147 window: event.event, 1148 root_rpos: Pos { 1149 x: event.root_x as i32, 1150 y: event.root_y as i32, 1151 }, 1152 window_rpos: Pos { 1153 x: event.event_x as i32, 1154 y: event.event_y as i32, 1155 }, 1156 }) 1157 } 1158 1159 #[inline] 1160 fn on_leave_notify( 1161 &self, 1162 event: &xproto::LeaveNotifyEvent, 1163 ) -> Option<Event> { 1164 Some(Event::Leave { 1165 window: event.event, 1166 root_rpos: Pos { 1167 x: event.root_x as i32, 1168 y: event.root_y as i32, 1169 }, 1170 window_rpos: Pos { 1171 x: event.event_x as i32, 1172 y: event.event_y as i32, 1173 }, 1174 }) 1175 } 1176 1177 #[inline] 1178 fn on_destroy_notify( 1179 &self, 1180 event: &xproto::DestroyNotifyEvent, 1181 ) -> Option<Event> { 1182 Some(Event::Destroy { 1183 window: event.window, 1184 }) 1185 } 1186 1187 #[inline] 1188 fn on_expose( 1189 &self, 1190 event: &xproto::ExposeEvent, 1191 ) -> Option<Event> { 1192 Some(Event::Expose { 1193 window: event.window, 1194 }) 1195 } 1196 1197 #[inline] 1198 fn on_unmap_notify( 1199 &self, 1200 event: &xproto::UnmapNotifyEvent, 1201 ) -> Option<Event> { 1202 self.conn 1203 .get_window_attributes(event.window) 1204 .ok() 1205 .map(|cookie| Event::Unmap { 1206 window: event.window, 1207 ignore: cookie 1208 .reply() 1209 .map_or(false, |reply| reply.override_redirect), 1210 }) 1211 } 1212 1213 #[inline] 1214 fn on_configure_request( 1215 &self, 1216 event: &xproto::ConfigureRequestEvent, 1217 ) -> Option<Event> { 1218 let geometry = self.get_window_geometry(event.window).ok()?; 1219 1220 let mut x = None; 1221 let mut y = None; 1222 let mut w = None; 1223 let mut h = None; 1224 1225 if event.value_mask & u16::from(xproto::ConfigWindow::X) != 0 { 1226 x = Some(event.x as i32); 1227 } 1228 1229 if event.value_mask & u16::from(xproto::ConfigWindow::Y) != 0 { 1230 y = Some(event.y as i32); 1231 } 1232 1233 if event.value_mask & u16::from(xproto::ConfigWindow::WIDTH) != 0 { 1234 w = Some(event.width as i32); 1235 } 1236 1237 if event.value_mask & u16::from(xproto::ConfigWindow::HEIGHT) != 0 { 1238 h = Some(event.height as i32); 1239 } 1240 1241 let pos = match (x, y) { 1242 (Some(x), Some(y)) => Some(Pos { 1243 x, 1244 y, 1245 }), 1246 (None, Some(y)) => Some(Pos { 1247 x: geometry.pos.x, 1248 y, 1249 }), 1250 (Some(x), None) => Some(Pos { 1251 x, 1252 y: geometry.pos.y, 1253 }), 1254 _ => None, 1255 }; 1256 1257 let dim = match (w, h) { 1258 (Some(w), Some(h)) => Some(Dim { 1259 w, 1260 h, 1261 }), 1262 (None, Some(h)) => Some(Dim { 1263 w: geometry.dim.w, 1264 h, 1265 }), 1266 (Some(w), None) => Some(Dim { 1267 w, 1268 h: geometry.dim.h, 1269 }), 1270 _ => None, 1271 }; 1272 1273 if pos.is_some() || dim.is_some() { 1274 return Some(Event::PlacementRequest { 1275 window: event.window, 1276 pos, 1277 dim, 1278 on_root: event.window == self.screen.root, 1279 }); 1280 } 1281 1282 if event.value_mask & u16::from(xproto::ConfigWindow::STACK_MODE) != 0 { 1283 if event.sibling != x11rb::NONE { 1284 match event.stack_mode { 1285 // window is placed above sibling 1286 xproto::StackMode::ABOVE => { 1287 return Some(Event::RestackRequest { 1288 window: event.window, 1289 sibling: event.sibling, 1290 mode: StackMode::Above, 1291 on_root: event.window == self.screen.root, 1292 }); 1293 }, 1294 // sibling is placed above window 1295 xproto::StackMode::BELOW => { 1296 return Some(Event::RestackRequest { 1297 window: event.window, 1298 sibling: event.sibling, 1299 mode: StackMode::Below, 1300 on_root: event.window == self.screen.root, 1301 }); 1302 }, 1303 _ => {}, 1304 } 1305 } 1306 } 1307 1308 None 1309 } 1310 1311 #[inline] 1312 fn on_configure_notify( 1313 &self, 1314 event: &xproto::ConfigureNotifyEvent, 1315 ) -> Option<Event> { 1316 Some(Event::Configure { 1317 window: event.window, 1318 region: Region::new( 1319 event.x as i32, 1320 event.y as i32, 1321 event.width as i32, 1322 event.height as i32, 1323 ), 1324 on_root: event.window == self.screen.root, 1325 }) 1326 } 1327 1328 #[inline] 1329 fn on_property_notify( 1330 &self, 1331 event: &xproto::PropertyNotifyEvent, 1332 ) -> Option<Event> { 1333 if event.state == xproto::Property::NEW_VALUE { 1334 if event.atom == self.atoms.WM_NAME || event.atom == self.atoms._NET_WM_NAME { 1335 return Some(Event::Property { 1336 window: event.window, 1337 kind: PropertyKind::Name, 1338 on_root: event.window == self.screen.root, 1339 }); 1340 } 1341 1342 if event.atom == self.atoms.WM_CLASS { 1343 return Some(Event::Property { 1344 window: event.window, 1345 kind: PropertyKind::Class, 1346 on_root: event.window == self.screen.root, 1347 }); 1348 } 1349 1350 if event.atom == self.atoms.WM_NORMAL_HINTS { 1351 return Some(Event::Property { 1352 window: event.window, 1353 kind: PropertyKind::Size, 1354 on_root: event.window == self.screen.root, 1355 }); 1356 } 1357 } 1358 1359 if event.atom == self.atoms._NET_WM_STRUT || event.atom == self.atoms._NET_WM_STRUT_PARTIAL 1360 { 1361 return Some(Event::Property { 1362 window: event.window, 1363 kind: PropertyKind::Strut, 1364 on_root: event.window == self.screen.root, 1365 }); 1366 } 1367 1368 None 1369 } 1370 1371 #[inline] 1372 fn on_client_message( 1373 &self, 1374 event: &xproto::ClientMessageEvent, 1375 ) -> Option<Event> { 1376 let data = match event.format { 1377 8 => event.data.as_data8().iter().map(|&i| i as usize).collect(), 1378 16 => event.data.as_data16().iter().map(|&i| i as usize).collect(), 1379 32 => event.data.as_data32().iter().map(|&i| i as usize).collect(), 1380 _ => Vec::with_capacity(0), 1381 }; 1382 1383 if event.type_ == self.atoms._NET_WM_STATE { 1384 if event.format != 32 || data.len() < 3 { 1385 return None; 1386 } 1387 1388 let mut state = None; 1389 1390 for i in 1..=2 { 1391 if state.is_none() { 1392 if data[i] != 0 { 1393 state = self.get_window_state_from_atom(data[i] as Atom); 1394 } 1395 } 1396 } 1397 1398 if let Some(state) = state { 1399 return Some(Event::StateRequest { 1400 window: event.window, 1401 state, 1402 action: match data[0] { 1403 0 => ToggleAction::Remove, 1404 1 => ToggleAction::Add, 1405 2 => ToggleAction::Toggle, 1406 _ => return None, 1407 }, 1408 on_root: event.window == self.screen.root, 1409 }); 1410 } 1411 } else if event.type_ == self.atoms._NET_MOVERESIZE_WINDOW { 1412 // TODO: handle gravity 1413 let x = data.get(1)?.clone(); 1414 let y = data.get(2)?.clone(); 1415 let width = data.get(3)?.clone(); 1416 let height = data.get(4)?.clone(); 1417 1418 return Some(Event::PlacementRequest { 1419 window: event.window, 1420 pos: Some(Pos { 1421 x: x as i32, 1422 y: y as i32, 1423 }), 1424 dim: Some(Dim { 1425 w: width as i32, 1426 h: height as i32, 1427 }), 1428 on_root: event.window == self.screen.root, 1429 }); 1430 } else if event.type_ == self.atoms._NET_WM_MOVERESIZE { 1431 let x_root = data.get(0)?.clone(); 1432 let y_root = data.get(1)?.clone(); 1433 let direction = data.get(2)?.clone(); 1434 1435 return Some(Event::GripRequest { 1436 window: event.window, 1437 pos: Pos { 1438 x: x_root as i32, 1439 y: y_root as i32, 1440 }, 1441 grip: match direction { 1442 0 => Some(Grip::Corner(Corner::TopLeft)), 1443 1 => Some(Grip::Edge(Edge::Top)), 1444 2 => Some(Grip::Corner(Corner::TopRight)), 1445 3 => Some(Grip::Edge(Edge::Right)), 1446 4 => Some(Grip::Corner(Corner::BottomRight)), 1447 5 => Some(Grip::Edge(Edge::Bottom)), 1448 6 => Some(Grip::Corner(Corner::BottomLeft)), 1449 7 => Some(Grip::Edge(Edge::Left)), 1450 8 => None, 1451 _ => return None, 1452 }, 1453 on_root: event.window == self.screen.root, 1454 }); 1455 } else if event.type_ == self.atoms._NET_REQUEST_FRAME_EXTENTS { 1456 return Some(Event::FrameExtentsRequest { 1457 window: event.window, 1458 on_root: event.window == self.screen.root, 1459 }); 1460 } else if event.type_ == self.atoms._NET_CURRENT_DESKTOP { 1461 return Some(Event::WorkspaceRequest { 1462 window: None, 1463 index: data.get(0)?.clone(), 1464 on_root: event.window == self.screen.root, 1465 }); 1466 } else if event.type_ == self.atoms._NET_CLOSE_WINDOW { 1467 return Some(Event::CloseRequest { 1468 window: event.window, 1469 on_root: event.window == self.screen.root, 1470 }); 1471 } else if event.type_ == self.atoms._NET_ACTIVE_WINDOW { 1472 if let Some(&source) = data.get(0) { 1473 if source <= 2 { 1474 return Some(Event::FocusRequest { 1475 window: event.window, 1476 on_root: event.window == self.screen.root, 1477 }); 1478 } 1479 } 1480 } 1481 1482 None 1483 } 1484 1485 #[inline] 1486 fn on_mapping_notify( 1487 &self, 1488 event: &xproto::MappingNotifyEvent, 1489 ) -> Option<Event> { 1490 None 1491 } 1492 1493 #[inline] 1494 fn on_randr_notify( 1495 &self, 1496 _event: &randr::NotifyEvent, 1497 ) -> Option<Event> { 1498 Some(Event::ScreenChange) 1499 } 1500 } 1501 1502 impl<'conn, Conn: connection::Connection> Connection for XConnection<'conn, Conn> { 1503 #[inline] 1504 fn flush(&self) -> bool { 1505 self.conn.flush().is_ok() 1506 } 1507 1508 #[inline] 1509 fn step(&self) -> Option<Event> { 1510 self.conn 1511 .wait_for_event() 1512 .ok() 1513 .and_then(|event| match event { 1514 XEvent::ButtonPress(e) => self.on_button_press(&e), 1515 XEvent::ButtonRelease(e) => self.on_button_release(&e), 1516 XEvent::MotionNotify(e) => self.on_motion_notify(&e), 1517 XEvent::KeyPress(e) => self.on_key_press(&e), 1518 XEvent::MapRequest(e) => self.on_map_request(&e), 1519 XEvent::MapNotify(e) => self.on_map_notify(&e), 1520 XEvent::EnterNotify(e) => self.on_enter_notify(&e), 1521 XEvent::LeaveNotify(e) => self.on_leave_notify(&e), 1522 XEvent::DestroyNotify(e) => self.on_destroy_notify(&e), 1523 XEvent::Expose(e) => self.on_expose(&e), 1524 XEvent::UnmapNotify(e) => self.on_unmap_notify(&e), 1525 XEvent::ConfigureRequest(e) => self.on_configure_request(&e), 1526 XEvent::ConfigureNotify(e) => self.on_configure_notify(&e), 1527 XEvent::PropertyNotify(e) => self.on_property_notify(&e), 1528 XEvent::ClientMessage(e) => self.on_client_message(&e), 1529 XEvent::MappingNotify(e) => self.on_mapping_notify(&e), 1530 XEvent::RandrNotify(e) => self.on_randr_notify(&e), 1531 _ => None, 1532 }) 1533 } 1534 1535 fn connected_outputs(&self) -> Vec<Screen> { 1536 let resources = randr::get_screen_resources(self.conn, self.check_window); 1537 1538 if let Ok(resources) = resources { 1539 if let Ok(reply) = resources.reply() { 1540 return reply 1541 .crtcs 1542 .into_iter() 1543 .flat_map(|crtc| { 1544 randr::get_crtc_info(self.conn, crtc, 0).map(|cookie| cookie.reply()) 1545 }) 1546 .enumerate() 1547 .map(|(i, reply)| { 1548 let reply = reply.unwrap(); 1549 let region = Region { 1550 pos: Pos { 1551 x: reply.x as i32, 1552 y: reply.y as i32, 1553 }, 1554 dim: Dim { 1555 w: reply.width as i32, 1556 h: reply.height as i32, 1557 }, 1558 }; 1559 1560 Screen::new(region, i) 1561 }) 1562 .filter(|screen| { 1563 let region = screen.full_region(); 1564 region.dim.w > 0 && region.dim.h > 0 1565 }) 1566 .collect(); 1567 } 1568 } 1569 1570 panic!("could not obtain screen resources") 1571 } 1572 1573 fn top_level_windows(&self) -> Vec<Window> { 1574 self.conn 1575 .query_tree(self.screen.root) 1576 .map_or(Vec::with_capacity(0), |cookie| { 1577 cookie.reply().map_or(Vec::with_capacity(0), |reply| { 1578 reply 1579 .children 1580 .into_iter() 1581 .filter(|&w| self.must_manage_window(w)) 1582 .collect() 1583 }) 1584 }) 1585 } 1586 1587 #[inline] 1588 fn get_pointer_position(&self) -> Pos { 1589 self.conn 1590 .query_pointer(self.screen.root) 1591 .map_or(Pos::default(), |cookie| { 1592 cookie.reply().map_or(Pos::default(), |reply| Pos { 1593 x: reply.root_x as i32, 1594 y: reply.root_y as i32, 1595 }) 1596 }) 1597 } 1598 1599 #[inline] 1600 fn warp_pointer_center_of_window_or_root( 1601 &self, 1602 window: Option<Window>, 1603 screen: &Screen, 1604 ) { 1605 let pos = Pos::from_center_of_dim( 1606 window 1607 .and_then(|window| self.get_window_geometry(window).ok()) 1608 .unwrap_or(screen.placeable_region()) 1609 .dim, 1610 ); 1611 1612 drop(self.conn.warp_pointer( 1613 x11rb::NONE, 1614 window.unwrap_or(self.screen.root), 1615 0, 1616 0, 1617 0, 1618 0, 1619 pos.x as i16, 1620 pos.y as i16, 1621 )); 1622 } 1623 1624 #[inline] 1625 fn warp_pointer( 1626 &self, 1627 pos: Pos, 1628 ) { 1629 drop(self.conn.warp_pointer( 1630 x11rb::NONE, 1631 self.screen.root, 1632 0, 1633 0, 1634 0, 1635 0, 1636 pos.x as i16, 1637 pos.y as i16, 1638 )); 1639 } 1640 1641 fn warp_pointer_rpos( 1642 &self, 1643 window: Window, 1644 pos: Pos, 1645 ) { 1646 drop( 1647 self.conn 1648 .warp_pointer(x11rb::NONE, window, 0, 0, 0, 0, pos.x as i16, pos.y as i16), 1649 ); 1650 } 1651 1652 #[inline] 1653 fn confine_pointer( 1654 &self, 1655 window: Window, 1656 ) { 1657 if self.confined_to.get().is_none() { 1658 if self 1659 .conn 1660 .grab_pointer( 1661 false, 1662 self.screen.root, 1663 u32::from(EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE) as u16, 1664 xproto::GrabMode::ASYNC, 1665 xproto::GrabMode::ASYNC, 1666 self.screen.root, 1667 x11rb::NONE, 1668 x11rb::CURRENT_TIME, 1669 ) 1670 .is_ok() 1671 { 1672 drop(self.conn.grab_keyboard( 1673 false, 1674 self.screen.root, 1675 x11rb::CURRENT_TIME, 1676 xproto::GrabMode::ASYNC, 1677 xproto::GrabMode::ASYNC, 1678 )); 1679 1680 self.confined_to.set(Some(window)); 1681 } 1682 } 1683 } 1684 1685 #[inline] 1686 fn release_pointer(&self) { 1687 if self.confined_to.get().is_some() { 1688 drop(self.conn.ungrab_pointer(x11rb::CURRENT_TIME)); 1689 drop(self.conn.ungrab_keyboard(x11rb::CURRENT_TIME)); 1690 1691 self.confined_to.set(None); 1692 } 1693 } 1694 1695 fn cleanup(&self) { 1696 drop( 1697 self.conn 1698 .ungrab_key(xproto::Grab::ANY, self.screen.root, xproto::ModMask::ANY), 1699 ); 1700 1701 drop(self.conn.destroy_window(self.check_window)); 1702 1703 drop( 1704 self.conn 1705 .delete_property(self.screen.root, self.atoms._NET_ACTIVE_WINDOW), 1706 ); 1707 1708 drop( 1709 self.conn 1710 .delete_property(self.screen.root, self.atoms._NET_SUPPORTING_WM_CHECK), 1711 ); 1712 1713 drop( 1714 self.conn 1715 .delete_property(self.screen.root, self.atoms._NET_WM_NAME), 1716 ); 1717 1718 drop( 1719 self.conn 1720 .delete_property(self.screen.root, self.atoms.WM_CLASS), 1721 ); 1722 1723 drop( 1724 self.conn 1725 .delete_property(self.screen.root, self.atoms._NET_SUPPORTED), 1726 ); 1727 1728 drop( 1729 self.conn 1730 .delete_property(self.screen.root, self.atoms._NET_WM_PID), 1731 ); 1732 1733 drop( 1734 self.conn 1735 .delete_property(self.screen.root, self.atoms._NET_CLIENT_LIST), 1736 ); 1737 1738 drop(self.conn); 1739 } 1740 1741 #[inline] 1742 fn create_frame( 1743 &self, 1744 region: Region, 1745 ) -> Window { 1746 const ERR: &str = "unable to create frame"; 1747 1748 let frame = self.conn.generate_id().expect(ERR); 1749 let aux = xproto::CreateWindowAux::new() 1750 .backing_store(Some(xproto::BackingStore::ALWAYS)) 1751 .event_mask(EventMask::EXPOSURE | EventMask::KEY_PRESS); 1752 1753 self.conn 1754 .create_window( 1755 x11rb::COPY_DEPTH_FROM_PARENT, 1756 frame, 1757 self.screen.root, 1758 region.pos.x as i16, 1759 region.pos.y as i16, 1760 region.dim.w as u16, 1761 region.dim.h as u16, 1762 0, 1763 xproto::WindowClass::INPUT_OUTPUT, 1764 0, 1765 &aux, 1766 ) 1767 .expect(ERR); 1768 1769 self.flush(); 1770 1771 frame 1772 } 1773 1774 #[inline] 1775 fn create_handle(&self) -> Window { 1776 const ERR: &str = "unable to create handle"; 1777 1778 let handle = self.conn.generate_id().expect(ERR); 1779 let aux = xproto::CreateWindowAux::new().override_redirect(1); 1780 1781 self.conn 1782 .create_window( 1783 x11rb::COPY_DEPTH_FROM_PARENT, 1784 handle, 1785 self.screen.root, 1786 -2, 1787 -2, 1788 1, 1789 1, 1790 0, 1791 xproto::WindowClass::INPUT_ONLY, 1792 0, 1793 &aux, 1794 ) 1795 .expect(ERR); 1796 1797 self.flush(); 1798 1799 handle 1800 } 1801 1802 #[inline] 1803 fn init_window( 1804 &self, 1805 window: Window, 1806 focus_follows_mouse: bool, 1807 ) { 1808 drop(self.conn.change_window_attributes( 1809 window, 1810 &xproto::ChangeWindowAttributesAux::default().event_mask(self.window_event_mask), 1811 )); 1812 } 1813 1814 #[inline] 1815 fn init_frame( 1816 &self, 1817 window: Window, 1818 focus_follows_mouse: bool, 1819 ) { 1820 drop(self.conn.change_window_attributes( 1821 window, 1822 &xproto::ChangeWindowAttributesAux::default().event_mask( 1823 self.frame_event_mask 1824 | if focus_follows_mouse { 1825 EventMask::ENTER_WINDOW 1826 } else { 1827 EventMask::NO_EVENT 1828 }, 1829 ), 1830 )); 1831 } 1832 1833 #[inline] 1834 fn init_unmanaged( 1835 &self, 1836 window: Window, 1837 ) { 1838 drop(self.conn.change_window_attributes( 1839 window, 1840 &xproto::ChangeWindowAttributesAux::default().event_mask(EventMask::STRUCTURE_NOTIFY), 1841 )); 1842 } 1843 1844 #[inline] 1845 fn cleanup_window( 1846 &self, 1847 window: Window, 1848 ) { 1849 drop(self.conn.delete_property(window, self.atoms._NET_WM_STATE)); 1850 drop( 1851 self.conn 1852 .delete_property(window, self.atoms._NET_WM_DESKTOP), 1853 ); 1854 } 1855 1856 #[inline] 1857 fn map_window( 1858 &self, 1859 window: Window, 1860 ) { 1861 drop(self.conn.map_window(window)); 1862 } 1863 1864 #[inline] 1865 fn unmap_window( 1866 &self, 1867 window: Window, 1868 ) { 1869 drop(self.conn.unmap_window(window)); 1870 } 1871 1872 #[inline] 1873 fn reparent_window( 1874 &self, 1875 window: Window, 1876 parent: Window, 1877 pos: Pos, 1878 ) { 1879 drop( 1880 self.conn 1881 .reparent_window(window, parent, pos.x as i16, pos.y as i16), 1882 ); 1883 } 1884 1885 #[inline] 1886 fn unparent_window( 1887 &self, 1888 window: Window, 1889 pos: Pos, 1890 ) { 1891 drop( 1892 self.conn 1893 .reparent_window(window, self.screen.root, pos.x as i16, pos.y as i16), 1894 ); 1895 } 1896 1897 #[inline] 1898 fn destroy_window( 1899 &self, 1900 window: Window, 1901 ) { 1902 drop(self.conn.destroy_window(window)); 1903 } 1904 1905 #[inline] 1906 fn close_window( 1907 &self, 1908 window: Window, 1909 ) -> bool { 1910 self.send_protocol_client_message(window, self.atoms.WM_DELETE_WINDOW) 1911 .map_or(false, |_| self.flush()) 1912 } 1913 1914 #[inline] 1915 fn kill_window( 1916 &self, 1917 window: Window, 1918 ) -> bool { 1919 let protocols = &[self.atoms.WM_DELETE_WINDOW]; 1920 1921 if self.window_has_any_of_protocols(window, protocols) { 1922 self.close_window(window) 1923 } else { 1924 self.conn 1925 .kill_client(window) 1926 .map_or(false, |_| self.flush()) 1927 } 1928 } 1929 1930 #[inline] 1931 fn place_window( 1932 &self, 1933 window: Window, 1934 region: &Region, 1935 ) { 1936 drop( 1937 self.conn.configure_window( 1938 window, 1939 &xproto::ConfigureWindowAux::default() 1940 .x(region.pos.x as i32) 1941 .y(region.pos.y as i32) 1942 .width(region.dim.w as u32) 1943 .height(region.dim.h as u32), 1944 ), 1945 ); 1946 } 1947 1948 #[inline] 1949 fn move_window( 1950 &self, 1951 window: Window, 1952 pos: Pos, 1953 ) { 1954 drop( 1955 self.conn.configure_window( 1956 window, 1957 &xproto::ConfigureWindowAux::default() 1958 .x(pos.x as i32) 1959 .y(pos.y as i32), 1960 ), 1961 ); 1962 } 1963 1964 #[inline] 1965 fn resize_window( 1966 &self, 1967 window: Window, 1968 dim: Dim, 1969 ) { 1970 drop( 1971 self.conn.configure_window( 1972 window, 1973 &xproto::ConfigureWindowAux::default() 1974 .width(dim.w as u32) 1975 .height(dim.h as u32), 1976 ), 1977 ); 1978 } 1979 1980 #[inline] 1981 fn focus_window( 1982 &self, 1983 window: Window, 1984 ) { 1985 drop( 1986 self.conn 1987 .set_input_focus(xproto::InputFocus::PARENT, window, x11rb::CURRENT_TIME), 1988 ); 1989 1990 drop(self.conn.change_property32( 1991 xproto::PropMode::REPLACE, 1992 self.screen.root, 1993 self.atoms._NET_ACTIVE_WINDOW, 1994 xproto::AtomEnum::WINDOW, 1995 &[window], 1996 )); 1997 } 1998 1999 #[inline] 2000 fn stack_window_above( 2001 &self, 2002 window: Window, 2003 sibling: Option<Window>, 2004 ) { 2005 let mut aux = xproto::ConfigureWindowAux::default().stack_mode(xproto::StackMode::ABOVE); 2006 2007 if let Some(sibling) = sibling { 2008 aux = aux.sibling(sibling); 2009 } 2010 2011 drop(self.conn.configure_window(window, &aux)); 2012 } 2013 2014 #[inline] 2015 fn stack_window_below( 2016 &self, 2017 window: Window, 2018 sibling: Option<Window>, 2019 ) { 2020 let mut aux = xproto::ConfigureWindowAux::default().stack_mode(xproto::StackMode::BELOW); 2021 2022 if let Some(sibling) = sibling { 2023 aux = aux.sibling(sibling); 2024 } 2025 2026 drop(self.conn.configure_window(window, &aux)); 2027 } 2028 2029 #[inline] 2030 fn insert_window_in_save_set( 2031 &self, 2032 window: Window, 2033 ) { 2034 drop(self.conn.change_save_set(xproto::SetMode::INSERT, window)); 2035 } 2036 2037 fn grab_bindings( 2038 &self, 2039 key_inputs: &[&KeyInput], 2040 mouse_inputs: &[&MouseInput], 2041 ) { 2042 for &m in &[0, u16::from(ModMask::M2), u16::from(ModMask::M5)] { 2043 for key_input in key_inputs { 2044 drop( 2045 self.conn.grab_key( 2046 false, 2047 self.screen.root, 2048 key_input 2049 .modifiers 2050 .iter() 2051 .fold(0u16, |acc, &m| acc | { 2052 u16::from(match m { 2053 Modifier::Ctrl => xproto::ModMask::CONTROL, 2054 Modifier::Shift => xproto::ModMask::SHIFT, 2055 Modifier::Alt => xproto::ModMask::M1, 2056 Modifier::Super => xproto::ModMask::M4, 2057 Modifier::NumLock => xproto::ModMask::M2, 2058 Modifier::ScrollLock => xproto::ModMask::M5, 2059 _ => xproto::ModMask::ANY, 2060 }) 2061 }) 2062 | u16::from(m), 2063 self.get_keycode(key_input.key), 2064 xproto::GrabMode::ASYNC, 2065 xproto::GrabMode::ASYNC, 2066 ), 2067 ); 2068 } 2069 2070 for mouse_input in mouse_inputs { 2071 drop( 2072 self.conn.grab_button( 2073 false, 2074 self.screen.root, 2075 u32::from(self.mouse_event_mask) as u16, 2076 xproto::GrabMode::ASYNC, 2077 xproto::GrabMode::ASYNC, 2078 x11rb::NONE, 2079 x11rb::NONE, 2080 xproto::ButtonIndex::try_from(mouse_input.button as u8).unwrap(), 2081 mouse_input 2082 .modifiers 2083 .iter() 2084 .fold(0u16, |acc, &m| acc | { 2085 u16::from(match m { 2086 Modifier::Ctrl => xproto::ModMask::CONTROL, 2087 Modifier::Shift => xproto::ModMask::SHIFT, 2088 Modifier::Alt => xproto::ModMask::M1, 2089 Modifier::Super => xproto::ModMask::M4, 2090 Modifier::NumLock => xproto::ModMask::M2, 2091 Modifier::ScrollLock => xproto::ModMask::M5, 2092 _ => xproto::ModMask::ANY, 2093 }) 2094 }) 2095 | u16::from(m), 2096 ), 2097 ); 2098 } 2099 } 2100 2101 drop(self.conn.change_window_attributes( 2102 self.screen.root, 2103 &xproto::ChangeWindowAttributesAux::default().event_mask(self.root_event_mask), 2104 )); 2105 2106 self.flush(); 2107 } 2108 2109 #[inline] 2110 fn regrab_buttons( 2111 &self, 2112 window: Window, 2113 ) { 2114 drop(self.conn.grab_button( 2115 true, 2116 window, 2117 u32::from(self.regrab_event_mask) as u16, 2118 xproto::GrabMode::ASYNC, 2119 xproto::GrabMode::ASYNC, 2120 x11rb::NONE, 2121 x11rb::NONE, 2122 xproto::ButtonIndex::ANY, 2123 xproto::ModMask::ANY, 2124 )); 2125 } 2126 2127 #[inline] 2128 fn ungrab_buttons( 2129 &self, 2130 window: Window, 2131 ) { 2132 drop( 2133 self.conn 2134 .ungrab_button(xproto::ButtonIndex::ANY, window, xproto::ModMask::ANY), 2135 ); 2136 } 2137 2138 #[inline] 2139 fn unfocus(&self) { 2140 drop(self.conn.set_input_focus( 2141 xproto::InputFocus::PARENT, 2142 self.check_window, 2143 x11rb::CURRENT_TIME, 2144 )); 2145 2146 drop( 2147 self.conn 2148 .delete_property(self.screen.root, self.atoms._NET_ACTIVE_WINDOW), 2149 ); 2150 } 2151 2152 #[inline] 2153 fn set_window_border_width( 2154 &self, 2155 window: Window, 2156 width: u32, 2157 ) { 2158 drop(self.conn.configure_window( 2159 window, 2160 &xproto::ConfigureWindowAux::default().border_width(width), 2161 )); 2162 } 2163 2164 #[inline] 2165 fn set_window_border_color( 2166 &self, 2167 window: Window, 2168 color: u32, 2169 ) { 2170 drop(self.conn.change_window_attributes( 2171 window, 2172 &xproto::ChangeWindowAttributesAux::default().border_pixel(color), 2173 )); 2174 } 2175 2176 #[inline] 2177 fn set_window_background_color( 2178 &self, 2179 window: Window, 2180 color: u32, 2181 ) { 2182 if let Ok(geometry) = self.get_window_geometry(window) { 2183 drop(self.conn.change_gc( 2184 self.background_gc, 2185 &xproto::ChangeGCAux::new().foreground(color), 2186 )); 2187 2188 drop( 2189 self.conn 2190 .poly_fill_rectangle(window, self.background_gc, &[xproto::Rectangle { 2191 x: 0, 2192 y: 0, 2193 width: geometry.dim.w as u16, 2194 height: geometry.dim.h as u16, 2195 }]), 2196 ); 2197 } 2198 } 2199 2200 #[inline] 2201 fn update_window_offset( 2202 &self, 2203 window: Window, 2204 frame: Window, 2205 ) { 2206 if let Ok(frame_geometry) = self.get_window_geometry(frame) { 2207 if let Ok(window_geometry) = self.get_window_geometry(window) { 2208 let event = xproto::ConfigureNotifyEvent { 2209 response_type: xproto::CONFIGURE_NOTIFY_EVENT, 2210 sequence: 0, 2211 event: window, 2212 window, 2213 above_sibling: x11rb::NONE, 2214 x: (frame_geometry.pos.x + window_geometry.pos.x) as i16, 2215 y: (frame_geometry.pos.y + window_geometry.pos.y) as i16, 2216 width: window_geometry.dim.w as u16, 2217 height: window_geometry.dim.h as u16, 2218 border_width: 0, 2219 override_redirect: false, 2220 }; 2221 2222 drop( 2223 self.conn 2224 .send_event(false, window, EventMask::STRUCTURE_NOTIFY, &event), 2225 ); 2226 } 2227 } 2228 } 2229 2230 #[inline] 2231 fn get_focused_window(&self) -> Window { 2232 self.conn 2233 .get_input_focus() 2234 .map_or(self.screen.root, |cookie| { 2235 cookie.reply().map_or(self.screen.root, |reply| reply.focus) 2236 }) 2237 } 2238 2239 #[inline] 2240 fn get_window_geometry( 2241 &self, 2242 window: Window, 2243 ) -> Result<Region> { 2244 Ok(self.conn.get_geometry(window)?.reply().map(|reply| { 2245 Region::new( 2246 reply.x as i32, 2247 reply.y as i32, 2248 reply.width as i32, 2249 reply.height as i32, 2250 ) 2251 })?) 2252 } 2253 2254 #[inline] 2255 fn get_window_pid( 2256 &self, 2257 window: Window, 2258 ) -> Option<Pid> { 2259 let id_spec = protocol::res::ClientIdSpec { 2260 client: window, 2261 mask: u8::from(protocol::res::ClientIdMask::LOCAL_CLIENT_PID) as u32, 2262 }; 2263 2264 protocol::res::query_client_ids(self.conn, &[id_spec]) 2265 .ok() 2266 .and_then(|cookie| cookie.reply().ok()) 2267 .and_then(|reply| { 2268 for i in reply.ids { 2269 if (i.spec.mask 2270 & (u8::from(protocol::res::ClientIdMask::LOCAL_CLIENT_PID)) as u32) 2271 != 0 2272 { 2273 if i.value.len() > 0 && i.value[0] != 0 { 2274 return Some(i.value[0] as Pid); 2275 } 2276 } 2277 } 2278 2279 None 2280 }) 2281 } 2282 2283 #[inline] 2284 fn must_manage_window( 2285 &self, 2286 window: Window, 2287 ) -> bool { 2288 let do_not_manage = self 2289 .conn 2290 .get_window_attributes(window) 2291 .map_or(false, |cookie| { 2292 cookie.reply().map_or(false, |reply| { 2293 reply.override_redirect || reply.class == xproto::WindowClass::INPUT_ONLY 2294 }) 2295 }); 2296 2297 if do_not_manage { 2298 return false; 2299 } 2300 2301 let to_exclude = &[ 2302 self.atoms._NET_WM_WINDOW_TYPE_DOCK, 2303 self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, 2304 ]; 2305 2306 !self.window_is_any_of_types(window, to_exclude) 2307 } 2308 2309 #[inline] 2310 fn must_free_window( 2311 &self, 2312 window: Window, 2313 ) -> bool { 2314 if self.get_window_desktop(window) == Some(0xFFFFFFFF) 2315 || self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_MODAL]) 2316 || self.window_is_any_of_types(window, &[ 2317 self.atoms._NET_WM_WINDOW_TYPE_DIALOG, 2318 self.atoms._NET_WM_WINDOW_TYPE_UTILITY, 2319 self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, 2320 self.atoms._NET_WM_WINDOW_TYPE_SPLASH, 2321 ]) 2322 { 2323 return true; 2324 } 2325 2326 self.get_window_geometry(window).map_or(false, |geometry| { 2327 let (_, sh) = self.get_icccm_window_size_hints(window, None, &None); 2328 2329 sh.map_or(false, |sh| { 2330 match (sh.min_width, sh.min_height, sh.max_width, sh.max_height) { 2331 (Some(miw), Some(mih), Some(maw), Some(mah)) => { 2332 maw > 0 && mah > 0 && maw == miw && mah == mih 2333 }, 2334 _ => false, 2335 } 2336 }) 2337 }) 2338 } 2339 2340 fn window_is_mappable( 2341 &self, 2342 window: Window, 2343 ) -> bool { 2344 self.conn 2345 .get_window_attributes(window) 2346 .map_or(false, |cookie| { 2347 cookie.reply().map_or(false, |reply| { 2348 let default_state = properties::WmHintsState::Normal; 2349 let initial_state = properties::WmHints::get(self.conn, window).ok().map_or( 2350 default_state, 2351 |cookie| { 2352 cookie.reply().map_or(default_state, |reply| { 2353 reply.initial_state.unwrap_or(default_state) 2354 }) 2355 }, 2356 ); 2357 2358 reply.class != xproto::WindowClass::INPUT_ONLY 2359 && !self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_HIDDEN]) 2360 && match initial_state { 2361 properties::WmHintsState::Normal => true, 2362 _ => false, 2363 } 2364 }) 2365 }) 2366 } 2367 2368 #[inline] 2369 fn set_icccm_window_state( 2370 &self, 2371 window: Window, 2372 state: IcccmWindowState, 2373 ) { 2374 drop(self.conn.change_property32( 2375 xproto::PropMode::REPLACE, 2376 window, 2377 self.atoms.WM_STATE, 2378 self.atoms.CARDINAL, 2379 &[ 2380 match state { 2381 IcccmWindowState::Withdrawn => 0, 2382 IcccmWindowState::Normal => 1, 2383 IcccmWindowState::Iconic => 3, 2384 }, 2385 0, 2386 ], 2387 )); 2388 } 2389 2390 #[inline] 2391 fn set_icccm_window_hints( 2392 &self, 2393 window: Window, 2394 hints: Hints, 2395 ) { 2396 let wm_hints = properties::WmHints { 2397 input: hints.input, 2398 initial_state: match hints.initial_state { 2399 Some(IcccmWindowState::Normal) => Some(properties::WmHintsState::Normal), 2400 Some(IcccmWindowState::Iconic) => Some(properties::WmHintsState::Iconic), 2401 _ => None, 2402 }, 2403 icon_pixmap: None, 2404 icon_window: None, 2405 icon_position: None, 2406 icon_mask: None, 2407 window_group: hints.group, 2408 urgent: hints.urgent, 2409 }; 2410 2411 drop(wm_hints.set(self.conn, window)); 2412 } 2413 2414 #[inline] 2415 fn get_icccm_window_name( 2416 &self, 2417 window: Window, 2418 ) -> String { 2419 static NO_NAME: &str = "n/a"; 2420 2421 self.conn 2422 .get_property( 2423 false, 2424 window, 2425 self.atoms.WM_NAME, 2426 self.atoms.UTF8_STRING, 2427 0, 2428 std::u32::MAX, 2429 ) 2430 .map_or(NO_NAME.to_owned(), |cookie| { 2431 cookie.reply().map_or(NO_NAME.to_owned(), |reply| { 2432 std::str::from_utf8( 2433 &reply 2434 .value8() 2435 .map_or(Vec::with_capacity(0), |value| value.collect::<Vec<u8>>()), 2436 ) 2437 .map_or(NO_NAME.to_owned(), |name| name.to_owned()) 2438 }) 2439 }) 2440 } 2441 2442 #[inline] 2443 fn get_icccm_window_class( 2444 &self, 2445 window: Window, 2446 ) -> String { 2447 static NO_CLASS: &str = "n/a"; 2448 2449 properties::WmClass::get(self.conn, window).map_or(NO_CLASS.to_owned(), |cookie| { 2450 cookie.reply().map_or(NO_CLASS.to_owned(), |reply| { 2451 std::str::from_utf8(reply.class()) 2452 .map_or(NO_CLASS.to_owned(), |class| class.to_owned()) 2453 }) 2454 }) 2455 } 2456 2457 #[inline] 2458 fn get_icccm_window_instance( 2459 &self, 2460 window: Window, 2461 ) -> String { 2462 static NO_INSTANCE: &str = "n/a"; 2463 2464 properties::WmClass::get(self.conn, window).map_or(NO_INSTANCE.to_owned(), |cookie| { 2465 cookie.reply().map_or(NO_INSTANCE.to_owned(), |reply| { 2466 std::str::from_utf8(reply.instance()) 2467 .map_or(NO_INSTANCE.to_owned(), |instance| instance.to_owned()) 2468 }) 2469 }) 2470 } 2471 2472 #[inline] 2473 fn get_icccm_window_transient_for( 2474 &self, 2475 window: Window, 2476 ) -> Option<Window> { 2477 self.conn 2478 .get_property( 2479 false, 2480 window, 2481 self.atoms.WM_TRANSIENT_FOR, 2482 self.atoms.WINDOW, 2483 0, 2484 std::u32::MAX, 2485 ) 2486 .ok()? 2487 .reply() 2488 .ok() 2489 .and_then(|transient_for| { 2490 let transient_for: Vec<u32> = transient_for.value32()?.collect(); 2491 2492 if transient_for.is_empty() { 2493 None 2494 } else { 2495 Some(transient_for[0]) 2496 } 2497 }) 2498 } 2499 2500 #[inline] 2501 fn get_icccm_window_client_leader( 2502 &self, 2503 window: Window, 2504 ) -> Option<Window> { 2505 self.conn 2506 .get_property( 2507 false, 2508 window, 2509 self.atoms.WM_CLIENT_LEADER, 2510 self.atoms.WINDOW, 2511 0, 2512 std::u32::MAX, 2513 ) 2514 .ok()? 2515 .reply() 2516 .ok() 2517 .and_then(|client_leader| { 2518 let client_leader: Vec<u32> = client_leader.value32()?.collect(); 2519 2520 if client_leader.is_empty() { 2521 None 2522 } else { 2523 Some(client_leader[0]) 2524 } 2525 }) 2526 } 2527 2528 #[inline] 2529 fn get_icccm_window_hints( 2530 &self, 2531 window: Window, 2532 ) -> Option<Hints> { 2533 let hints = properties::WmHints::get(self.conn, window) 2534 .ok()? 2535 .reply() 2536 .ok()?; 2537 2538 let urgent = hints.urgent; 2539 let input = hints.input; 2540 let group = hints.window_group; 2541 2542 let initial_state = hints.initial_state.map(|state| match state { 2543 properties::WmHintsState::Normal => IcccmWindowState::Normal, 2544 properties::WmHintsState::Iconic => IcccmWindowState::Iconic, 2545 }); 2546 2547 Some(Hints { 2548 urgent, 2549 input, 2550 initial_state, 2551 group, 2552 }) 2553 } 2554 2555 #[inline] 2556 fn get_icccm_window_size_hints( 2557 &self, 2558 window: Window, 2559 min_window_dim: Option<Dim>, 2560 current_size_hints: &Option<SizeHints>, 2561 ) -> (bool, Option<SizeHints>) { 2562 let size_hints = properties::WmSizeHints::get_normal_hints(self.conn, window) 2563 .ok() 2564 .and_then(|cookie| cookie.reply().ok()); 2565 2566 if size_hints.is_none() { 2567 return (!current_size_hints.is_none(), None); 2568 } 2569 2570 let size_hints = size_hints.unwrap(); 2571 2572 let (by_user, pos) = size_hints.position.map_or((false, None), |(spec, x, y)| { 2573 ( 2574 match spec { 2575 properties::WmSizeHintsSpecification::UserSpecified => true, 2576 _ => false, 2577 }, 2578 if x > 0 || y > 0 { 2579 Some(Pos { 2580 x, 2581 y, 2582 }) 2583 } else { 2584 None 2585 }, 2586 ) 2587 }); 2588 2589 let (sh_min_width, sh_min_height) = 2590 size_hints.min_size.map_or((None, None), |(width, height)| { 2591 ( 2592 if width > 0 { Some(width) } else { None }, 2593 if height > 0 { 2594 Some(height as i32) 2595 } else { 2596 None 2597 }, 2598 ) 2599 }); 2600 2601 let (sh_base_width, sh_base_height) = 2602 size_hints 2603 .base_size 2604 .map_or((None, None), |(width, height)| { 2605 ( 2606 if width > 0 { Some(width) } else { None }, 2607 if height > 0 { Some(height) } else { None }, 2608 ) 2609 }); 2610 2611 let (max_width, max_height) = 2612 size_hints.max_size.map_or((None, None), |(width, height)| { 2613 ( 2614 if width > 0 { Some(width) } else { None }, 2615 if height > 0 { Some(height) } else { None }, 2616 ) 2617 }); 2618 2619 let min_width = if sh_min_width.is_some() { 2620 sh_min_width 2621 } else { 2622 sh_base_width 2623 }; 2624 let min_height = if sh_min_height.is_some() { 2625 sh_min_height 2626 } else { 2627 sh_base_height 2628 }; 2629 2630 let base_width = if sh_base_width.is_some() { 2631 sh_base_width 2632 } else { 2633 sh_min_width 2634 }; 2635 let base_height = if sh_base_height.is_some() { 2636 sh_base_height 2637 } else { 2638 sh_min_height 2639 }; 2640 2641 let min_width = if let Some(min_width) = min_width { 2642 if let Some(min_window_dim) = min_window_dim { 2643 if min_width >= min_window_dim.w { 2644 Some(min_width) 2645 } else { 2646 Some(min_window_dim.w) 2647 } 2648 } else if min_width > 0 { 2649 Some(min_width) 2650 } else { 2651 None 2652 } 2653 } else { 2654 None 2655 }; 2656 2657 let min_height = if let Some(min_height) = min_height { 2658 if let Some(min_window_dim) = min_window_dim { 2659 if min_height >= min_window_dim.h { 2660 Some(min_height) 2661 } else { 2662 Some(min_window_dim.h) 2663 } 2664 } else if min_height > 0 { 2665 Some(min_height) 2666 } else { 2667 None 2668 } 2669 } else { 2670 None 2671 }; 2672 2673 let (inc_width, inc_height) = 2674 size_hints 2675 .size_increment 2676 .map_or((None, None), |(inc_width, inc_height)| { 2677 ( 2678 if inc_width > 0 && inc_width < 0xFFFF { 2679 Some(inc_width) 2680 } else { 2681 None 2682 }, 2683 if inc_height > 0 && inc_height < 0xFFFF { 2684 Some(inc_height) 2685 } else { 2686 None 2687 }, 2688 ) 2689 }); 2690 2691 let ((min_ratio, max_ratio), (min_ratio_vulgar, max_ratio_vulgar)) = size_hints 2692 .aspect 2693 .map_or(((None, None), (None, None)), |(min_ratio, max_ratio)| { 2694 ( 2695 ( 2696 if min_ratio.numerator > 0 && min_ratio.denominator > 0 { 2697 Some(min_ratio.numerator as f64 / min_ratio.denominator as f64) 2698 } else { 2699 None 2700 }, 2701 if max_ratio.numerator > 0 && max_ratio.denominator > 0 { 2702 Some(max_ratio.numerator as f64 / max_ratio.denominator as f64) 2703 } else { 2704 None 2705 }, 2706 ), 2707 ( 2708 Some(Ratio { 2709 numerator: min_ratio.numerator as i32, 2710 denominator: min_ratio.denominator as i32, 2711 }), 2712 Some(Ratio { 2713 numerator: max_ratio.numerator as i32, 2714 denominator: max_ratio.denominator as i32, 2715 }), 2716 ), 2717 ) 2718 }); 2719 2720 let size_hints = Some(SizeHints { 2721 by_user, 2722 pos, 2723 min_width, 2724 min_height, 2725 max_width, 2726 max_height, 2727 base_width, 2728 base_height, 2729 inc_width, 2730 inc_height, 2731 min_ratio, 2732 max_ratio, 2733 min_ratio_vulgar, 2734 max_ratio_vulgar, 2735 }); 2736 2737 (*current_size_hints != size_hints, size_hints) 2738 } 2739 2740 fn init_wm_properties( 2741 &self, 2742 wm_name: &str, 2743 desktop_names: &[&str], 2744 ) { 2745 let wm_instance_class_names = &[wm_name, wm_name]; 2746 let wm_class = wm_instance_class_names.join("\0"); 2747 2748 drop(self.conn.change_property8( 2749 xproto::PropMode::REPLACE, 2750 self.check_window, 2751 self.atoms._NET_WM_NAME, 2752 self.atoms.UTF8_STRING, 2753 wm_name.as_bytes(), 2754 )); 2755 2756 drop(self.conn.change_property8( 2757 xproto::PropMode::REPLACE, 2758 self.check_window, 2759 self.atoms.WM_CLASS, 2760 self.atoms.UTF8_STRING, 2761 wm_class.as_bytes(), 2762 )); 2763 2764 drop(self.conn.change_property32( 2765 xproto::PropMode::REPLACE, 2766 self.check_window, 2767 self.atoms._NET_WM_PID, 2768 self.atoms.CARDINAL, 2769 &[std::process::id() as u32], 2770 )); 2771 2772 drop(self.conn.change_property32( 2773 xproto::PropMode::REPLACE, 2774 self.screen.root, 2775 self.atoms._NET_SUPPORTING_WM_CHECK, 2776 self.atoms.WINDOW, 2777 &[self.check_window], 2778 )); 2779 2780 drop(self.conn.change_property8( 2781 xproto::PropMode::REPLACE, 2782 self.screen.root, 2783 self.atoms._NET_WM_NAME, 2784 self.atoms.UTF8_STRING, 2785 wm_name.as_bytes(), 2786 )); 2787 2788 drop(self.conn.change_property8( 2789 xproto::PropMode::REPLACE, 2790 self.screen.root, 2791 self.atoms.WM_CLASS, 2792 self.atoms.UTF8_STRING, 2793 wm_class.as_bytes(), 2794 )); 2795 2796 drop(self.conn.change_property32( 2797 xproto::PropMode::REPLACE, 2798 self.check_window, 2799 self.atoms._NET_SUPPORTING_WM_CHECK, 2800 self.atoms.WINDOW, 2801 &[self.check_window], 2802 )); 2803 2804 drop(self.conn.change_property32( 2805 xproto::PropMode::REPLACE, 2806 self.screen.root, 2807 self.atoms._NET_SUPPORTED, 2808 self.atoms.ATOM, 2809 &[ 2810 self.atoms._NET_ACTIVE_WINDOW, 2811 self.atoms._NET_CLIENT_LIST, 2812 self.atoms._NET_CLIENT_LIST_STACKING, 2813 self.atoms._NET_CLOSE_WINDOW, 2814 self.atoms._NET_CURRENT_DESKTOP, 2815 self.atoms._NET_DESKTOP_NAMES, 2816 self.atoms._NET_DESKTOP_VIEWPORT, 2817 self.atoms._NET_MOVERESIZE_WINDOW, 2818 self.atoms._NET_NUMBER_OF_DESKTOPS, 2819 self.atoms._NET_SUPPORTED, 2820 self.atoms._NET_SUPPORTING_WM_CHECK, 2821 self.atoms._NET_WM_DESKTOP, 2822 self.atoms._NET_MOVERESIZE_WINDOW, 2823 self.atoms._NET_WM_MOVERESIZE, 2824 self.atoms._NET_WM_NAME, 2825 self.atoms._NET_WM_STATE, 2826 self.atoms._NET_WM_STATE_DEMANDS_ATTENTION, 2827 self.atoms._NET_WM_STATE_FOCUSED, 2828 self.atoms._NET_WM_STATE_FULLSCREEN, 2829 self.atoms._NET_WM_STATE_HIDDEN, 2830 self.atoms._NET_WM_STATE_MODAL, 2831 self.atoms._NET_WM_STATE_STICKY, 2832 self.atoms._NET_WM_STRUT_PARTIAL, 2833 self.atoms._NET_WM_VISIBLE_NAME, 2834 self.atoms._NET_WM_WINDOW_TYPE, 2835 self.atoms._NET_WM_WINDOW_TYPE_DIALOG, 2836 self.atoms._NET_WM_WINDOW_TYPE_DOCK, 2837 self.atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU, 2838 self.atoms._NET_WM_WINDOW_TYPE_MENU, 2839 self.atoms._NET_WM_WINDOW_TYPE_NORMAL, 2840 self.atoms._NET_WM_WINDOW_TYPE_NOTIFICATION, 2841 self.atoms._NET_WM_WINDOW_TYPE_POPUP_MENU, 2842 self.atoms._NET_WM_WINDOW_TYPE_SPLASH, 2843 self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, 2844 self.atoms._NET_WM_WINDOW_TYPE_TOOLTIP, 2845 self.atoms._NET_WM_WINDOW_TYPE_UTILITY, 2846 ], 2847 )); 2848 2849 drop(self.conn.change_property32( 2850 xproto::PropMode::REPLACE, 2851 self.screen.root, 2852 self.atoms._NET_WM_PID, 2853 self.atoms.CARDINAL, 2854 &[std::process::id() as u32], 2855 )); 2856 2857 drop( 2858 self.conn 2859 .delete_property(self.screen.root, self.atoms._NET_CLIENT_LIST), 2860 ); 2861 2862 self.update_desktops(desktop_names); 2863 } 2864 2865 #[inline] 2866 fn set_current_desktop( 2867 &self, 2868 index: usize, 2869 ) { 2870 drop(self.conn.change_property32( 2871 xproto::PropMode::REPLACE, 2872 self.screen.root, 2873 self.atoms._NET_CURRENT_DESKTOP, 2874 self.atoms.CARDINAL, 2875 &[index as u32], 2876 )); 2877 } 2878 2879 #[inline] 2880 fn set_root_window_name( 2881 &self, 2882 name: &str, 2883 ) { 2884 drop(self.conn.change_property8( 2885 xproto::PropMode::REPLACE, 2886 self.screen.root, 2887 self.atoms.WM_NAME, 2888 self.atoms.UTF8_STRING, 2889 name.as_bytes(), 2890 )); 2891 } 2892 2893 #[inline] 2894 fn set_window_desktop( 2895 &self, 2896 window: Window, 2897 index: usize, 2898 ) { 2899 drop(self.conn.change_property32( 2900 xproto::PropMode::REPLACE, 2901 window, 2902 self.atoms._NET_WM_DESKTOP, 2903 self.atoms.CARDINAL, 2904 &[index as u32], 2905 )); 2906 } 2907 2908 #[inline] 2909 fn set_window_state( 2910 &self, 2911 window: Window, 2912 state: WindowState, 2913 on: bool, 2914 ) { 2915 self.set_window_state_atom( 2916 window, 2917 match state { 2918 WindowState::Modal => self.atoms._NET_WM_STATE_MODAL, 2919 WindowState::Sticky => self.atoms._NET_WM_STATE_STICKY, 2920 WindowState::MaximizedVert => self.atoms._NET_WM_STATE_MAXIMIZED_VERT, 2921 WindowState::MaximizedHorz => self.atoms._NET_WM_STATE_MAXIMIZED_HORZ, 2922 WindowState::Shaded => self.atoms._NET_WM_STATE_SHADED, 2923 WindowState::SkipTaskbar => self.atoms._NET_WM_STATE_SKIP_TASKBAR, 2924 WindowState::SkipPager => self.atoms._NET_WM_STATE_SKIP_PAGER, 2925 WindowState::Hidden => self.atoms._NET_WM_STATE_HIDDEN, 2926 WindowState::Fullscreen => self.atoms._NET_WM_STATE_FULLSCREEN, 2927 WindowState::Above => self.atoms._NET_WM_STATE_ABOVE, 2928 WindowState::Below => self.atoms._NET_WM_STATE_BELOW, 2929 WindowState::DemandsAttention => self.atoms._NET_WM_STATE_DEMANDS_ATTENTION, 2930 }, 2931 on, 2932 ); 2933 } 2934 2935 #[inline] 2936 fn set_window_frame_extents( 2937 &self, 2938 window: Window, 2939 extents: Extents, 2940 ) { 2941 let mut frame_extents: Vec<u32> = Vec::with_capacity(4); 2942 2943 frame_extents.push(extents.left as u32); 2944 frame_extents.push(extents.right as u32); 2945 frame_extents.push(extents.top as u32); 2946 frame_extents.push(extents.bottom as u32); 2947 2948 drop(self.conn.change_property32( 2949 xproto::PropMode::REPLACE, 2950 window, 2951 self.atoms._NET_FRAME_EXTENTS, 2952 self.atoms.CARDINAL, 2953 &frame_extents[..], 2954 )); 2955 } 2956 2957 #[inline] 2958 fn set_desktop_geometry( 2959 &self, 2960 geometries: &[&Region], 2961 ) { 2962 let mut areas = Vec::with_capacity(geometries.len()); 2963 2964 geometries.iter().for_each(|geometry| { 2965 areas.push(geometry.pos.x as u32); 2966 areas.push(geometry.pos.y as u32); 2967 areas.push(geometry.dim.w as u32); 2968 areas.push(geometry.dim.h as u32); 2969 }); 2970 2971 drop(self.conn.change_property32( 2972 xproto::PropMode::REPLACE, 2973 self.screen.root, 2974 self.atoms._NET_DESKTOP_GEOMETRY, 2975 self.atoms.CARDINAL, 2976 &areas[..], 2977 )); 2978 } 2979 2980 #[inline] 2981 fn set_desktop_viewport( 2982 &self, 2983 viewports: &[&Region], 2984 ) { 2985 let mut areas = Vec::with_capacity(viewports.len()); 2986 2987 viewports.iter().for_each(|viewport| { 2988 areas.push(viewport.pos.x as u32); 2989 areas.push(viewport.pos.y as u32); 2990 areas.push(viewport.dim.w as u32); 2991 areas.push(viewport.dim.h as u32); 2992 }); 2993 2994 drop(self.conn.change_property32( 2995 xproto::PropMode::REPLACE, 2996 self.screen.root, 2997 self.atoms._NET_DESKTOP_VIEWPORT, 2998 self.atoms.CARDINAL, 2999 &areas[..], 3000 )); 3001 } 3002 3003 #[inline] 3004 fn set_workarea( 3005 &self, 3006 workareas: &[&Region], 3007 ) { 3008 let mut areas = Vec::with_capacity(workareas.len()); 3009 3010 workareas.iter().for_each(|workarea| { 3011 areas.push(workarea.pos.x as u32); 3012 areas.push(workarea.pos.y as u32); 3013 areas.push(workarea.dim.w as u32); 3014 areas.push(workarea.dim.h as u32); 3015 }); 3016 3017 drop(self.conn.change_property32( 3018 xproto::PropMode::REPLACE, 3019 self.screen.root, 3020 self.atoms._NET_WORKAREA, 3021 self.atoms.CARDINAL, 3022 &areas[..], 3023 )); 3024 } 3025 3026 #[inline] 3027 fn update_desktops( 3028 &self, 3029 desktop_names: &[&str], 3030 ) { 3031 drop(self.conn.change_property32( 3032 xproto::PropMode::REPLACE, 3033 self.screen.root, 3034 self.atoms._NET_NUMBER_OF_DESKTOPS, 3035 self.atoms.CARDINAL, 3036 &[desktop_names.len() as u32], 3037 )); 3038 3039 drop(self.conn.change_property8( 3040 xproto::PropMode::REPLACE, 3041 self.screen.root, 3042 self.atoms._NET_DESKTOP_NAMES, 3043 self.atoms.UTF8_STRING, 3044 desktop_names.join("\0").as_bytes(), 3045 )); 3046 } 3047 3048 #[inline] 3049 fn update_client_list( 3050 &self, 3051 clients: &[Window], 3052 ) { 3053 drop(self.conn.change_property32( 3054 xproto::PropMode::REPLACE, 3055 self.screen.root, 3056 self.atoms._NET_CLIENT_LIST, 3057 self.atoms.WINDOW, 3058 clients, 3059 )); 3060 } 3061 3062 #[inline] 3063 fn update_client_list_stacking( 3064 &self, 3065 clients: &[Window], 3066 ) { 3067 drop(self.conn.change_property32( 3068 xproto::PropMode::REPLACE, 3069 self.screen.root, 3070 self.atoms._NET_CLIENT_LIST_STACKING, 3071 self.atoms.WINDOW, 3072 clients, 3073 )); 3074 } 3075 3076 #[inline] 3077 fn get_window_strut( 3078 &self, 3079 window: Window, 3080 ) -> Option<Vec<Option<Strut>>> { 3081 if let Some(strut) = self.get_window_strut_partial(window) { 3082 return Some(strut); 3083 } 3084 3085 self.conn 3086 .get_property( 3087 false, 3088 window, 3089 self.atoms._NET_WM_STRUT, 3090 self.atoms.CARDINAL, 3091 0, 3092 std::u32::MAX, 3093 ) 3094 .ok()? 3095 .reply() 3096 .ok() 3097 .and_then(|strut| { 3098 let widths: Vec<u32> = strut.value32()?.collect(); 3099 3100 if widths.is_empty() { 3101 return None; 3102 } 3103 3104 let mut struts = Vec::with_capacity(4); 3105 3106 for (i, &width) in widths.iter().enumerate() { 3107 if i == 4 { 3108 break; 3109 } 3110 3111 struts.push(if width != 0 { 3112 Some(Strut { 3113 window, 3114 width, 3115 }) 3116 } else { 3117 None 3118 }); 3119 } 3120 3121 Some(struts) 3122 }) 3123 } 3124 3125 #[inline] 3126 fn get_window_strut_partial( 3127 &self, 3128 window: Window, 3129 ) -> Option<Vec<Option<Strut>>> { 3130 self.conn 3131 .get_property( 3132 false, 3133 window, 3134 self.atoms._NET_WM_STRUT_PARTIAL, 3135 self.atoms.CARDINAL, 3136 0, 3137 std::u32::MAX, 3138 ) 3139 .ok()? 3140 .reply() 3141 .ok() 3142 .and_then(|strut_partial| { 3143 let widths: Vec<u32> = strut_partial.value32()?.collect(); 3144 3145 if widths.is_empty() { 3146 return None; 3147 } 3148 3149 let mut struts = Vec::with_capacity(1); 3150 3151 for (i, &width) in widths.iter().enumerate() { 3152 if i == 4 { 3153 break; 3154 } 3155 3156 struts.push(if width != 0 { 3157 Some(Strut { 3158 window, 3159 width, 3160 }) 3161 } else { 3162 None 3163 }); 3164 } 3165 3166 Some(struts) 3167 }) 3168 } 3169 3170 #[inline] 3171 fn get_window_desktop( 3172 &self, 3173 window: Window, 3174 ) -> Option<usize> { 3175 self.conn 3176 .get_property( 3177 false, 3178 window, 3179 self.atoms._NET_WM_DESKTOP, 3180 self.atoms.CARDINAL, 3181 0, 3182 std::u32::MAX, 3183 ) 3184 .ok()? 3185 .reply() 3186 .ok() 3187 .and_then(|desktop| { 3188 let desktop: Vec<u32> = desktop.value32()?.collect(); 3189 3190 if desktop.is_empty() { 3191 None 3192 } else { 3193 Some(desktop[0] as usize) 3194 } 3195 }) 3196 } 3197 3198 #[inline] 3199 fn get_window_preferred_type( 3200 &self, 3201 window: Window, 3202 ) -> WindowType { 3203 self.get_window_types(window) 3204 .get(0) 3205 .map_or(WindowType::Normal, |&type_| type_) 3206 } 3207 3208 fn get_window_types( 3209 &self, 3210 window: Window, 3211 ) -> Vec<WindowType> { 3212 let mut window_types = Vec::new(); 3213 3214 drop( 3215 self.conn 3216 .get_property( 3217 false, 3218 window, 3219 self.atoms._NET_WM_WINDOW_TYPE, 3220 self.atoms.ATOM, 3221 0, 3222 std::u32::MAX, 3223 ) 3224 .ok() 3225 .and_then(|cookie| cookie.reply().ok()) 3226 .map(|types| { 3227 let types: Vec<u32> = types 3228 .value32() 3229 .map_or(Vec::with_capacity(0), |value| value.collect()); 3230 3231 for type_ in types { 3232 if let Some(type_) = self.get_window_type_from_atom(type_) { 3233 window_types.push(type_); 3234 } 3235 } 3236 }), 3237 ); 3238 3239 window_types 3240 } 3241 3242 #[inline] 3243 fn get_window_preferred_state( 3244 &self, 3245 window: Window, 3246 ) -> Option<WindowState> { 3247 self.get_window_states(window).get(0).copied() 3248 } 3249 3250 fn get_window_states( 3251 &self, 3252 window: Window, 3253 ) -> Vec<WindowState> { 3254 let mut window_states = Vec::new(); 3255 3256 drop( 3257 self.conn 3258 .get_property( 3259 false, 3260 window, 3261 self.atoms._NET_WM_STATE, 3262 self.atoms.ATOM, 3263 0, 3264 std::u32::MAX, 3265 ) 3266 .ok() 3267 .and_then(|cookie| cookie.reply().ok()) 3268 .map(|states| { 3269 let states: Vec<u32> = states 3270 .value32() 3271 .map_or(Vec::with_capacity(0), |value| value.collect()); 3272 3273 for state in states { 3274 if let Some(state) = self.get_window_state_from_atom(state) { 3275 window_states.push(state); 3276 } 3277 } 3278 }), 3279 ); 3280 3281 window_states 3282 } 3283 3284 #[inline] 3285 fn window_is_fullscreen( 3286 &self, 3287 window: Window, 3288 ) -> bool { 3289 self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_FULLSCREEN]) 3290 } 3291 3292 #[inline] 3293 fn window_is_above( 3294 &self, 3295 window: Window, 3296 ) -> bool { 3297 self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_ABOVE]) 3298 } 3299 3300 #[inline] 3301 fn window_is_below( 3302 &self, 3303 window: Window, 3304 ) -> bool { 3305 self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_BELOW]) 3306 } 3307 3308 #[inline] 3309 fn window_is_sticky( 3310 &self, 3311 window: Window, 3312 ) -> bool { 3313 self.window_is_any_of_states(window, &[self.atoms._NET_WM_STATE_STICKY]) 3314 || self.get_window_desktop(window) == Some(0xFFFFFFFF) 3315 } 3316 }