wzrd

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

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 }