1use std::mem::MaybeUninit;
7
8use crate::{
9 error::{Error, Result},
10 ffi,
11};
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct Id(pub(crate) ffi::GhosttyStyleId);
19
20#[expect(
26 clippy::struct_excessive_bools,
27 reason = "style attributes should be just a bunch of bools"
28)]
29#[expect(missing_docs, reason = "self-explanatory")]
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub struct Style {
32 pub fg_color: StyleColor,
33 pub bg_color: StyleColor,
34 pub underline_color: StyleColor,
35 pub bold: bool,
36 pub italic: bool,
37 pub faint: bool,
38 pub blink: bool,
39 pub inverse: bool,
40 pub invisible: bool,
41 pub strikethrough: bool,
42 pub overline: bool,
43 pub underline: Underline,
44}
45
46impl Style {
47 #[must_use]
51 pub fn is_default(self) -> bool {
52 let raw = ffi::GhosttyStyle::from(self);
53 unsafe { ffi::ghostty_style_is_default(&raw const raw) }
54 }
55}
56
57impl Default for Style {
58 fn default() -> Self {
59 let mut style = MaybeUninit::zeroed();
60 unsafe {
61 ffi::ghostty_style_default(style.as_mut_ptr());
62 }
63
64 Self::try_from(unsafe { style.assume_init() })
66 .expect("ghostty_style_default to init valid Style")
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub enum StyleColor {
73 None,
75 Palette(PaletteIndex),
77 Rgb(RgbColor),
79}
80
81#[repr(C)]
83#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
84pub struct RgbColor {
85 pub r: u8,
87 pub g: u8,
89 pub b: u8,
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
95pub struct PaletteIndex(pub u8);
96
97impl PaletteIndex {
98 #![expect(clippy::cast_possible_truncation, reason = "bindgen ain't perfect")]
99 #![expect(missing_docs, reason = "self-explanatory")]
100 pub const BLACK: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BLACK as u8);
101 pub const RED: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_RED as u8);
102 pub const GREEN: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_GREEN as u8);
103 pub const YELLOW: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_YELLOW as u8);
104 pub const BLUE: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BLUE as u8);
105 pub const MAGENTA: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_MAGENTA as u8);
106 pub const CYAN: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_CYAN as u8);
107 pub const WHITE: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_WHITE as u8);
108 pub const BRIGHT_BLACK: PaletteIndex =
109 PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BRIGHT_BLACK as u8);
110 pub const BRIGHT_RED: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BRIGHT_RED as u8);
111 pub const BRIGHT_GREEN: PaletteIndex =
112 PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BRIGHT_GREEN as u8);
113 pub const BRIGHT_YELLOW: PaletteIndex =
114 PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BRIGHT_YELLOW as u8);
115 pub const BRIGHT_BLUE: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BRIGHT_BLUE as u8);
116 pub const BRIGHT_MAGENTA: PaletteIndex =
117 PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BRIGHT_MAGENTA as u8);
118 pub const BRIGHT_CYAN: PaletteIndex = PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BRIGHT_CYAN as u8);
119 pub const BRIGHT_WHITE: PaletteIndex =
120 PaletteIndex(ffi::GHOSTTY_COLOR_NAMED_BRIGHT_WHITE as u8);
121}
122
123#[repr(u32)]
125#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, int_enum::IntEnum)]
126#[non_exhaustive]
127#[expect(missing_docs, reason = "self-explanatory")]
128pub enum Underline {
129 None = ffi::GhosttySgrUnderline_GHOSTTY_SGR_UNDERLINE_NONE,
130 Single = ffi::GhosttySgrUnderline_GHOSTTY_SGR_UNDERLINE_SINGLE,
131 Double = ffi::GhosttySgrUnderline_GHOSTTY_SGR_UNDERLINE_DOUBLE,
132 Curly = ffi::GhosttySgrUnderline_GHOSTTY_SGR_UNDERLINE_CURLY,
133 Dotted = ffi::GhosttySgrUnderline_GHOSTTY_SGR_UNDERLINE_DOTTED,
134 Dashed = ffi::GhosttySgrUnderline_GHOSTTY_SGR_UNDERLINE_DASHED,
135}
136
137impl TryFrom<ffi::GhosttyStyle> for Style {
142 type Error = Error;
143 fn try_from(value: ffi::GhosttyStyle) -> Result<Self> {
144 Ok(Self {
145 fg_color: StyleColor::try_from(value.fg_color)?,
146 bg_color: StyleColor::try_from(value.bg_color)?,
147 underline_color: StyleColor::try_from(value.underline_color)?,
148 bold: value.bold,
149 italic: value.italic,
150 faint: value.faint,
151 blink: value.blink,
152 inverse: value.inverse,
153 invisible: value.invisible,
154 strikethrough: value.strikethrough,
155 overline: value.overline,
156 #[expect(clippy::cast_sign_loss, reason = "bindgen ain't perfect")]
157 underline: Underline::try_from(value.underline as u32)
158 .map_err(|_| Error::InvalidValue)?,
159 })
160 }
161}
162
163impl From<Style> for ffi::GhosttyStyle {
164 fn from(value: Style) -> Self {
165 Self {
166 size: std::mem::size_of::<Self>(),
167 fg_color: value.fg_color.into(),
168 bg_color: value.bg_color.into(),
169 underline_color: value.underline_color.into(),
170 bold: value.bold,
171 italic: value.italic,
172 faint: value.faint,
173 blink: value.blink,
174 inverse: value.inverse,
175 invisible: value.invisible,
176 strikethrough: value.strikethrough,
177 overline: value.overline,
178 #[expect(clippy::cast_possible_wrap, reason = "bindgen ain't perfect")]
179 underline: u32::from(value.underline) as i32,
180 }
181 }
182}
183
184impl TryFrom<ffi::GhosttyStyleColor> for StyleColor {
185 type Error = Error;
186 fn try_from(value: ffi::GhosttyStyleColor) -> Result<Self> {
187 Ok(match value.tag {
188 ffi::GhosttyStyleColorTag_GHOSTTY_STYLE_COLOR_NONE => Self::None,
189 ffi::GhosttyStyleColorTag_GHOSTTY_STYLE_COLOR_PALETTE => {
190 Self::Palette(PaletteIndex(unsafe { value.value.palette }))
191 }
192 ffi::GhosttyStyleColorTag_GHOSTTY_STYLE_COLOR_RGB => {
193 Self::Rgb(unsafe { value.value.rgb }.into())
194 }
195 _ => return Err(Error::InvalidValue),
196 })
197 }
198}
199
200impl From<StyleColor> for ffi::GhosttyStyleColor {
201 fn from(value: StyleColor) -> Self {
202 match value {
203 StyleColor::None => Self {
204 tag: ffi::GhosttyStyleColorTag_GHOSTTY_STYLE_COLOR_NONE,
205 value: ffi::GhosttyStyleColorValue::default(),
206 },
207 StyleColor::Palette(PaletteIndex(palette)) => Self {
208 tag: ffi::GhosttyStyleColorTag_GHOSTTY_STYLE_COLOR_PALETTE,
209 value: ffi::GhosttyStyleColorValue { palette },
210 },
211 StyleColor::Rgb(rgb) => Self {
212 tag: ffi::GhosttyStyleColorTag_GHOSTTY_STYLE_COLOR_NONE,
213 value: ffi::GhosttyStyleColorValue { rgb: rgb.into() },
214 },
215 }
216 }
217}
218
219impl From<ffi::GhosttyColorRgb> for RgbColor {
220 fn from(value: ffi::GhosttyColorRgb) -> Self {
221 let ffi::GhosttyColorRgb { r, g, b } = value;
222 Self { r, g, b }
223 }
224}
225
226impl From<RgbColor> for ffi::GhosttyColorRgb {
227 fn from(value: RgbColor) -> Self {
228 let RgbColor { r, g, b } = value;
229 Self { r, g, b }
230 }
231}