diff --git a/Cargo.toml b/Cargo.toml index 49baef0..630cedd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ homepage = "https://git.friedl.net/incubator/xcb-wm" [dependencies] xcb = "1" paste = "1" +bitflags = "1.3.2" [features] icccm = [] diff --git a/src/ewmh/connection.rs b/src/ewmh/connection.rs index dd1c70e..f49b6fd 100644 --- a/src/ewmh/connection.rs +++ b/src/ewmh/connection.rs @@ -112,17 +112,6 @@ mod tests { println!("{:?}", reply); } - #[test] - fn set_number_of_desktops() { - let xcb_con = xcb::Connection::connect(Option::None).unwrap().0; - let ewmh_con = crate::ewmh::Connection::connect(&xcb_con); - - let request = crate::ewmh::proto::SetNumberOfDesktops::new(&ewmh_con, 4); - let cookie = ewmh_con.send_request_checked(&request); - let reply = xcb_con.check_request(cookie); - println!("{:?}", reply); - } - // #[test] // fn number_of_desktops2() { // let xcb_con = xcb::Connection::connect(Option::None).unwrap().0; diff --git a/src/icccm/connection.rs b/src/icccm/connection.rs index d24d956..dc58818 100644 --- a/src/icccm/connection.rs +++ b/src/icccm/connection.rs @@ -76,7 +76,7 @@ impl<'a> Connection<'a> { #[cfg(test)] mod tests { - use crate::icccm::proto::SetWmName; + use crate::icccm::proto::{GetWmNormalHints, SetWmName, SetWmNormalHints, WmSizeHints}; #[test] fn number_of_desktops() { @@ -108,4 +108,19 @@ mod tests { let reply = xcb_con.check_request(cookie); println!("{:?}", reply); } + + #[test] + fn get_wm_normal_hints() { + let xcb_con = xcb::Connection::connect(Option::None).unwrap().0; + let icccm_con = crate::icccm::Connection::connect(&xcb_con); + + use xcb::XidNew; + + let window = unsafe { xcb::x::Window::new(0x440013e) }; + + let request = GetWmNormalHints::new(window); + let cookie = icccm_con.send_request(&request); + let reply = icccm_con.wait_for_reply(cookie); + println!("{:?}", reply); + } } diff --git a/src/icccm/proto/client_props.rs b/src/icccm/proto/client_props.rs index 78fda87..119814a 100644 --- a/src/icccm/proto/client_props.rs +++ b/src/icccm/proto/client_props.rs @@ -4,6 +4,8 @@ #![allow(dead_code)] +use bitflags::bitflags; +use std::convert::TryInto; use xcb::{Xid, XidNew}; use crate::icccm::traits::*; @@ -257,3 +259,225 @@ icccm_set_property! { } } // }}} + +// WM_NORMAL_HINTS, +// {{{ + +bitflags! { + struct WmSizeHintsFlags: u32 { + const Empty = 0b0000_0000_0000; + const USPosition = 0b0000_0000_0001; + const USSize = 0b0000_0000_0010; + const PPosition = 0b0000_0000_0100; + const PSize = 0b0000_0000_1000; + const PMinSize = 0b0000_0001_0000; + const PMaxSize = 0b0000_0010_0000; + const PResizeInc = 0b0000_0100_0000; + const PAspect = 0b0000_1000_0000; + const PBaseSize = 0b0001_0000_0000; + const PWinGravity = 0b0010_0000_0000; + } +} + +#[derive(Debug)] +pub struct WmSizeHints { + flags: WmSizeHintsFlags, + x: u32, + y: u32, + width: u32, + height: u32, + min_width: u32, + min_height: u32, + max_width: u32, + max_height: u32, + width_inc: u32, + height_inc: u32, + min_aspect: (u32, u32), + max_aspect: (u32, u32), + base_width: u32, + base_height: u32, + win_gravity: xcb::x::Gravity, +} + +impl Default for WmSizeHints { + fn default() -> Self { + WmSizeHints { + flags: WmSizeHintsFlags::Empty, + x: 0, + y: 0, + width: 0, + height: 0, + min_width: 0, + min_height: 0, + max_width: 0, + max_height: 0, + width_inc: 0, + height_inc: 0, + min_aspect: (0, 0), + max_aspect: (0, 0), + base_width: 0, + base_height: 0, + win_gravity: xcb::x::Gravity::NorthWest, + } + } +} + +impl WmSizeHints { + pub fn as_data(&mut self) -> Vec { + vec![ + self.flags.bits, + self.x, + self.y, + self.width, + self.height, + self.min_width, + self.min_height, + self.max_width, + self.max_height, + self.width_inc, + self.height_inc, + self.min_aspect.0, + self.min_aspect.1, + self.max_aspect.0, + self.max_aspect.1, + self.base_width, + self.base_height, + self.win_gravity as u32, + ] + } + + pub fn from_reply(reply: xcb::x::GetPropertyReply) -> WmSizeHints { + let packed_vals = reply.value::(); + WmSizeHints { + flags: WmSizeHintsFlags::from_bits_truncate(packed_vals[0]), + x: packed_vals[1], + y: packed_vals[2], + width: packed_vals[3], + height: packed_vals[4], + min_width: packed_vals[5], + min_height: packed_vals[6], + max_width: packed_vals[7], + max_height: packed_vals[8], + width_inc: packed_vals[9], + height_inc: packed_vals[10], + min_aspect: (packed_vals[11], packed_vals[12]), + max_aspect: (packed_vals[13], packed_vals[14]), + base_width: packed_vals[15], + base_height: packed_vals[16], + win_gravity: unsafe { std::mem::transmute(packed_vals[17]) }, + } + } + + pub fn position(&mut self, user_specified: bool, x: u32, y: u32) { + // reset + self.flags &= !(WmSizeHintsFlags::USPosition | WmSizeHintsFlags::PPosition); + + if user_specified { + self.flags |= WmSizeHintsFlags::USPosition + } else { + self.flags |= WmSizeHintsFlags::PPosition + } + + self.x = x; + self.y = y; + } + + pub fn size(&mut self, user_specified: bool, width: u32, height: u32) { + // reset + self.flags &= !(WmSizeHintsFlags::USSize | WmSizeHintsFlags::PSize); + + if user_specified { + self.flags |= WmSizeHintsFlags::USSize + } else { + self.flags |= WmSizeHintsFlags::PSize + } + + self.width = width; + self.height = height; + } + + pub fn min_size(&mut self, min_width: u32, min_height: u32) { + self.flags |= WmSizeHintsFlags::PMinSize; + + self.min_width = min_width; + self.min_height = min_height; + } + + pub fn max_size(&mut self, max_width: u32, max_height: u32) { + self.flags |= WmSizeHintsFlags::PMaxSize; + + self.max_width = max_width; + self.max_height = max_height; + } + + pub fn resize_inc(&mut self, width_inc: u32, height_inc: u32) { + self.flags |= WmSizeHintsFlags::PResizeInc; + + self.width_inc = width_inc; + self.height_inc = height_inc; + } + + pub fn aspect(&mut self, min_aspect: (u32, u32), max_aspect: (u32, u32)) { + self.flags |= WmSizeHintsFlags::PAspect; + + self.min_aspect = min_aspect; + self.max_aspect = max_aspect; + } + + pub fn base_size(&mut self, base_width: u32, base_height: u32) { + self.flags |= WmSizeHintsFlags::PBaseSize; + + self.base_width = base_width; + self.base_height = base_height; + } + + pub fn win_gravity(&mut self, win_gravity: xcb::x::Gravity) { + self.flags |= WmSizeHintsFlags::PWinGravity; + + self.win_gravity = win_gravity; + } +} + +#[derive(Debug)] +pub struct GetWmNormalHintsReply { + pub size_hints: WmSizeHints, +} + +impl From for GetWmNormalHintsReply { + fn from(reply: xcb::x::GetPropertyReply) -> Self { + GetWmNormalHintsReply { + size_hints: WmSizeHints::from_reply(reply), + } + } +} + +icccm_get_property! { + request=GetWmNormalHints{ + window: client, + property: ATOM_WM_NORMAL_HINTS, + xtype: ATOM_WM_SIZE_HINTS + }, + reply=GetWmNormalHintsReply +} + +pub struct SetWmNormalHints { + window: xcb::x::Window, + data: Vec, +} + +impl SetWmNormalHints { + pub fn new(window: xcb::x::Window, size_hints: &mut WmSizeHints) -> SetWmNormalHints { + SetWmNormalHints { + window: window, + data: size_hints.as_data(), + } + } +} + +icccm_set_hint_property! { + request=SetWmNormalHints{ + property: ATOM_WM_NORMAL_HINTS, + xtype: ATOM_WM_SIZE_HINTS + } +} +// }}} diff --git a/src/icccm/proto/macros/set_property.rs b/src/icccm/proto/macros/set_property.rs index cb47c7c..4e9bba9 100644 --- a/src/icccm/proto/macros/set_property.rs +++ b/src/icccm/proto/macros/set_property.rs @@ -126,3 +126,43 @@ macro_rules! icccm_set_property { } }; } + +macro_rules! icccm_set_hint_property { + (request=$request:ident{ + property: $property:ident, + xtype: $type:ident + }) => { + impl<'a> IcccmRequest<'a> for $request { + type XcbRequest = xcb::x::ChangeProperty<'a, u32>; + type IcccmCookie = xcb::VoidCookie; + + fn xcb_request(&'a self, con: &Connection) -> xcb::x::ChangeProperty<'a, u32> { + xcb::x::ChangeProperty { + mode: xcb::x::PropMode::Replace, + window: self.window, + property: xcb::x::$property, + r#type: xcb::x::$type, + data: &self.data, + } + } + + fn convert_cookie(&'a self, xcb_cookie: xcb::VoidCookie) -> Self::IcccmCookie { + xcb_cookie + } + } + + impl<'a> IcccmVoidRequestChecked<'a> for $request { + type XcbRequest = xcb::x::ChangeProperty<'a, u32>; + + fn xcb_request(&'a self, con: &Connection) -> xcb::x::ChangeProperty<'a, u32> { + xcb::x::ChangeProperty { + mode: xcb::x::PropMode::Replace, + window: self.window, + property: xcb::x::$property, + r#type: xcb::x::$type, + data: &self.data, + } + } + } + }; +}