2zw

Owner: IIIlllIIIllI URL: git@git.0x00nyx.xyz:seb/2zw.git

src/draw.zig

// MIT License

// Copyright (c) 2025 Sebastian <sebastian.michalk@pm.me>

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

const std = @import("std");
const C = @import("c.zig");

pub const Drw = struct {
    alloc: std.mem.Allocator,
    dpy: *C.Display,
    scr: c_int,
    vis: *C.Visual,
    cmap: C.Colormap,
    gc: C.GC,
    font: *C.XftFont,
    ascent: c_int,
    descent: c_int,
    lh: c_int,

    pub fn init(
        alloc: std.mem.Allocator,
        dpy: *C.Display,
        scr: c_int,
        font_name: [*:0]const u8,
    ) !Drw {
        const font = loadfont(dpy, scr, font_name) orelse
            return error.FontUnavailable;

        const root = C.RootWindow(dpy, scr);
        const gc_opt = C.XCreateGC(dpy, root, 0, null);
        if (gc_opt == null) {
            C.XftFontClose(dpy, font);
            return error.CreateGcFailed;
        }
        const gc = gc_opt.?;

        const vis = C.XDefaultVisual(dpy, scr);
        const cmap = C.XDefaultColormap(dpy, scr);

        return Drw{
            .alloc = alloc,
            .dpy = dpy,
            .scr = scr,
            .vis = vis,
            .cmap = cmap,
            .gc = gc,
            .font = font,
            .ascent = font.*.ascent,
            .descent = font.*.descent,
            .lh = font.*.ascent + font.*.descent,
        };
    }

    fn loadfont(
        dpy: *C.Display,
        scr: c_int,
        preferred: [*:0]const u8,
    ) ?*C.XftFont {
        const candidates = [_][*:0]const u8{
            preferred,
            "monospace:size=10",
            "fixed",
        };
        for (candidates) |name| {
            if (name[0] == 0) continue;
            const font_ptr = C.XftFontOpenName(dpy, scr, name);
            if (font_ptr != null) {
                if (name != preferred) {
                    std.log.warn("bar font fallback to {s}", .{name});
                }
                return font_ptr;
            }
        }
        return null;
    }

    pub fn deinit(self: *Drw) void {
        C.XftFontClose(self.dpy, self.font);
        _ = C.XFreeGC(self.dpy, self.gc);
    }

    pub fn alloccol(self: *Drw, rgb: u32) !C.ulong {
        var color: C.XColor = undefined;
        color.pixel = 0;
        color.red = @intCast(((rgb >> 16) & 0xff) * 257);
        color.green = @intCast(((rgb >> 8) & 0xff) * 257);
        color.blue = @intCast((rgb & 0xff) * 257);
        color.flags = @intCast(C.DoRed | C.DoGreen | C.DoBlue);
        color.pad = 0;

        if (C.XAllocColor(self.dpy, self.cmap, &color) == 0) {
            return error.ColorAllocationFailed;
        }

        return color.pixel;
    }

    pub fn allocxftcol(self: *Drw, rgb: u32) !C.XftColor {
        const r: u16 = @intCast(((rgb >> 16) & 0xff) * 257);
        const g: u16 = @intCast(((rgb >> 8) & 0xff) * 257);
        const b: u16 = @intCast((rgb & 0xff) * 257);
        var xft_color: C.XftColor = undefined;
        var render_color: C.XRenderColor = undefined;
        render_color.red = r;
        render_color.green = g;
        render_color.blue = b;
        render_color.alpha = 0xffff;
        if (C.XftColorAllocValue(
            self.dpy,
            self.vis,
            self.cmap,
            &render_color,
            &xft_color,
        ) == 0) {
            return error.ColorAllocationFailed;
        }
        return xft_color;
    }

    pub fn rect(
        self: *Drw,
        draw: C.Drawable,
        col: C.ulong,
        x: c_int,
        y: c_int,
        w: c_uint,
        h: c_uint,
    ) void {
        _ = C.XSetForeground(self.dpy, self.gc, col);
        _ = C.XFillRectangle(self.dpy, draw, self.gc, x, y, w, h);
    }

    pub fn text(
        self: *Drw,
        _: C.Drawable,
        xft: *C.XftDraw,
        col: *C.XftColor,
        x: c_int,
        y: c_int,
        txt: []const u8,
    ) void {
        if (txt.len == 0) return;
        C.XftDrawStringUtf8(
            xft,
            col,
            self.font,
            x,
            y,
            @ptrCast(txt.ptr),
            @intCast(txt.len),
        );
    }

    pub fn textw(self: *Drw, txt: []const u8) c_int {
        if (txt.len == 0) return 0;
        var extents: C.XGlyphInfo = undefined;
        C.XftTextExtentsUtf8(
            self.dpy,
            self.font,
            @ptrCast(txt.ptr),
            @intCast(txt.len),
            &extents,
        );
        return @intCast(extents.xOff);
    }
};