From 960663d476d8a1391b8bcc4424f3f716147068ab Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Wed, 11 Nov 2026 21:03:30 -0700 Subject: all the xcb wrapper types are in individual files now the trait inheritance structure isn't THAT convoluted, but having more than one of these in a file was obscuring things. Change-Id: I5132a6304796654d778d731be1ecb1c7b4e77120 Force-Push: yes --- src/main.rs | 24 ++--- src/x.rs | 13 +++ src/x/atoms.rs | 12 +++ src/x/color.rs | 49 +++++++++ src/x/connection.rs | 97 ++++++++++++++++++ src/x/context.rs | 44 +++++++++ src/x/display.rs | 56 +++++++++++ src/x/window.rs | 134 +++++++++++++++++++++++++ src/xcolor.rs | 46 --------- src/xcontext.rs | 44 --------- src/xtypes.rs | 280 ---------------------------------------------------- 11 files changed, 414 insertions(+), 385 deletions(-) create mode 100644 src/x.rs create mode 100644 src/x/atoms.rs create mode 100644 src/x/color.rs create mode 100644 src/x/connection.rs create mode 100644 src/x/context.rs create mode 100644 src/x/display.rs create mode 100644 src/x/window.rs delete mode 100644 src/xcolor.rs delete mode 100644 src/xcontext.rs delete mode 100644 src/xtypes.rs diff --git a/src/main.rs b/src/main.rs index 57eabf0..51cb7bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,11 @@ -use crate::xtypes::*; -use crate::xcolor::DisplayColorExt; -use crate::xcontext::GraphicContext; +use crate::x::*; use std::sync::Arc; use xcb::{ Xid }; use xcb::x::{ - self, ClientMessageData, Cw, CreateWindow, - CreateGc, FreeGc, Drawable, EventMask, PolyFillRectangle, @@ -17,9 +13,7 @@ use xcb::x::{ WindowClass }; -mod xcolor; -mod xcontext; -mod xtypes; +mod x; #[derive(Clone, Copy, PartialEq, Eq)] @@ -66,15 +60,15 @@ fn main() -> xcb::Result<()> { loop { match display.wait_for_event()? { - xcb::Event::X(x::Event::Expose(_event)) => { + xcb::Event::X(xcb::x::Event::Expose(_event)) => { let _ = dock.redraw(); // TODO } - xcb::Event::X(x::Event::ButtonPress(event)) => { + xcb::Event::X(xcb::x::Event::ButtonPress(event)) => { println!("click {:?}", event); } - xcb::Event::X(x::Event::ClientMessage(event)) => { + xcb::Event::X(xcb::x::Event::ClientMessage(event)) => { if let ClientMessageData::Data32([atom, ..]) = event.data() { if atom == display.atoms().wm_delete_window.resource_id() { break; @@ -99,8 +93,8 @@ impl Dock { let dock_bounds = Dock::compute_bounds(&screen, &configuration); let window = Window::new(Arc::clone(&display), &mut CreateWindow { - depth: x::COPY_FROM_PARENT as u8, - wid: x::Window::none(), + depth: xcb::x::COPY_FROM_PARENT as u8, + wid: xcb::x::Window::none(), parent: screen.root(), x: dock_bounds.left as i16, y: dock_bounds.top as i16, @@ -116,7 +110,7 @@ impl Dock { })?; let dock = Dock { window, configuration, bounds: Arc::new(dock_bounds) }; - dock.set_string_property(&x::ATOM_WM_NAME, b"sandbar")?; + dock.set_string_property(&xcb::x::ATOM_WM_NAME, b"sandbar")?; dock.set_atom_property(&dock.atoms().wm_protocols, &[dock.atoms().wm_delete_window])?; @@ -221,7 +215,7 @@ impl Dock { let cookie = connection.send_request_checked(&PolyFillRectangle { drawable: Drawable::Window(self.window_id()), property: *property, - r#type: x::ATOM_CARDINAL, + r#type: xcb::x::ATOM_CARDINAL, data: values, }); connection.check_request(cookie)?; diff --git a/src/x.rs b/src/x.rs new file mode 100644 index 0000000..dcef69b --- /dev/null +++ b/src/x.rs @@ -0,0 +1,13 @@ +pub use color::DisplayColorExt; +pub use context::GraphicContext; +pub use connection::{ Connection, HasConnection, ConnectionExt }; +pub use display::{ Display, HasDisplay, DisplayExt }; +pub use window::{ Window, HasWindow, WindowExt }; + +pub mod atoms; +pub mod color; +pub mod connection; +pub mod context; +pub mod display; +pub mod window; + diff --git a/src/x/atoms.rs b/src/x/atoms.rs new file mode 100644 index 0000000..9f489a3 --- /dev/null +++ b/src/x/atoms.rs @@ -0,0 +1,12 @@ +use xcb::x::Atom; + + +pub struct Atoms { + pub wm_protocols: Atom, + pub wm_delete_window: Atom, + pub wm_type: Atom, + pub wm_type_dock: Atom, + pub wm_strut: Atom, + pub wm_strut_partial: Atom, +} + diff --git a/src/x/color.rs b/src/x/color.rs new file mode 100644 index 0000000..10ad651 --- /dev/null +++ b/src/x/color.rs @@ -0,0 +1,49 @@ +/* Color management in X has become an obscure topic because everyone prefers + * to assume they have 24-bit direct. In dealing with it, [1] has been an + * important resource. + * + * [1] https://xcb.freedesktop.org/xlibtoxcbtranslationguide/ + */ +use crate::x::connection::*; +use crate::x::display::*; + +use xcb::x::{ AllocColor, Visualtype }; + + +pub trait DisplayColorExt: DisplayExt { + /* This is sufficiently obscure knowledge that it's worth keeping here + * even though it's unused. + */ + #[allow(dead_code)] + fn find_root_visual(&self) -> Option<&Visualtype> { + let visual_id = self.screen().root_visual(); + + for depth in self.screen().allowed_depths() { + for visual in depth.visuals() { + if visual_id == visual.visual_id() { + return Some(visual); + } + } + } + + None + } + + fn allocate_color(&self, red: u16, green: u16, blue: u16) + -> xcb::Result + { + let connection = self.connection(); + + let colormap = self.screen().default_colormap(); + + let cookie = connection.send_request(&AllocColor { + cmap: colormap, + red, green, blue, + }); + + Ok(connection.wait_for_reply(cookie)?.pixel()) + } +} + +impl DisplayColorExt for T { } + diff --git a/src/x/connection.rs b/src/x/connection.rs new file mode 100644 index 0000000..d9ed9ca --- /dev/null +++ b/src/x/connection.rs @@ -0,0 +1,97 @@ +use crate::x::atoms::Atoms; + +use xcb::XidNew; +use xcb::x::{ Atom, InternAtom }; + + +pub struct Connection(xcb::Connection); + +impl Connection { + pub fn connect(display_name: Option<&str>) + -> xcb::ConnResult<(Connection, i32)> + { + let (connection, screen_index) + = xcb::Connection::connect(display_name)?; + + Ok((Connection(connection), screen_index)) + } +} + + +pub trait HasConnection { + fn connection(&self) -> &Connection; +} + +impl HasConnection for Connection { + fn connection(&self) -> &Connection { self } +} + + +pub trait ConnectionExt: HasConnection { + fn get_setup(&self) -> &xcb::x::Setup { + self.connection().0.get_setup() + } + + fn send_request(&self, request: &R) -> R::Cookie { + self.connection().0.send_request(request) + } + + fn wait_for_reply(&self, cookie: C) + -> xcb::Result + { + self.connection().0.wait_for_reply(cookie) + } + + fn send_request_checked(&self, request: &R) + -> xcb::VoidCookieChecked + { + self.connection().0.send_request_checked(request) + } + + fn check_request(&self, cookie: xcb::VoidCookieChecked) + -> xcb::ProtocolResult<()> + { + self.connection().0.check_request(cookie) + } + + fn wait_for_event(&self) -> xcb::Result { + self.connection().0.wait_for_event() + } + + fn generate_id(&self) -> T { + let connection = self.connection(); + + connection.0.generate_id() + } + + fn get_atom(&self, name: &[u8]) -> xcb::Result { + let connection = self.connection(); + + let cookie = connection.send_request(&InternAtom { + only_if_exists: true, + name: name, + }); + + Ok(connection.wait_for_reply(cookie)?.atom()) + } + + fn get_atoms(&self) -> xcb::Result { + let wm_protocols = self.get_atom(b"WM_PROTOCOLS")?; + let wm_delete_window = self.get_atom(b"WM_DELETE_WINDOW")?; + + let wm_type = self.get_atom(b"_NET_WM_WINDOW_TYPE")?; + let wm_type_dock = self.get_atom(b"_NET_WM_WINDOW_TYPE_DOCK")?; + + let wm_strut = self.get_atom(b"_NET_WM_STRUT")?; + let wm_strut_partial = self.get_atom(b"_NET_WM_STRUT_PARTIAL")?; + + Ok(Atoms { + wm_protocols, wm_delete_window, + wm_type, wm_type_dock, + wm_strut, wm_strut_partial, + }) + } +} + +impl ConnectionExt for T { } + diff --git a/src/x/context.rs b/src/x/context.rs new file mode 100644 index 0000000..0a8d11e --- /dev/null +++ b/src/x/context.rs @@ -0,0 +1,44 @@ +use crate::x::*; + +use std::sync::Arc; + +use xcb::x::{ self, Drawable, CreateGc, FreeGc }; + + +pub struct GraphicContext { + display: Arc, + context_id: x::Gcontext, +} + + +impl GraphicContext { + pub fn new(display: Arc, drawable: Drawable) + -> xcb::Result + { + let context_id: x::Gcontext = display.generate_id(); + let cookie = display.send_request_checked(&CreateGc { + cid: context_id, + drawable, + value_list: &[], + }); + display.check_request(cookie)?; + + Ok(GraphicContext { display, context_id }) + } +} + +impl Drop for GraphicContext { + fn drop(&mut self) { + let cookie = self.send_request_checked(&FreeGc { gc: self.context_id }); + let _ = self.check_request(cookie); // TODO + } +} + +impl HasDisplay for GraphicContext { + fn display(&self) -> &Arc { &self.display } +} + +impl HasConnection for GraphicContext { + fn connection(&self) -> &Connection { self.display.connection() } +} + diff --git a/src/x/display.rs b/src/x/display.rs new file mode 100644 index 0000000..ca552db --- /dev/null +++ b/src/x/display.rs @@ -0,0 +1,56 @@ +use crate::x::atoms::Atoms; +use crate::x::connection::*; + +use std::sync::Arc; + +use xcb::x::Screen; + + +pub struct Display { + connection: Connection, + screen_index: i32, + atoms: Atoms, +} + +impl HasConnection for Arc { + fn connection(&self) -> &Connection { &self.connection } +} + + +impl Display { + pub fn new() -> xcb::Result { + let (connection, screen_index) = Connection::connect(None)?; + + let atoms = connection.get_atoms()?; + + Ok(Display { connection, screen_index, atoms }) + } + + pub fn screen_index(&self) -> i32 { + self.screen_index + } +} + + +pub trait HasDisplay { + fn display(&self) -> &Arc; + + fn atoms(&self) -> &Atoms { &self.display().atoms } +} + +impl HasDisplay for Arc { + fn display(&self) -> &Arc { self } +} + + +pub trait DisplayExt: HasDisplay + HasConnection { + fn screen(&self) -> &Screen { + let setup = self.connection().get_setup(); + let screen_index = self.display().screen_index() as usize; + + &setup.roots().nth(screen_index).unwrap() + } +} + +impl DisplayExt for T { } + diff --git a/src/x/window.rs b/src/x/window.rs new file mode 100644 index 0000000..d264eb9 --- /dev/null +++ b/src/x/window.rs @@ -0,0 +1,134 @@ +use crate::x::*; + +use std::sync::Arc; +use std::ops::Deref; + +use xcb::x::{ + self, + Atom, + ChangeProperty, + CreateWindow, + MapWindow, + PropMode, +}; + + +pub struct Window { + display: Arc, + window_id: x::Window, +} + + + +impl Window { + pub fn new(display: Arc, create_window: &mut CreateWindow) + -> xcb::Result + { + let connection = display.connection(); + + let window_id: x::Window = connection.generate_id(); + + create_window.wid = window_id; + let cookie = connection.send_request_checked(create_window); + connection.check_request(cookie)?; + + Ok(Window { display, window_id }) + } +} + + +pub trait HasWindow { + fn window(&self) -> &Window; + + fn window_id(&self) -> x::Window { self.window().window_id } +} + +impl HasWindow for Window { + fn window(&self) -> &Window { self } +} + +impl> HasWindow for T { + fn window(&self) -> &Window { &*self } +} + + +impl HasConnection for Window { + fn connection(&self) -> &Connection { self.display.connection() } +} + + +impl HasDisplay for Window { + fn display(&self) -> &Arc { &self.display } +} + + + + +pub trait WindowExt: HasWindow + HasDisplay + HasConnection { + fn map_window(&self) -> xcb::Result<()> { + let connection = self.connection(); + + let cookie = connection.send_request_checked(&MapWindow { + window: self.window_id() + }); + connection.check_request(cookie)?; + + Ok(()) + } + + fn set_string_property(&self, property: &Atom, value: &[u8]) + -> xcb::Result<()> + { + let connection = self.connection(); + + let cookie = connection.send_request_checked(&ChangeProperty { + mode: PropMode::Replace, + window: self.window_id(), + property: *property, + r#type: x::ATOM_STRING, + data: value, + }); + connection.check_request(cookie)?; + + Ok(()) + } + + + fn set_atom_property(&self, property: &Atom, values: &[Atom]) + -> xcb::Result<()> + { + let connection = self.connection(); + + let cookie = connection.send_request_checked(&ChangeProperty { + mode: PropMode::Replace, + window: self.window_id(), + property: *property, + r#type: x::ATOM_ATOM, + data: values, + }); + connection.check_request(cookie)?; + + Ok(()) + } + + + fn set_cardinal_property(&self, property: &Atom, values: &[u32]) + -> xcb::Result<()> + { + let connection = self.connection(); + + let cookie = connection.send_request_checked(&ChangeProperty { + mode: PropMode::Replace, + window: self.window_id(), + property: *property, + r#type: x::ATOM_CARDINAL, + data: values, + }); + connection.check_request(cookie)?; + + Ok(()) + } +} + +impl WindowExt for T { } + diff --git a/src/xcolor.rs b/src/xcolor.rs deleted file mode 100644 index 0fab0c8..0000000 --- a/src/xcolor.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* Color management in X has become an obscure topic because everyone prefers - * to assume they have 24-bit direct. In dealing with it, [1] has been an - * important resource. - * - * [1] https://xcb.freedesktop.org/xlibtoxcbtranslationguide/ - */ -use crate::xtypes::*; - -use xcb::x::{ AllocColor, Visualtype }; - - -pub trait DisplayColorExt: DisplayExt { - /* This is sufficiently obscure knowledge that it's worth keeping here - * even though it's unused. - */ - #[allow(dead_code)] - fn find_root_visual(&self) -> Option<&Visualtype> { - let visual_id = self.screen().root_visual(); - - for depth in self.screen().allowed_depths() { - for visual in depth.visuals() { - if visual_id == visual.visual_id() { - return Some(visual); - } - } - } - - None - } - - fn allocate_color(&self, red: u16, green: u16, blue: u16) - -> xcb::Result - { - let colormap = self.screen().default_colormap(); - - let cookie = self.send_request(&AllocColor { - cmap: colormap, - red, green, blue, - }); - - Ok(self.wait_for_reply(cookie)?.pixel()) - } -} - -impl DisplayColorExt for T { } - diff --git a/src/xcontext.rs b/src/xcontext.rs deleted file mode 100644 index d9543f7..0000000 --- a/src/xcontext.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::xtypes::*; - -use std::sync::Arc; - -use xcb::x::{ self, Drawable, CreateGc, FreeGc }; - - -pub struct GraphicContext { - display: Arc, - context_id: x::Gcontext, -} - - -impl GraphicContext { - pub fn new(display: Arc, drawable: Drawable) - -> xcb::Result - { - let context_id: x::Gcontext = display.generate_id(); - let cookie = display.send_request_checked(&CreateGc { - cid: context_id, - drawable, - value_list: &[], - }); - display.check_request(cookie)?; - - Ok(GraphicContext { display, context_id }) - } -} - -impl Drop for GraphicContext { - fn drop(&mut self) { - let cookie = self.send_request_checked(&FreeGc { gc: self.context_id }); - let _ = self.check_request(cookie); // TODO - } -} - -impl HasDisplay for GraphicContext { - fn display(&self) -> &Arc { &self.display } -} - -impl HasConnection for GraphicContext { - fn connection(&self) -> &Connection { self.display.connection() } -} - diff --git a/src/xtypes.rs b/src/xtypes.rs deleted file mode 100644 index 89bb622..0000000 --- a/src/xtypes.rs +++ /dev/null @@ -1,280 +0,0 @@ -use std::borrow::Borrow; -use std::sync::Arc; -use std::ops::Deref; - -use xcb::XidNew; -use xcb::x::{ - self, - Atom, - ChangeProperty, - CreateWindow, - InternAtom, - MapWindow, - PropMode, - Screen, -}; - - -pub struct Atoms { - pub wm_protocols: Atom, - pub wm_delete_window: Atom, - pub wm_type: Atom, - pub wm_type_dock: Atom, - pub wm_strut: Atom, - pub wm_strut_partial: Atom, -} - - -pub struct Connection(xcb::Connection); - -pub struct Display { - connection: Connection, - screen_index: i32, - atoms: Atoms, -} - -pub struct Window { - display: Arc, - window_id: x::Window, -} - - -impl Connection { - pub fn connect(display_name: Option<&str>) - -> xcb::ConnResult<(Connection, i32)> - { - let (connection, screen_index) - = xcb::Connection::connect(display_name)?; - - Ok((Connection(connection), screen_index)) - } -} - - -impl Display { - pub fn new() -> xcb::Result { - let (connection, screen_index) = Connection::connect(None)?; - - let atoms = connection.get_atoms()?; - - Ok(Display { connection, screen_index, atoms }) - } - - pub fn screen_index(&self) -> i32 { - self.screen_index - } -} - - -impl Window { - pub fn new(display: Arc, create_window: &mut CreateWindow) - -> xcb::Result - { - let display_ref: &Display = display.borrow(); - let connection: &Connection = &display_ref.connection; - - let window_id: x::Window = connection.generate_id(); - - create_window.wid = window_id; - let cookie = connection.send_request_checked(create_window); - connection.check_request(cookie)?; - - Ok(Window { display, window_id }) - } -} - - -pub trait HasWindow { - fn window(&self) -> &Window; - - fn window_id(&self) -> x::Window { self.window().window_id } -} - -impl HasWindow for Window { - fn window(&self) -> &Window { self } -} - -impl> HasWindow for T { - fn window(&self) -> &Window { &*self } -} - - -pub trait HasConnection { - fn connection(&self) -> &Connection; - - fn get_setup(&self) -> &xcb::x::Setup { - self.connection().0.get_setup() - } - - fn send_request(&self, request: &R) -> R::Cookie { - self.connection().0.send_request(request) - } - - fn wait_for_reply(&self, cookie: C) - -> xcb::Result - { - self.connection().0.wait_for_reply(cookie) - } - - fn send_request_checked(&self, request: &R) - -> xcb::VoidCookieChecked - { - self.connection().0.send_request_checked(request) - } - - fn check_request(&self, cookie: xcb::VoidCookieChecked) - -> xcb::ProtocolResult<()> - { - self.connection().0.check_request(cookie) - } - - fn wait_for_event(&self) -> xcb::Result { - self.connection().0.wait_for_event() - } -} - -impl HasConnection for Connection { - fn connection(&self) -> &Connection { self } -} - -impl HasConnection for Arc { - fn connection(&self) -> &Connection { &self.connection } -} - -impl HasConnection for Window { - fn connection(&self) -> &Connection { &self.display.connection } -} - - -pub trait HasDisplay { - fn display(&self) -> &Arc; - - fn atoms(&self) -> &Atoms { &self.display().atoms } -} - -impl HasDisplay for Arc { - fn display(&self) -> &Arc { self } -} - -impl HasDisplay for Window { - fn display(&self) -> &Arc { &self.display } -} - - -pub trait ConnectionExt: HasConnection { - fn generate_id(&self) -> T { - let connection = self.connection(); - - connection.0.generate_id() - } - - fn get_atom(&self, name: &[u8]) -> xcb::Result { - let connection = self.connection(); - - let cookie = connection.send_request(&InternAtom { - only_if_exists: true, - name: name, - }); - - Ok(connection.wait_for_reply(cookie)?.atom()) - } - - fn get_atoms(&self) -> xcb::Result { - let wm_protocols = self.get_atom(b"WM_PROTOCOLS")?; - let wm_delete_window = self.get_atom(b"WM_DELETE_WINDOW")?; - - let wm_type = self.get_atom(b"_NET_WM_WINDOW_TYPE")?; - let wm_type_dock = self.get_atom(b"_NET_WM_WINDOW_TYPE_DOCK")?; - - let wm_strut = self.get_atom(b"_NET_WM_STRUT")?; - let wm_strut_partial = self.get_atom(b"_NET_WM_STRUT_PARTIAL")?; - - Ok(Atoms { - wm_protocols, wm_delete_window, - wm_type, wm_type_dock, - wm_strut, wm_strut_partial, - }) - } -} - -impl ConnectionExt for T { } - - -pub trait DisplayExt: HasDisplay + HasConnection { - fn screen(&self) -> &Screen { - let setup = self.connection().get_setup(); - let screen_index = self.display().screen_index() as usize; - - &setup.roots().nth(screen_index).unwrap() - } -} - -impl DisplayExt for T { } - - -pub trait WindowExt: HasWindow + HasDisplay + HasConnection { - fn map_window(&self) -> xcb::Result<()> { - let cookie = self.send_request_checked(&MapWindow { - window: self.window_id() - }); - self.check_request(cookie)?; - - Ok(()) - } - - fn set_string_property(&self, property: &Atom, value: &[u8]) - -> xcb::Result<()> - { - let connection = self.connection(); - - let cookie = connection.send_request_checked(&ChangeProperty { - mode: PropMode::Replace, - window: self.window_id(), - property: *property, - r#type: x::ATOM_STRING, - data: value, - }); - connection.check_request(cookie)?; - - Ok(()) - } - - - fn set_atom_property(&self, property: &Atom, values: &[Atom]) - -> xcb::Result<()> - { - let connection = self.connection(); - - let cookie = connection.send_request_checked(&ChangeProperty { - mode: PropMode::Replace, - window: self.window_id(), - property: *property, - r#type: x::ATOM_ATOM, - data: values, - }); - connection.check_request(cookie)?; - - Ok(()) - } - - - fn set_cardinal_property(&self, property: &Atom, values: &[u32]) - -> xcb::Result<()> - { - let connection = self.connection(); - - let cookie = connection.send_request_checked(&ChangeProperty { - mode: PropMode::Replace, - window: self.window_id(), - property: *property, - r#type: x::ATOM_CARDINAL, - data: values, - }); - connection.check_request(cookie)?; - - Ok(()) - } -} - -impl WindowExt for T { } - -- cgit 1.4.1