I am trying to write a simple window manager with xcb in zig where there is an event loop with some possible events (key press, ...). My system is running an arch linux and all the necessary libraries are installed. Here is my main.zig:
const std = @import("std");
const print = std.debug.print;
const xcb = @cImport({
@cInclude("xcb/xcb.h");
});
pub fn main() !void {
const conn: ?*xcb.xcb_connection_t = xcb.xcb_connect(null, null);
defer xcb.xcb_disconnect(conn);
if (conn == null or xcb.xcb_connection_has_error(conn) != 0) {
print("Failed to connect to X server\n", .{});
return error.XConnectionFailed;
}
const xcbsetup = xcb.xcb_get_setup(conn);
if (xcbsetup == null) {
print("Failed to get XCB setup\n", .{});
return error.XCBSetupFailed;
}
const screen_iter = xcb.xcb_setup_roots_iterator(xcbsetup);
if (screen_iter.rem == 0) {
print("No screens found\n", .{});
return error.NoScreens;
}
const screen: *xcb.xcb_screen_t = screen_iter.data;
const win = xcb.xcb_generate_id(conn);
// const event_mask = xcb.XCB_EVENT_MASK_KEY_PRESS;
// Event masks: capturing all possible events
const event_mask = xcb.XCB_EVENT_MASK_EXPOSURE |
xcb.XCB_EVENT_MASK_KEY_PRESS |
xcb.XCB_EVENT_MASK_KEY_RELEASE |
xcb.XCB_EVENT_MASK_BUTTON_PRESS |
xcb.XCB_EVENT_MASK_BUTTON_RELEASE |
xcb.XCB_EVENT_MASK_POINTER_MOTION |
xcb.XCB_EVENT_MASK_ENTER_WINDOW |
xcb.XCB_EVENT_MASK_LEAVE_WINDOW |
xcb.XCB_EVENT_MASK_FOCUS_CHANGE |
xcb.XCB_EVENT_MASK_STRUCTURE_NOTIFY;
const value_list = [_]u32{event_mask};
_ = xcb.xcb_create_window(
conn,
xcb.XCB_COPY_FROM_PARENT,
win,
screen.root,
0,
0, // x, y
400,
300, // width, height
10, // border width
xcb.XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen.root_visual,
xcb.XCB_CW_EVENT_MASK,
&value_list,
);
_ = xcb.xcb_map_window(conn, win);
_ = xcb.xcb_flush(conn);
while (true) {
const event = xcb.xcb_wait_for_event(conn);
if (event == null) {
print("Error waiting for event\n", .{});
continue;
}
switch (event.*.response_type) {
xcb.XCB_KEY_PRESS => {
const key_event: *xcb.xcb_key_press_event_t = @ptrCast(event);
print("Key pressed: code = {}, state = {}\n", .{ key_event.detail, key_event.state });
},
else => {
print("Unhandled event of type {}.\n", .{event.*.response_type});
},
}
// Free the event
// xcb.xcb_free_event(conn, event);
}
}
Here is my build.zig:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "blakewm",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe.linkSystemLibrary("xcb");
b.installArtifact(exe);
}
I don't think there is a need to show my build.zig.zon since there is nothing special in it. Now there are two problems:
- I have no idea how one can free events in the event loop as in the c example they use
free(ev). - When I build the code with
zig buildand then run it with
Xephyr -ac -br -noreset -screen 800x600 :1 & sleep 1; DISPLAY=:1 ./zig-out/bin/blakewm
I see the following messages and cannot have it write all the keys being pressed:
The XKEYBOARD keymap compiler (xkbcomp) reports:
> Warning: Could not resolve keysym XF86RefreshRateToggle
> Warning: Could not resolve keysym XF86Accessibility
> Warning: Could not resolve keysym XF86DoNotDisturb
Errors from xkbcomp are not fatal to the X server
Segmentation fault at address 0x0
???:?:?: 0x0 in ??? (???)
./runb.sh: line 1: 2898 Aborted (core dumped) DISPLAY=:1 ./zig-out/bin/blakewm
runb.sh just contains the Xephyr call so that I don't have to type it every time I want to test the code.