[root]/ src
/ bar.zig
7.9KB
const std = @import("std");
const c = @import("c.zig").c;
const config = @import("config.zig");
const client_mod = @import("client.zig");
const WM = client_mod.WM;
const Client = client_mod.Client;
const util = @import("util.zig");
fn allocColor(display: ?*c.Display, visual: ?*c.Visual, cmap: c.Colormap, name: [:0]const u8) !c.XftColor {
var color: c.XftColor = undefined;
if (c.XftColorAllocName(display, visual, cmap, name, &color) == 0)
return error.ColorAllocFailed;
return color;
}
fn fontHeight(fonts: []?*c.XftFont) i32 {
for (fonts) |f| {
if (f) |font| return font.ascent + font.descent;
}
return config.bar_height;
}
fn fontAscent(fonts: []?*c.XftFont) i32 {
for (fonts) |f| {
if (f) |font| return font.ascent;
}
return 0;
}
fn textWidth(dpy: ?*c.Display, fonts: []?*c.XftFont, text: []const u8) i32 {
for (fonts) |f| {
if (f == null) continue;
var ext: c.XGlyphInfo = undefined;
c.XftTextExtents8(dpy, f.?, text.ptr, @intCast(text.len), &ext);
return @intCast(ext.xOff);
}
return @intCast(text.len * 8);
}
fn drawChar(dpy: ?*c.Display, xft_draw: ?*c.XftDraw, fonts: []?*c.XftFont, color: *c.XftColor, x: i32, y: i32, ch: u8) i32 {
const rune: c.FcChar32 = ch;
for (fonts) |f| {
if (f == null) continue;
const glyph = c.XftCharIndex(dpy, f.?, rune);
if (glyph == 0) continue;
var ext: c.XGlyphInfo = undefined;
c.XftGlyphExtents(dpy, f.?, &glyph, 1, &ext);
c.XftDrawGlyphs(xft_draw, color, f.?, x, y, &glyph, 1);
return @intCast(ext.xOff);
}
return 8;
}
fn drawText(dpy: ?*c.Display, xft_draw: ?*c.XftDraw, fonts: []?*c.XftFont, color: *c.XftColor, x: i32, y: i32, text: []const u8) i32 {
var cx = x;
for (text) |ch| {
cx += drawChar(dpy, xft_draw, fonts, color, cx, y, ch);
}
return cx;
}
pub fn create(wm: *WM) !void {
const d = wm.display;
const s = wm.screen;
const root = wm.root;
const visual = wm.visual;
const cmap = wm.colormap;
for (&config.font_names, 0..) |name, i| {
wm.bar.fonts[i] = c.XftFontOpenName(d, s, name);
if (wm.bar.fonts[i] == null)
util.die("cannot open font: {s}\n", .{name});
}
wm.bar.colors.bg = allocColor(d, visual, cmap, config.col.bar_bg) catch
util.die("cannot allocate color: {s}\n", .{config.col.bar_bg});
wm.bar.colors.fg = allocColor(d, visual, cmap, config.col.bar_fg) catch
util.die("cannot allocate color: {s}\n", .{config.col.bar_fg});
wm.bar.colors.sel_bg = allocColor(d, visual, cmap, config.col.bar_sel_bg) catch
util.die("cannot allocate color: {s}\n", .{config.col.bar_sel_bg});
wm.bar.colors.min_fg = allocColor(d, visual, cmap, config.col.bar_min_fg) catch
util.die("cannot allocate color: {s}\n", .{config.col.bar_min_fg});
wm.bar.colors.urg_fg = allocColor(d, visual, cmap, config.col.bar_urg_fg) catch
util.die("cannot allocate color: {s}\n", .{config.col.bar_urg_fg});
wm.bar.colors.border_focused = allocColor(d, visual, cmap, config.col.border_focused) catch
util.die("cannot allocate color: {s}\n", .{config.col.border_focused});
wm.bar.colors.border_unfocused = allocColor(d, visual, cmap, config.col.border_unfocused) catch
util.die("cannot allocate color: {s}\n", .{config.col.border_unfocused});
var wa = std.mem.zeroes(c.XSetWindowAttributes);
wa.override_redirect = 1;
wa.background_pixmap = c.ParentRelative;
wa.event_mask = c.ExposureMask | c.ButtonPressMask;
wm.bar.window = c.XCreateWindow(d, root, 0, 0, wm.sw, config.bar_height, 0,
c.CopyFromParent, c.InputOutput, visual,
c.CWOverrideRedirect | c.CWBackPixmap | c.CWEventMask, &wa);
_ = c.XChangeProperty(d, wm.bar.window, wm.atoms.net_wm_window_type,
c.XA_ATOM, 32, c.PropModeReplace,
@ptrCast(@constCast(&wm.atoms.net_wm_window_type_dock)), 1);
_ = c.XMapWindow(d, wm.bar.window);
wm.bar.pixmap = c.XCreatePixmap(d, wm.bar.window, wm.sw, config.bar_height,
@intCast(c.DefaultDepth(d, s)));
wm.bar.draw = c.XftDrawCreate(d, wm.bar.pixmap, visual, cmap);
wm.bar.button_count = 0;
}
pub fn draw(wm: *WM) void {
const bar = &wm.bar;
const d = wm.display;
const fonts = &bar.fonts;
const bh: i32 = @intCast(config.bar_height);
const asc = fontAscent(fonts) + @divTrunc(bh - fontHeight(fonts), 2);
_ = c.XftDrawRect(bar.draw, &bar.colors.bg, 0, 0, @intCast(wm.sw), bh);
var x: i32 = 0;
var btn_idx: usize = 0;
var it = wm.clients;
while (it) |cl| : (it = cl.next) {
if (btn_idx >= bar.buttons.len) break;
const title = if (cl.title.len > 0) cl.title else "???";
var tw = textWidth(d, fonts, title) + 12;
if (x + tw > @as(i32, @intCast(wm.sw))) tw = @as(i32, @intCast(wm.sw)) - x;
if (wm.focused == cl) {
_ = c.XftDrawRect(bar.draw, &bar.colors.sel_bg, x, 0, @intCast(tw), @intCast(bh));
}
const fg_color: *c.XftColor = if (cl.flags.minimized)
&bar.colors.min_fg
else if (cl.flags.urgent)
&bar.colors.urg_fg
else
&bar.colors.fg;
const draw_w = @max(tw - 12, 0);
if (draw_w > 0) {
var end: usize = 0;
while (end < title.len) : (end += 1) {
if (textWidth(d, fonts, title[0 .. end + 1]) > draw_w) break;
}
if (end > 0)
_ = drawText(d, bar.draw, fonts, fg_color, x + 6, asc, title[0..end]);
}
bar.buttons[btn_idx] = .{ .x = x, .w = @intCast(tw), .win = cl.window };
btn_idx += 1;
x += tw;
}
bar.button_count = btn_idx;
var status_buf: [256]u8 = undefined;
const status = readStatus(wm, &status_buf);
if (status.len > 0) {
const sw = textWidth(d, fonts, status);
const sx = @as(i32, @intCast(wm.sw)) - sw - 6;
if (sx > x) {
_ = drawText(d, bar.draw, fonts, &bar.colors.fg, sx, asc, status);
}
}
_ = c.XCopyArea(d, bar.pixmap, bar.window,
c.DefaultGC(d, wm.screen), 0, 0, @intCast(wm.sw), bh, 0, 0);
_ = c.XRaiseWindow(d, wm.bar.window);
}
fn readPropText(wm: *WM, prop_atom: c.Atom, buf: *[256]u8) []u8 {
var actual_type: c.Atom = 0;
var actual_format: c_int = 0;
var nitems: c_ulong = 0;
var bytes_after: c_ulong = 0;
var prop: [*c]u8 = null;
if (c.XGetWindowProperty(wm.display, wm.root, prop_atom,
0, 1024, 0, c.AnyPropertyType, &actual_type, &actual_format,
&nitems, &bytes_after, &prop) == c.Success and prop != null)
{
defer _ = c.XFree(prop);
if (actual_format != 8 or nitems == 0) return &.{};
const len = @min(nitems, buf.len - 1);
@memcpy(buf[0..len], prop[0..len]);
buf[len] = 0;
return buf[0..len];
}
return &.{};
}
fn readStatus(wm: *WM, buf: *[256]u8) []u8 {
const utf8 = readPropText(wm, wm.atoms.net_wm_name, buf);
if (utf8.len > 0) return utf8;
return readPropText(wm, c.XA_WM_NAME, buf);
}
pub fn destroy(wm: *WM) void {
if (wm.bar.draw) |d| c.XftDrawDestroy(d);
if (wm.bar.pixmap != 0) {
_ = c.XFreePixmap(wm.display, wm.bar.pixmap);
}
if (wm.bar.window != 0) {
_ = c.XDestroyWindow(wm.display, wm.bar.window);
}
for (&wm.bar.fonts) |f| {
if (f) |font| c.XftFontClose(wm.display, font);
}
c.XftColorFree(wm.display, wm.visual, wm.colormap, &wm.bar.colors.bg);
c.XftColorFree(wm.display, wm.visual, wm.colormap, &wm.bar.colors.fg);
c.XftColorFree(wm.display, wm.visual, wm.colormap, &wm.bar.colors.sel_bg);
c.XftColorFree(wm.display, wm.visual, wm.colormap, &wm.bar.colors.min_fg);
c.XftColorFree(wm.display, wm.visual, wm.colormap, &wm.bar.colors.urg_fg);
c.XftColorFree(wm.display, wm.visual, wm.colormap, &wm.bar.colors.border_focused);
c.XftColorFree(wm.display, wm.visual, wm.colormap, &wm.bar.colors.border_unfocused);
}