Diff
diff --git a/Cargo.lock b/Cargo.lock
index 1eab2ff..c241f04 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -51,9 +51,16 @@ name = "oxwm"
version = "0.1.0"
dependencies = [
"anyhow",
+ "x11",
"x11rb",
]
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
[[package]]
name = "rustix"
version = "1.1.2"
@@ -146,6 +153,16 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+[[package]]
+name = "x11"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
[[package]]
name = "x11rb"
version = "0.13.2"
diff --git a/Cargo.toml b/Cargo.toml
index f578c6d..7d6e1be 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
+x11 = { version = "2.21", features = ["xlib", "xft"] }
x11rb = "0.13"
anyhow = "1"
diff --git a/flake.nix b/flake.nix
index b0fa72d..13322bd 100644
--- a/flake.nix
+++ b/flake.nix
@@ -3,29 +3,39 @@
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
- outputs = {
- self,
- nixpkgs,
- }: let
- system = "x86_64-linux";
- pkgs = import nixpkgs {inherit system;};
- in {
- devShells.${system}.default = pkgs.mkShell {
- buildInputs = [
- pkgs.rustc
- pkgs.cargo
- pkgs.xorg.xclock
- pkgs.xterm
- pkgs.alacritty
- pkgs.just
- ];
- shellHook = ''
- export PS1="(oxwm-dev) $PS1"
- '';
+ outputs =
+ { self
+ , nixpkgs
+ ,
+ }:
+ let
+ system = "x86_64-linux";
+ pkgs = import nixpkgs { inherit system; };
+ in
+ {
+ devShells.${system}.default = pkgs.mkShell {
+ buildInputs = [
+ pkgs.rustc
+ pkgs.cargo
+ pkgs.alacritty
+ pkgs.just
+ pkgs.xorg.libX11
+ pkgs.xorg.libXft
+ pkgs.xorg.libXrender
+ pkgs.freetype
+ pkgs.fontconfig
+ pkgs.pkg-config
+ ];
- RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}";
- };
+ shellHook = ''
+ export PKG_CONFIG_PATH="${pkgs.xorg.libX11.dev}/lib/pkgconfig:${pkgs.xorg.libXft.dev}/lib/pkgconfig:${pkgs.xorg.libXrender.dev}/lib/pkgconfig:${pkgs.freetype.dev}/lib/pkgconfig:${pkgs.fontconfig.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"
+
+ export PS1="(oxwm-dev) $PS1"
+ '';
- formatter.${system} = pkgs.alejandra;
- };
+ RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}";
+ };
+
+ formatter.${system} = pkgs.alejandra;
+ };
}
diff --git a/src/bar/bar.rs b/src/bar/bar.rs
index 7abbc43..cc476fa 100644
--- a/src/bar/bar.rs
+++ b/src/bar/bar.rs
@@ -1,9 +1,11 @@
use super::BAR_HEIGHT;
-use crate::config::{SCHEME_NORMAL, SCHEME_OCCUPIED, SCHEME_SELECTED, TAGS};
+use super::font::{Font, FontDraw};
+use crate::config::{FONT, SCHEME_NORMAL, SCHEME_OCCUPIED, SCHEME_SELECTED, TAGS};
use anyhow::Result;
use x11rb::COPY_DEPTH_FROM_PARENT;
use x11rb::connection::Connection;
use x11rb::protocol::xproto::*;
+use x11rb::rust_connection::RustConnection;
pub struct Bar {
window: Window,
@@ -11,12 +13,16 @@ pub struct Bar {
height: u16,
graphics_context: Gcontext,
+ font: Font,
+ font_draw: FontDraw,
+ display: *mut x11::xlib::Display,
+
tag_widths: Vec<u16>,
needs_redraw: bool,
}
impl Bar {
- pub fn new<C: Connection>(connection: &C, screen: &Screen) -> Result<Self> {
+ pub fn new(connection: &RustConnection, screen: &Screen, screen_num: usize) -> Result<Self> {
let window = connection.generate_id()?;
let graphics_context = connection.generate_id()?;
@@ -51,14 +57,28 @@ impl Bar {
connection.map_window(window)?;
connection.flush()?;
- // TODO: actual text width calculation when we add fonts
- let tag_widths = TAGS.iter().map(|tag| (tag.len() as u16 * 8) + 10).collect();
+ let display = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
+ if display.is_null() {
+ anyhow::bail!("Failed to open X11 display for XFT");
+ }
+
+ let font = Font::new(display, screen_num as i32, FONT)?;
+
+ let visual = unsafe { x11::xlib::XDefaultVisual(display, screen_num as i32) };
+ let colormap = unsafe { x11::xlib::XDefaultColormap(display, screen_num as i32) };
+
+ let font_draw = FontDraw::new(display, window as x11::xlib::Drawable, visual, colormap)?;
+
+ let tag_widths = TAGS.iter().map(|tag| font.text_width(tag) + 10).collect();
Ok(Bar {
window,
width,
height,
graphics_context,
+ font,
+ font_draw,
+ display,
tag_widths,
needs_redraw: true,
})
@@ -76,9 +96,9 @@ impl Bar {
self.needs_redraw = true;
}
- pub fn draw<C: Connection>(
+ pub fn draw(
&mut self,
- connection: &C,
+ connection: &RustConnection,
current_tags: u32,
occupied_tags: u32,
) -> Result<()> {
@@ -150,24 +170,19 @@ impl Bar {
)?;
}
- connection.change_gc(
- self.graphics_context,
- &ChangeGCAux::new().foreground(scheme.foreground),
- )?;
-
- // TODO: Replace with actual font rendering later
- connection.image_text8(
- self.window,
- self.graphics_context,
- x_position + 5,
- self.height as i16 - 5,
- tag.as_bytes(),
- )?;
+ let text_y = (self.height as i16 / 2) + (self.font.ascent() / 2);
+ self.font_draw
+ .draw_text(&self.font, scheme.foreground, x_position + 5, text_y, tag);
x_position += tag_width as i16;
}
connection.flush()?;
+
+ unsafe {
+ x11::xlib::XFlush(self.display);
+ }
+
self.needs_redraw = false;
Ok(())
diff --git a/src/bar/font.rs b/src/bar/font.rs
new file mode 100644
index 0000000..7851139
--- /dev/null
+++ b/src/bar/font.rs
@@ -0,0 +1,136 @@
+use anyhow::Result;
+use std::ffi::CString;
+use std::ptr;
+use x11::xft::{XftColor, XftDraw, XftDrawStringUtf8, XftFont, XftFontOpenName};
+use x11::xlib::{Colormap, Display, Drawable, Visual};
+use x11::xrender::XRenderColor;
+
+pub struct Font {
+ xft_font: *mut XftFont,
+ display: *mut Display,
+}
+
+impl Font {
+ pub fn new(display: *mut Display, screen: i32, font_name: &str) -> Result<Self> {
+ let font_name_cstr = CString::new(font_name)?;
+
+ let xft_font = unsafe { XftFontOpenName(display, screen, font_name_cstr.as_ptr()) };
+
+ if xft_font.is_null() {
+ anyhow::bail!("Failed to load font: {}", font_name);
+ }
+
+ Ok(Font { xft_font, display })
+ }
+
+ pub fn height(&self) -> u16 {
+ unsafe {
+ let font = &*self.xft_font;
+ font.height as u16
+ }
+ }
+
+ pub fn ascent(&self) -> i16 {
+ unsafe {
+ let font = &*self.xft_font;
+ font.ascent as i16
+ }
+ }
+
+ pub fn text_width(&self, text: &str) -> u16 {
+ unsafe {
+ let mut extents = std::mem::zeroed();
+ x11::xft::XftTextExtentsUtf8(
+ self.display,
+ self.xft_font,
+ text.as_ptr(),
+ text.len() as i32,
+ &mut extents,
+ );
+ extents.width as u16
+ }
+ }
+}
+
+impl Drop for Font {
+ fn drop(&mut self) {
+ unsafe {
+ if !self.xft_font.is_null() {
+ x11::xft::XftFontClose(self.display, self.xft_font);
+ }
+ }
+ }
+}
+
+pub struct FontDraw {
+ xft_draw: *mut XftDraw,
+}
+
+impl FontDraw {
+ pub fn new(
+ display: *mut Display,
+ drawable: Drawable,
+ visual: *mut Visual,
+ colormap: Colormap,
+ ) -> Result<Self> {
+ let xft_draw = unsafe { x11::xft::XftDrawCreate(display, drawable, visual, colormap) };
+
+ if xft_draw.is_null() {
+ anyhow::bail!("Failed to create XftDraw");
+ }
+
+ Ok(FontDraw { xft_draw })
+ }
+
+ pub fn draw_text(&self, font: &Font, color: u32, x: i16, y: i16, text: &str) {
+ let red = ((color >> 16) & 0xFF) as u16;
+ let green = ((color >> 8) & 0xFF) as u16;
+ let blue = (color & 0xFF) as u16;
+
+ let render_color = XRenderColor {
+ red: red << 8 | red,
+ green: green << 8 | green,
+ blue: blue << 8 | blue,
+ alpha: 0xFFFF,
+ };
+
+ let mut xft_color: XftColor = unsafe { std::mem::zeroed() };
+
+ unsafe {
+ x11::xft::XftColorAllocValue(
+ x11::xft::XftDrawDisplay(self.xft_draw),
+ x11::xft::XftDrawVisual(self.xft_draw),
+ x11::xft::XftDrawColormap(self.xft_draw),
+ &render_color,
+ &mut xft_color,
+ );
+
+ XftDrawStringUtf8(
+ self.xft_draw,
+ &xft_color,
+ font.xft_font,
+ x as i32,
+ y as i32,
+ text.as_ptr(),
+ text.len() as i32,
+ );
+
+ x11::xft::XftColorFree(
+ x11::xft::XftDrawDisplay(self.xft_draw),
+ x11::xft::XftDrawVisual(self.xft_draw),
+ x11::xft::XftDrawColormap(self.xft_draw),
+ &mut xft_color,
+ );
+ }
+ }
+}
+
+impl Drop for FontDraw {
+ fn drop(&mut self) {
+ unsafe {
+ if !self.xft_draw.is_null() {
+ x11::xft::XftDrawDestroy(self.xft_draw);
+ }
+ }
+ }
+}
diff --git a/src/bar/mod.rs b/src/bar/mod.rs
index bac11c9..f582771 100644
--- a/src/bar/mod.rs
+++ b/src/bar/mod.rs
@@ -1,4 +1,5 @@
mod bar;
+mod font;
// mod widgets; // TODO: implement later
pub use bar::Bar;
diff --git a/src/config.rs b/src/config.rs
index 6639819..ae6b74a 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -8,6 +8,7 @@ use x11rb::protocol::xproto::KeyButMask;
pub const BORDER_WIDTH: u32 = 1;
pub const BORDER_FOCUSED: u32 = 0x6dade3;
pub const BORDER_UNFOCUSED: u32 = 0xbbbbbb;
+pub const FONT: &str = "JetBrainsMono Nerd Font:size=12";
// ========================================
// DEFAULTS
diff --git a/src/window_manager.rs b/src/window_manager.rs
index 9f35c27..7a59360 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -46,7 +46,7 @@ impl WindowManager {
)?
.check()?;
- let bar = Bar::new(&connection, &screen)?;
+ let bar = Bar::new(&connection, &screen, screen_number)?;
return Ok(Self {
connection,