From 0f6d1a0842f3966975d4ddab7553f585bbc759e1 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Mon, 31 Aug 2026 01:09:53 +0200 Subject: [PATCH] xcb: add sample --- packages/xcb/build.zig | 29 ++++++++++++ packages/xcb/sample/root.zig | 92 ++++++++++++++++++++++++++++++++++++ packages/xcb/src/root.zig | 3 +- 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 packages/xcb/sample/root.zig diff --git a/packages/xcb/build.zig b/packages/xcb/build.zig index 2cb8fd0..2fd2b28 100644 --- a/packages/xcb/build.zig +++ b/packages/xcb/build.zig @@ -2,10 +2,13 @@ const std = @import("std"); pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); const mod = b.addModule("xcb", .{ .root_source_file = b.path("src/root.zig"), .target = target, + .optimize = optimize, + .link_libc = true, }); mod.linkSystemLibrary("xcb", .{}); @@ -18,4 +21,30 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Run tests"); test_step.dependOn(&run_mod_tests.step); + + const sample_module = b.createModule(.{ + .root_source_file = b.path("sample/root.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "xcb", .module = mod }, + }, + }); + + const sample_exe = b.addExecutable(.{ + .name = "sample", + .root_module = sample_module, + }); + + const sample_install = b.addInstallArtifact(sample_exe, .{}); + + const run_sample = b.addRunArtifact(sample_exe); + run_sample.step.dependOn(&sample_install.step); + + if (b.args) |args| { + run_sample.addArgs(args); + } + + const sample_step = b.step("sample", "Run sample"); + sample_step.dependOn(&run_sample.step); } diff --git a/packages/xcb/sample/root.zig b/packages/xcb/sample/root.zig new file mode 100644 index 0000000..2bbd720 --- /dev/null +++ b/packages/xcb/sample/root.zig @@ -0,0 +1,92 @@ +const std = @import("std"); +const xcb = @import("xcb"); + +const WM_PROTOCOLS = "WM_PROTOCOLS"; +const WM_DELETE_WINDOW = "WM_DELETE_WINDOW"; + +pub fn main(init: std.process.Init) !void { + _ = init; + + const connection = xcb.import.xcb_connect(null, null); + defer xcb.import.xcb_disconnect(connection); + + { + const res = xcb.import.xcb_connection_has_error(connection); + if (res != 0) { + std.debug.print("Error: xcb_connection_has_error returned {}", .{res}); + return error.XcbConnection; + } + } + + const screen = xcb.import.xcb_setup_roots_iterator(xcb.import.xcb_get_setup(connection)).data.?; + + const mask: xcb.Cw = .{ + .BACK_PIXEL = true, + .EVENT_MASK = true, + }; + + const values = [_]u32{ + screen.white_pixel, + @bitCast(xcb.EventMask{ + .KEY_PRESS = true, + }), + }; + + const window: xcb.Window = @enumFromInt(xcb.import.xcb_generate_id(connection)); + _ = xcb.import.xcb_create_window( + connection, + xcb.COPY_FROM_PARENT, + window, + screen.root, + 10, + 10, + 640, + 480, + 1, + @intFromEnum(xcb.WindowClass.INPUT_OUTPUT), + screen.root_visual, + @bitCast(mask), + &values, + ); + + const wm_protocols_atom_cookie = xcb.import.xcb_intern_atom(connection, 1, WM_PROTOCOLS.len, WM_PROTOCOLS); + const wm_delete_window_atom_cookie = xcb.import.xcb_intern_atom(connection, 0, WM_DELETE_WINDOW.len, WM_DELETE_WINDOW); + + const wm_protocols_atom_reply: *xcb.InternAtomReply = xcb.import.xcb_intern_atom_reply(connection, wm_protocols_atom_cookie, null) orelse return error.XcbInternAtom; + const wm_delete_window_atom_reply: *xcb.InternAtomReply = xcb.import.xcb_intern_atom_reply(connection, wm_delete_window_atom_cookie, null) orelse return error.XcbInternAtom; + + _ = xcb.import.xcb_change_property( + connection, + @intFromEnum(xcb.PropMode.REPLACE), + window, + wm_protocols_atom_reply.atom, + .ATOM, + 32, + 1, + &wm_delete_window_atom_reply.atom, + ); + + _ = xcb.import.xcb_map_window(connection, window); + _ = xcb.import.xcb_flush(connection); + + var running = true; + while (running) { + const maybe_event: ?*xcb.GenericEvent = xcb.import.xcb_wait_for_event(connection); + if (maybe_event) |event| { + defer std.c.free(event); + + switch (event.response_type & ~@as(u8, 0x80)) { + xcb.CLIENT_MESSAGE => { + const client_message_event: *xcb.ClientMessageEvent = @ptrCast(event); + if (client_message_event.data.data32[0] == @intFromEnum(wm_delete_window_atom_reply.atom)) { + running = false; + } + }, + else => {}, + } + } else { + std.debug.print("Error: xcb_wait_for_event returned null", .{}); + return error.XcbWaitForEvent; + } + } +} diff --git a/packages/xcb/src/root.zig b/packages/xcb/src/root.zig index 995514e..c9f0e6f 100644 --- a/packages/xcb/src/root.zig +++ b/packages/xcb/src/root.zig @@ -4086,6 +4086,7 @@ pub const SET_MODIFIER_MAPPING = 118; pub const GET_MODIFIER_MAPPING = 119; pub const NO_OPERATION = 127; +pub const COPY_FROM_PARENT = 0; pub const NO_SYMBOL = 0; pub const import = struct { @@ -4728,7 +4729,7 @@ pub const import = struct { pub extern fn xcb_connect_to_fd(fd: c_int, auth_info: [*c]AuthInfo) ?*Connection; pub extern fn xcb_disconnect(c: ?*Connection) void; pub extern fn xcb_parse_display(name: [*c]const u8, host: [*c][*c]u8, display: [*c]c_int, screen: [*c]c_int) c_int; - pub extern fn xcb_connect(displayname: [*c]const u8, screenp: [*c]c_int) ?*Connection; + pub extern fn xcb_connect(displayname: [*c]const u8, screenp: [*c]c_int) *Connection; pub extern fn xcb_connect_to_display_with_auth_info(display: [*c]const u8, auth: [*c]AuthInfo, screen: [*c]c_int) ?*Connection; pub extern fn xcb_generate_id(c: ?*Connection) u32; pub extern fn xcb_total_read(c: ?*Connection) u64;