1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! # Penrose-ui: a bare bones toolkit for adding UI elements to Penrose
//!
//! ## A note on the intended purpose of this crate
//! Penrose-ui is not intended as a general purpose UI library. It is incredibly minimal in the
//! functionality it provides and is primarily designed to provide a built-in status bar for the
//! [penrose][0] tiling window manager library. While it should be possible to make use of this
//! crate for writing UIs without integrating with penrose, that is certainly not the intended
//! use case and is not fully supported.
//!
//! ## Getting started
//! The main functionality of this crate is provided through the [`Draw`] nad [`Context`] structs
//! which allow for simple graphics rendering backed by the xlib and fontconfig libraries.
//!
//! ## A note on the use of unsafe code
//! Given the aims of this crate and the desire to pull in as few dependencies as possible, it
//! makes heavy use of `unsafe` to wrap C FFI calls. Please make sure that you read the available
//! documentation and `SAFETY` comments in the source code to understand what is happening under
//! the hood if you have any concerns about this.
//!
//! [0]: https://github.com/sminez/penrose
#![warn(
    clippy::complexity,
    clippy::correctness,
    clippy::style,
    clippy::undocumented_unsafe_blocks,
    future_incompatible,
    missing_debug_implementations,
    missing_docs,
    rust_2018_idioms,
    rustdoc::all
)]
#![doc(
    html_logo_url = "https://raw.githubusercontent.com/sminez/penrose/develop/icon.svg",
    issue_tracker_base_url = "https://github.com/sminez/penrose/issues/"
)]

use penrose::{x::XConn, Color, Xid};
use std::ffi::NulError;

pub mod bar;
pub mod core;
pub mod layout_viewer;

pub use crate::core::{Context, Draw, TextStyle};
pub use bar::{Position, StatusBar};

use bar::widgets::{ActiveWindowName, CurrentLayout, RootWindowName, Workspaces};

/// Error variants from penrose_ui library.
#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// Creation of a [`Color`] from a string hex code was invalid
    #[error("Invalid Hex color code: {code}")]
    InvalidHexColor {
        /// The invalid string that was intended as a color hex code
        code: String,
    },

    /// The specified character can not be rendered by any font on this system
    #[error("Unable to find a fallback font for '{0}'")]
    NoFallbackFontForChar(char),

    /// A string being passed to underlying C APIs contained an internal null byte
    #[error(transparent)]
    NulError(#[from] NulError),

    /// Unable to parse an integer from a provided string.
    #[error(transparent)]
    ParseInt(#[from] std::num::ParseIntError),

    /// An error was returned from the [`XConn`] when interacting with the X server
    #[error(transparent)]
    Penrose(#[from] penrose::Error),

    /// Unable to allocate a requested color
    #[error("Unable to allocate the requested color using Xft")]
    UnableToAllocateColor,

    /// Unable to open a requested font
    #[error("Unable to open '{0}' as a font using Xft")]
    UnableToOpenFont(String),

    /// Unable to open a font using an Xft font pattern
    #[error("Unable to open font from FcPattern using Xft")]
    UnableToOpenFontPattern,

    /// Unable to parse an Xft font pattern
    #[error("Unable to parse '{0}' as an Xft font patten")]
    UnableToParseFontPattern(String),

    /// An attempt was made to work with a surface for a window that was not initialised
    /// by the [`Draw`] instance being used.
    #[error("no surface for {id}")]
    UnintialisedSurface {
        /// The window id requested
        id: Xid,
    },
}

/// A Result where the error type is a penrose_ui [`Error`]
pub type Result<T> = std::result::Result<T, Error>;

/// Create a default dwm style status bar that displays content pulled from the
/// WM_NAME property of the root window.
pub fn status_bar<X: XConn>(
    height: u32,
    font: &str,
    point_size: u8,
    style: TextStyle,
    highlight: impl Into<Color>,
    empty_ws: impl Into<Color>,
    position: Position,
) -> Result<StatusBar<X>> {
    let max_active_window_chars = 80;
    let highlight = highlight.into();

    StatusBar::try_new(
        position,
        height,
        style.bg.unwrap_or_else(|| 0x000000.into()),
        font,
        point_size,
        vec![
            Box::new(Workspaces::new(style, highlight, empty_ws)),
            Box::new(CurrentLayout::new(style)),
            Box::new(ActiveWindowName::new(
                max_active_window_chars,
                TextStyle {
                    bg: Some(highlight),
                    padding: (6, 4),
                    ..style
                },
                true,
                false,
            )),
            Box::new(RootWindowName::new(
                TextStyle {
                    padding: (4, 2),
                    ..style
                },
                false,
                true,
            )),
        ],
    )
}