[root]/ src
/ main.zig
6.6KB
const std = @import("std");
const c = @import("c.zig").c;
const config = @import("config.zig");
const util = @import("util.zig");
const atoms_mod = @import("atoms.zig");
const client_mod = @import("client.zig");
const WM = client_mod.WM;
const bar_mod = @import("bar.zig");
const event_mod = @import("event.zig");
var wm_global: ?*WM = null;
fn xerrorStrict(dpy: ?*c.Display, ev: [*c]c.XErrorEvent) callconv(.c) c_int {
_ = dpy;
_ = ev;
util.die("wm: another window manager is already running\n", .{});
return 0;
}
fn xerrorNormal(dpy: ?*c.Display, ev: [*c]c.XErrorEvent) callconv(.c) c_int {
const ec = ev.*.error_code;
if (ec == c.BadWindow or ec == c.BadMatch or ec == c.BadDrawable)
return 0;
var buf: [128]u8 = undefined;
_ = c.XGetErrorText(dpy, ec, &buf, buf.len);
util.log("wm: xerror: {s} ({d})\n", .{ std.mem.sliceTo(&buf, 0), ec });
return 0;
}
fn sigTerm(sig: c_int) callconv(.c) void {
_ = sig;
if (wm_global) |wm| wm.running = false;
}
fn sigChld(sig: c_int) callconv(.c) void {
_ = sig;
while (true) {
var status: c_int = 0;
const pid = std.c.waitpid(-1, &status, 1);
if (pid <= 0) break;
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const alloc = gpa.allocator();
var args = std.process.args();
_ = args.next();
while (args.next()) |arg| {
if (std.mem.eql(u8, arg, "-v")) {
util.log("wm 0.1.0\n", .{});
std.process.exit(0);
} else {
util.die("usage: wm [-v]\n", .{});
}
}
if (c.XOpenDisplay(null)) |dpy| {
var wm = WM{
.display = dpy,
.screen = c.DefaultScreen(dpy),
.root = c.RootWindow(dpy, c.DefaultScreen(dpy)),
.sw = @intCast(c.DisplayWidth(dpy, c.DefaultScreen(dpy))),
.sh = @intCast(c.DisplayHeight(dpy, c.DefaultScreen(dpy))),
.atoms = undefined,
.bar = undefined,
.clients = null,
.focused = null,
.running = true,
.allocator = alloc,
.support_win = 0,
.grab = null,
.visual = c.DefaultVisual(dpy, c.DefaultScreen(dpy)),
.colormap = c.DefaultColormap(dpy, c.DefaultScreen(dpy)),
};
wm_global = &wm;
_ = c.XSetErrorHandler(xerrorStrict);
_ = c.XSelectInput(dpy, wm.root,
c.SubstructureRedirectMask | c.SubstructureNotifyMask |
c.ButtonPressMask | c.StructureNotifyMask |
c.PropertyChangeMask | c.EnterWindowMask |
c.FocusChangeMask | c.KeymapStateMask);
_ = c.XSync(dpy, 0);
_ = c.XSetErrorHandler(xerrorNormal);
const cursor = c.XCreateFontCursor(dpy, 68);
_ = c.XDefineCursor(dpy, wm.root, cursor);
wm.atoms = atoms_mod.Atoms.init(dpy);
var wm_sn_buf: [32]u8 = undefined;
const wm_sn_name = std.fmt.bufPrintZ(&wm_sn_buf, "WM_S{d}", .{wm.screen}) catch "WM_S0";
const wm_sn = c.XInternAtom(dpy, wm_sn_name, 0);
wm.support_win = c.XCreateSimpleWindow(dpy, wm.root, -1, -1, 1, 1, 0, 0, 0);
_ = c.XChangeProperty(dpy, wm.support_win, wm.atoms.net_supporting_wm_check,
c.XA_WINDOW, 32, c.PropModeReplace,
@ptrCast(&wm.support_win), 1);
_ = c.XChangeProperty(dpy, wm.root, wm.atoms.net_supporting_wm_check,
c.XA_WINDOW, 32, c.PropModeReplace,
@ptrCast(&wm.support_win), 1);
_ = c.XSetSelectionOwner(dpy, wm_sn, wm.support_win, c.CurrentTime);
const pid_val: c_long = @intCast(std.os.linux.getpid());
_ = c.XChangeProperty(dpy, wm.support_win, wm.atoms.net_wm_pid,
c.XA_CARDINAL, 32, c.PropModeReplace,
@ptrCast(@constCast(&pid_val)), 1);
const supported = wm.atoms.supportedList();
_ = c.XChangeProperty(dpy, wm.root, wm.atoms.net_supported,
c.XA_ATOM, 32, c.PropModeReplace,
@ptrCast(@constCast(supported.ptr)), @intCast(supported.len));
var btn_init: [128]client_mod.ButtonEntry = undefined;
for (&btn_init) |*b| b.* = .{ .x = 0, .w = 0, .win = 0 };
wm.bar = .{
.window = 0,
.pixmap = 0,
.draw = null,
.fonts = .{null} ** config.font_names.len,
.colors = undefined,
.buttons = btn_init,
.button_count = 0,
};
bar_mod.create(&wm) catch |err| util.die("bar init: {}\n", .{err});
installSignals();
_ = c.XGrabServer(dpy);
scanWindows(&wm);
_ = c.XUngrabServer(dpy);
event_mod.grabKeys(&wm);
bar_mod.draw(&wm);
for (&config.autostart) |cmd| {
util.spawn(alloc, cmd);
}
var ev: c.XEvent = undefined;
while (wm.running) {
_ = c.XNextEvent(dpy, &ev);
if (wm.running)
event_mod.dispatch(&wm, &ev);
}
cleanup(&wm);
} else {
util.die("wm: cannot open display\n", .{});
}
}
fn installSignals() void {
const act = std.posix.Sigaction{
.handler = .{ .handler = sigTerm },
.mask = std.posix.empty_sigset,
.flags = 0,
};
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
std.posix.sigaction(std.posix.SIG.INT, &act, null);
const chld_act = std.posix.Sigaction{
.handler = .{ .handler = sigChld },
.mask = std.posix.empty_sigset,
.flags = 0,
};
std.posix.sigaction(std.posix.SIG.CHLD, &chld_act, null);
}
fn scanWindows(wm: *WM) void {
var root_ret: c.Window = 0;
var parent_ret: c.Window = 0;
var children: [*c]c.Window = null;
var nchildren: c_uint = 0;
if (c.XQueryTree(wm.display, wm.root, &root_ret, &parent_ret, &children, &nchildren) != 0) {
if (children != null) {
var i: c_uint = 0;
while (i < nchildren) : (i += 1) {
var wa: c.XWindowAttributes = undefined;
if (c.XGetWindowAttributes(wm.display, children[@intCast(i)], &wa) != 0) {
if (wa.override_redirect == 0 and wa.map_state == c.IsViewable) {
client_mod.manage(wm, children[@intCast(i)]) catch {};
}
}
}
_ = c.XFree(children);
}
}
}
fn cleanup(wm: *WM) void {
var it = wm.clients;
while (it) |cl| {
const next = cl.next;
client_mod.unmanage(wm, cl);
it = next;
}
bar_mod.destroy(wm);
if (wm.support_win != 0)
_ = c.XDestroyWindow(wm.display, wm.support_win);
_ = c.XCloseDisplay(wm.display);
}