oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

Added fonts.. (bloatware), updated devshell accordingly

Commit
fc9864b107552746b147d939f497a47f3b7d33d0
Parent
da4d6dc
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-10-03 10:09:16

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,