From the first post, I have been trying to understand how xcb library works and what are all those commands that was used previously. Today, I will explain how to get key press event in an event loop with xcb and zig.
This time, let's try to go step by step and review the code:
const std = @import("std");
const print = std.debug.print;
const c = @cImport({
@cInclude("xcb/xcb.h");
@cInclude("stdlib.h");
});
This code does
- importing standard library in Zig and assigning it to std,
- renaming the
std.debug.print
function toprint
for easier use, - and importing two important C libraries.
stdlib.h
has free function which is good to free event pointers at the end of each event loop.
Then, we start the main function as
pub fn main() !void {
const conn: ?*c.xcb_connection_t = c.xcb_connect(null, null);
defer c.xcb_disconnect(conn);
if (conn == null or c.xcb_connection_has_error(conn) != 0) {
print("Failed to connect to X server\n", .{});
return error.XConnectionFailed;
}
const xcbsetup = c.xcb_get_setup(conn);
if (xcbsetup == null) {
print("Failed to get XCB setup\n", .{});
return error.XCBSetupFailed;
}
const screen_iter = c.xcb_setup_roots_iterator(xcbsetup);
if (screen_iter.rem == 0) {
print("No screens found\n", .{});
return error.NoScreens;
}
const screen: *c.xcb_screen_t = screen_iter.data;
const win = c.xcb_generate_id(conn);
-
xcb_connection
establishes connection to the xorg server. - The rest of the lines help us access our screen and generate a window in it.
// Event masks: capturing all possible events
const event_mask = c.XCB_EVENT_MASK_EXPOSURE |
c.XCB_EVENT_MASK_KEY_PRESS |
c.XCB_EVENT_MASK_KEY_RELEASE |
c.XCB_EVENT_MASK_BUTTON_PRESS |
c.XCB_EVENT_MASK_BUTTON_RELEASE |
c.XCB_EVENT_MASK_POINTER_MOTION |
c.XCB_EVENT_MASK_ENTER_WINDOW |
c.XCB_EVENT_MASK_LEAVE_WINDOW |
c.XCB_EVENT_MASK_FOCUS_CHANGE |
c.XCB_EVENT_MASK_STRUCTURE_NOTIFY;
const value_list = [_]u32{event_mask};
_ = c.xcb_create_window(
conn,
c.XCB_COPY_FROM_PARENT,
win,
screen.root,
0,
0, // x, y
400,
300, // width, height
10, // border width
c.XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen.root_visual,
c.XCB_CW_EVENT_MASK,
&value_list,
);
_ = c.xcb_map_window(conn, win);
_ = c.xcb_flush(conn);
We first define all the events that we are waiting for!
xcb
library is not only to be used for window manager and it can be used by other applications as well. Those applications do not need to listen every even! We specify the events that we want by defining the event mask. Then we pass it to xcb to create a window for us.This window is not automatically shown to users, so we have to use the map window function to show it.
Finally, we use flush to send all the info to the xord server with our connection so that we can be ready to get events. apparently if we dont do this, we will receive those info instead of events (not completely understood it yet!).
while (true) {
const event = c.xcb_wait_for_event(conn);
defer c.free(event);
if (event == null) {
print("Error waiting for event\nBreaking from the event loop!\n", .{});
break;
}
switch (event.*.response_type) {
c.XCB_KEY_PRESS => {
const key_event: *c.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});
},
}
}
}
This is our event loop at the end of the main function. event is a pointer to struct type and in C we get its field by event->response_type
. In Zig this is done by .*
instead of ->
. In our switch we first check what is the type of the event passed to us and then just type cast it to the corresponding type by @ptrCast()
.
Now that this is done, we just have to add exe.linklibC();
in our build.zig
file so that our compiler links libc when compiling this code and we are good to go. Here is our build.zig
file:
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");
exe.linkLibC();
b.installArtifact(exe);
}
As usual, I built this code with zig build
and ran it using
Xephyr -ac -br -noreset -screen 800x600 :1 & sleep 1; DISPLAY=:1 ./zig-out/bin/blakewm
Some references to learn xcb
, Zig
:
Top comments (0)