use crate::{
core::{
config::Config,
hooks::{Hook, Hooks},
manager::WindowManager,
xconnection::Xid,
},
ErrorHandler,
};
#[cfg(feature = "xcb_draw")]
use crate::draw::{dwm_bar, Color, StatusBar, TextStyle};
pub mod api;
#[doc(hidden)]
pub mod conversions;
#[cfg(feature = "xcb_draw")]
pub mod draw;
pub mod helpers;
pub mod xconn;
#[doc(inline)]
pub use api::Api;
#[doc(inline)]
#[cfg(feature = "xcb_draw")]
pub use draw::{XcbDraw, XcbDrawContext};
#[doc(inline)]
pub use xconn::XcbConnection;
pub type XcbGenericEvent = xcb::Event<xcb::ffi::base::xcb_generic_event_t>;
pub type Result<T> = std::result::Result<T, XcbError>;
pub type XcbHooks = Hooks<XcbConnection>;
pub fn new_xcb_backed_window_manager(
config: Config,
hooks: Vec<Box<dyn Hook<XcbConnection>>>,
error_handler: ErrorHandler,
) -> crate::Result<WindowManager<XcbConnection>> {
let conn = XcbConnection::new()?;
let mut wm = WindowManager::new(config, conn, hooks, error_handler);
wm.init()?;
Ok(wm)
}
pub fn new_xcb_backed_status_bar(
height: usize,
style: &TextStyle,
highlight: impl Into<Color>,
empty_ws: impl Into<Color>,
workspaces: Vec<impl Into<String>>,
) -> crate::draw::Result<StatusBar<XcbDrawContext, XcbDraw, XcbConnection>> {
dwm_bar(
XcbDraw::new()?,
height,
style,
highlight,
empty_ws,
workspaces,
)
}
#[derive(thiserror::Error, Debug)]
pub enum XcbError {
#[error("Unable to connect to the X server via XCB")]
Connection(#[from] ::xcb::ConnError),
#[error("Xcb query returned None: {0}")]
EmptyResponse(String),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("'{0}' prop is not set for client {1}")]
MissingProp(String, Xid),
#[error("invalid client message data: format={0}")]
InvalidClientMessage(u8),
#[error("invalid property data: {0}")]
InvalidPropertyData(String),
#[error("Unable to fetch setup roots from XCB")]
NoScreens,
#[error("Requested property was not valid UTF8")]
NonUtf8Prop(#[from] std::string::FromUtf8Error),
#[error("Unable to determine required value: {0}")]
QueryFailed(&'static str),
#[error("randr query failed: {0}")]
Randr(String),
#[error("Unhandled error: {0}")]
Raw(String),
#[error(transparent)]
Strum(#[from] strum::ParseError),
#[error("The requested screen index was out of bounds: {0} > {1}")]
UnknownScreen(usize, usize),
#[error("X11 error: error seq={0}, code={1}, xid={2}, request: {3}:{4}")]
X11Error(u16, u8, u32, u8, u16),
#[error("Error making xcb query: {0:?}")]
XcbKnown(XErrorCode),
#[cfg(feature = "xcb_draw")]
#[error("Error calling Pango API: {0}")]
Pango(String),
#[cfg(feature = "xcb_draw")]
#[error("no cairo surface for {0}")]
UnintialisedSurface(Xid),
#[error("Unknown mouse button: {0}")]
UnknownMouseButton(u8),
#[error("Unknown error making xcb query: error_code={0} response_type={1}")]
XcbUnknown(u8, u8),
}
fn from_error_code(code: u8, response_type: u8) -> XcbError {
match code {
1..=11 => XcbError::XcbKnown(unsafe { std::mem::transmute(code) }),
_ => XcbError::XcbUnknown(code, response_type),
}
}
impl From<::xcb::GenericError> for XcbError {
fn from(raw: ::xcb::GenericError) -> Self {
from_error_code(raw.error_code(), raw.response_type())
}
}
impl From<&::xcb::GenericError> for XcbError {
fn from(raw: &::xcb::GenericError) -> Self {
from_error_code(raw.error_code(), raw.response_type())
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub enum XErrorCode {
BadRequest = 1,
BadValue = 2,
BadWindow = 3,
BadPixmap = 4,
BadAtom = 5,
BadCursor = 6,
BadFont = 7,
BadMatch = 8,
BadDrawable = 9,
BadAccess = 10,
}
#[doc(hidden)]
#[macro_export]
macro_rules! __xcb_impl_xatom_querier {
{ $struct:ident } => {
impl $crate::core::xconnection::XAtomQuerier for $struct {
fn atom_name(&self, atom: Xid) -> $crate::core::xconnection::Result<String> {
Ok(self.api.atom_name(atom)?)
}
fn atom_id(&self, name: &str) -> $crate::core::xconnection::Result<Xid> {
Ok(self.api.atom(name)?)
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __xcb_impl_xstate {
{ $struct:ident } => {
impl $crate::core::xconnection::XState for $struct {
fn root(&self) -> Xid {
self.api.root()
}
fn current_screens(&self) -> $crate::core::xconnection::Result<Vec<Screen>> {
Ok(self.api.current_screens()?)
}
fn cursor_position(&self) -> $crate::core::xconnection::Result<Point> {
Ok(self.api.cursor_position()?)
}
fn warp_cursor(&self, win_id: Option<Xid>, screen: &Screen) -> $crate::core::xconnection::Result<()> {
let (x, y, id) = match win_id {
Some(id) => {
let (_, _, w, h) = self.client_geometry(id)?.values();
((w / 2), (h / 2), id)
}
None => {
let (x, y, w, h) = screen.region(true).values();
((x + w / 2), (y + h / 2), self.api.root())
}
};
Ok(self.api.warp_cursor(id, x as usize, y as usize)?)
}
fn client_geometry(&self, id: Xid) -> $crate::core::xconnection::Result<Region> {
Ok(self.api.client_geometry(id)?)
}
fn active_clients(&self) -> $crate::core::xconnection::Result<Vec<Xid>> {
Ok(self.api.current_clients()?)
}
fn focused_client(&self) -> $crate::core::xconnection::Result<Xid> {
Ok(self.api.focused_client()?)
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __xcb_impl_xeventhandler {
{ $struct:ident } => {
impl $crate::core::xconnection::XEventHandler for $struct {
fn flush(&self) -> bool {
self.api.flush()
}
fn wait_for_event(&self) -> $crate::core::xconnection::Result<XEvent> {
Ok(self.api.wait_for_event()?)
}
fn send_client_event(&self, msg: ClientMessage) -> $crate::core::xconnection::Result<()> {
Ok(self.api.send_client_event(msg)?)
}
fn build_client_event(&self, kind: ClientMessageKind) -> $crate::core::xconnection::Result<ClientMessage> {
self.api.build_client_event(kind)
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __xcb_impl_xclienthandler {
{ $struct:ident } => {
impl $crate::core::xconnection::XClientHandler for $struct {
fn map_client(&self, id: Xid) -> $crate::core::xconnection::Result<()> {
Ok(self.api.map_client(id)?)
}
fn unmap_client(&self, id: Xid) -> $crate::core::xconnection::Result<()> {
Ok(self.api.unmap_client(id)?)
}
fn focus_client(&self, id: Xid) -> $crate::core::xconnection::Result<()> {
Ok(self.api.focus_client(id)?)
}
fn destroy_client(&self, id: Xid) -> $crate::core::xconnection::Result<()> {
Ok(self.api.destroy_client(id)?)
}
fn kill_client(&self, id: Xid) -> $crate::core::xconnection::Result<()> {
Ok(self.api.kill_client(id)?)
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __xcb_impl_xclientproperties {
{ $struct:ident } => {
impl $crate::core::xconnection::XClientProperties for $struct {
fn get_prop(&self, id: Xid, name: &str) -> $crate::core::xconnection::Result<Prop> {
match self.api.get_prop(id, name) {
Err(XcbError::XcbKnown($crate::xcb::XErrorCode::BadAtom)) => {
Err($crate::core::xconnection::XError::MissingProperty(name.into(), id))
},
other => Ok(other?),
}
}
fn list_props(&self, id: Xid) -> $crate::core::xconnection::Result<Vec<String>> {
Ok(self.api.list_props(id)?)
}
fn delete_prop(&self, id: Xid, name: &str) -> $crate::core::xconnection::Result<()> {
Ok(self.api.delete_prop(id, name)?)
}
fn change_prop(&self, id: Xid, prop: &str, val: Prop) -> $crate::core::xconnection::Result<()> {
Ok(self.api.change_prop(id, prop, val)?)
}
fn set_client_state(&self, id: Xid, state: WindowState) -> $crate::core::xconnection::Result<()> {
Ok(self.api.set_client_state(id, state)?)
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __xcb_impl_xclientconfig {
{ $struct:ident } => {
impl $crate::core::xconnection::XClientConfig for $struct {
fn configure_client(&self, id: Xid, data: &[ClientConfig]) -> $crate::core::xconnection::Result<()> {
Ok(self.api.configure_client(id, data)?)
}
fn set_client_attributes(&self, id: Xid, data: &[ClientAttr]) -> $crate::core::xconnection::Result<()> {
Ok(self.api.set_client_attributes(id, data)?)
}
fn get_window_attributes(&self, id: Xid) -> $crate::core::xconnection::Result<$crate::core::xconnection::WindowAttributes> {
Ok(self.api.get_window_attributes(id)?)
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __xcb_impl_xkeyboardhandler {
{ $struct:ident } => {
impl XKeyboardHandler for $struct {
fn grab_keyboard(&self) -> $crate::core::xconnection::Result<()> {
Ok(self.api.grab_keyboard()?)
}
fn ungrab_keyboard(&self) -> $crate::core::xconnection::Result<()> {
Ok(self.api.ungrab_keyboard()?)
}
fn next_keypress(&self) -> $crate::core::xconnection::Result<Option<KeyPressParseAttempt>> {
Ok(self.api.next_keypress()?)
}
fn next_keypress_blocking(&self) -> $crate::core::xconnection::Result<KeyPressParseAttempt> {
Ok(self.api.next_keypress_blocking()?)
}
}
}
}