Edit example 00_client_connect for part 2 of Wayland from the Wire

https://zig.news/leroycep/wayland-from-the-wire-part-2-1gb7
dev
LeRoyce Pearson 2023-12-19 13:59:52 -07:00
parent c3ca5aa772
commit 4a8254cca6
2 changed files with 31 additions and 40 deletions

View File

@ -167,6 +167,7 @@ pub fn main() !void {
// Create a surface using wl_compositor::create_surface // Create a surface using wl_compositor::create_surface
const surface_id = next_id; const surface_id = next_id;
next_id += 1; next_id += 1;
// https://wayland.app/protocols/wayland#wl_compositor:request:create_surface
const WL_COMPOSITOR_REQUEST_CREATE_SURFACE = 0; const WL_COMPOSITOR_REQUEST_CREATE_SURFACE = 0;
try writeRequest(socket, compositor_id, WL_COMPOSITOR_REQUEST_CREATE_SURFACE, &[_]u32{ try writeRequest(socket, compositor_id, WL_COMPOSITOR_REQUEST_CREATE_SURFACE, &[_]u32{
// id: new_id<wl_surface> // id: new_id<wl_surface>
@ -176,6 +177,7 @@ pub fn main() !void {
// Create an xdg_surface // Create an xdg_surface
const xdg_surface_id = next_id; const xdg_surface_id = next_id;
next_id += 1; next_id += 1;
// https://wayland.app/protocols/xdg-shell#xdg_wm_base:request:get_xdg_surface
const XDG_WM_BASE_REQUEST_GET_XDG_SURFACE = 2; const XDG_WM_BASE_REQUEST_GET_XDG_SURFACE = 2;
try writeRequest(socket, xdg_wm_base_id, XDG_WM_BASE_REQUEST_GET_XDG_SURFACE, &[_]u32{ try writeRequest(socket, xdg_wm_base_id, XDG_WM_BASE_REQUEST_GET_XDG_SURFACE, &[_]u32{
// id: new_id<xdg_surface> // id: new_id<xdg_surface>
@ -187,32 +189,25 @@ pub fn main() !void {
// Get the xdg_surface as an xdg_toplevel object // Get the xdg_surface as an xdg_toplevel object
const xdg_toplevel_id = next_id; const xdg_toplevel_id = next_id;
next_id += 1; next_id += 1;
// https://wayland.app/protocols/xdg-shell#xdg_surface:request:get_toplevel
const XDG_SURFACE_REQUEST_GET_TOPLEVEL = 1; const XDG_SURFACE_REQUEST_GET_TOPLEVEL = 1;
try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_GET_TOPLEVEL, &[_]u32{ try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_GET_TOPLEVEL, &[_]u32{
// id: new_id<xdg_surface> // id: new_id<xdg_surface>
xdg_toplevel_id, xdg_toplevel_id,
}); });
// Commit the surface. This tells wayland that we are done making changes, and it can display all the changes that have been // Commit the surface. This tells the compositor that the current batch of
// made so far. // changes is ready, and they can now be applied.
// https://wayland.app/protocols/wayland#wl_surface:request:commit
const WL_SURFACE_REQUEST_COMMIT = 6; const WL_SURFACE_REQUEST_COMMIT = 6;
try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_COMMIT, &[_]u32{}); try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_COMMIT, &[_]u32{});
// create another wl_callback
const create_surface_done_id = next_id;
next_id += 1;
const WL_DISPLAY_REQUEST_DONE = 0;
try writeRequest(socket, display_id, WL_DISPLAY_REQUEST_DONE, &[_]u32{create_surface_done_id});
// Wait for the surface to be configured before moving on // Wait for the surface to be configured before moving on
var done = false; while (true) {
var surface_configured = false;
while (!done or !surface_configured) {
const event = try Event.read(socket, &message_buffer); const event = try Event.read(socket, &message_buffer);
if (event.header.object_id == create_surface_done_id) { if (event.header.object_id == xdg_surface_id) {
done = true;
} else if (event.header.object_id == xdg_surface_id) {
switch (event.header.opcode) { switch (event.header.opcode) {
// https://wayland.app/protocols/xdg-shell#xdg_surface:event:configure // https://wayland.app/protocols/xdg-shell#xdg_surface:event:configure
0 => { 0 => {
@ -226,26 +221,10 @@ pub fn main() !void {
serial, serial,
}); });
surface_configured = true; try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_COMMIT, &[_]u32{});
},
else => return error.InvalidOpcode, // The surface has been configured! We can move on
} break;
} else if (event.header.object_id == display_id) {
switch (event.header.opcode) {
// https://wayland.app/protocols/wayland#wl_display:event:error
0 => {
const object_id: u32 = @bitCast(event.body[0..4].*);
const error_code: u32 = @bitCast(event.body[4..8].*);
const error_message_len: u32 = @bitCast(event.body[8..12].*);
const error_message = event.body[12 .. error_message_len - 1 :0];
std.log.warn("wl_display:error({}, {}, \"{}\")", .{ object_id, error_code, std.zig.fmtEscapes(error_message) });
},
// https://wayland.app/protocols/wayland#wl_display:event:delete_id
1 => {
// wl_display:delete_id tells us that we can reuse an id. In this article we log it, but
// otherwise ignore it.
const name: u32 = @bitCast(event.body[0..4].*);
std.log.debug("wl_display:delete_id({})", .{name});
}, },
else => return error.InvalidOpcode, else => return error.InvalidOpcode,
} }
@ -261,7 +240,6 @@ pub fn main() !void {
const shared_memory_pool_fd = try std.os.memfd_create("my-wayland-framebuffer", 0); const shared_memory_pool_fd = try std.os.memfd_create("my-wayland-framebuffer", 0);
try std.os.ftruncate(shared_memory_pool_fd, shared_memory_pool_len); try std.os.ftruncate(shared_memory_pool_fd, shared_memory_pool_len);
const shared_memory_pool_bytes = try std.os.mmap(null, shared_memory_pool_len, std.os.PROT.READ | std.os.PROT.WRITE, std.os.MAP.SHARED, shared_memory_pool_fd, 0);
// Create a wl_shm_pool (wayland shared memory pool). This will be used to create framebuffers, // Create a wl_shm_pool (wayland shared memory pool). This will be used to create framebuffers,
// though in this article we only plan on creating one. // though in this article we only plan on creating one.
@ -270,14 +248,16 @@ pub fn main() !void {
shm_id, shm_id,
&next_id, &next_id,
shared_memory_pool_fd, shared_memory_pool_fd,
@intCast(shared_memory_pool_bytes.len), @intCast(shared_memory_pool_len),
); );
// Now we allocate a framebuffer from the shared memory pool // Now we allocate a framebuffer from the shared memory pool
const wl_buffer_id = next_id; const wl_buffer_id = next_id;
next_id += 1; next_id += 1;
// https://wayland.app/protocols/wayland#wl_shm_pool:request:create_buffer
const WL_SHM_POOL_REQUEST_CREATE_BUFFER = 0; const WL_SHM_POOL_REQUEST_CREATE_BUFFER = 0;
// https://wayland.app/protocols/wayland#wl_shm:enum:format
const WL_SHM_POOL_ENUM_FORMAT_ARGB8888 = 0; const WL_SHM_POOL_ENUM_FORMAT_ARGB8888 = 0;
try writeRequest(socket, wl_shm_pool_id, WL_SHM_POOL_REQUEST_CREATE_BUFFER, &[_]u32{ try writeRequest(socket, wl_shm_pool_id, WL_SHM_POOL_REQUEST_CREATE_BUFFER, &[_]u32{
// id: new_id<wl_buffer>, // id: new_id<wl_buffer>,
@ -294,7 +274,8 @@ pub fn main() !void {
WL_SHM_POOL_ENUM_FORMAT_ARGB8888, WL_SHM_POOL_ENUM_FORMAT_ARGB8888,
}); });
// Now we turn the framebuffer we just allocated into a slice on our side for ease of use. // Now we turn the shared memory pool and the framebuffer we just allocated into slices on our side for ease of use.
const shared_memory_pool_bytes = try std.os.mmap(null, shared_memory_pool_len, std.os.PROT.READ | std.os.PROT.WRITE, std.os.MAP.SHARED, shared_memory_pool_fd, 0);
const framebuffer = @as([*]Pixel, @ptrCast(shared_memory_pool_bytes.ptr))[0 .. shared_memory_pool_bytes.len / @sizeOf(Pixel)]; const framebuffer = @as([*]Pixel, @ptrCast(shared_memory_pool_bytes.ptr))[0 .. shared_memory_pool_bytes.len / @sizeOf(Pixel)];
// put some interesting colors into the framebuffer // put some interesting colors into the framebuffer
@ -363,6 +344,7 @@ pub fn main() !void {
// We respond with the number it sent us, so it knows which configure we are responding to. // We respond with the number it sent us, so it knows which configure we are responding to.
serial, serial,
}); });
try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_COMMIT, &[_]u32{});
}, },
else => return error.InvalidOpcode, else => return error.InvalidOpcode,
} }
@ -428,7 +410,10 @@ pub fn main() !void {
pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 { pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 {
const xdg_runtime_dir_path = try std.process.getEnvVarOwned(gpa, "XDG_RUNTIME_DIR"); const xdg_runtime_dir_path = try std.process.getEnvVarOwned(gpa, "XDG_RUNTIME_DIR");
defer gpa.free(xdg_runtime_dir_path); defer gpa.free(xdg_runtime_dir_path);
const display_name = try std.process.getEnvVarOwned(gpa, "WAYLAND_DISPLAY"); const display_name = std.process.getEnvVarOwned(gpa, "WAYLAND_DISPLAY") catch |err| switch (err) {
error.EnvironmentVariableNotFound => return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, "wayland-0" }),
else => return err,
};
defer gpa.free(display_name); defer gpa.free(display_name);
return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, display_name }); return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, display_name });
@ -505,7 +490,7 @@ pub fn writeWlShmRequestCreatePool(socket: std.net.Stream, wl_shm_id: u32, next_
// documentation calling for 3: wl_shm_pool_id, fd, and size. This is because `fd` is sent in the control message, // documentation calling for 3: wl_shm_pool_id, fd, and size. This is because `fd` is sent in the control message,
// and so not included in the regular message body. // and so not included in the regular message body.
// Send the file descriptor through a control message // Create the message header as usual
const message_bytes = std.mem.sliceAsBytes(&message); const message_bytes = std.mem.sliceAsBytes(&message);
const header = Header{ const header = Header{
.object_id = wl_shm_id, .object_id = wl_shm_id,
@ -527,6 +512,8 @@ pub fn writeWlShmRequestCreatePool(socket: std.net.Stream, wl_shm_id: u32, next_
}, },
}; };
// Send the file descriptor through a control message
// This is the control message! It is not a fixed size struct. Instead it varies depending on the message you want to send. // This is the control message! It is not a fixed size struct. Instead it varies depending on the message you want to send.
// C uses macros to define it, here we make a comptime function instead. // C uses macros to define it, here we make a comptime function instead.
const control_message = cmsg(std.os.fd_t){ const control_message = cmsg(std.os.fd_t){
@ -552,6 +539,7 @@ pub fn writeWlShmRequestCreatePool(socket: std.net.Stream, wl_shm_id: u32, next_
return error.ConnectionClosed; return error.ConnectionClosed;
} }
// Wait to increment until we know the message has been sent
next_id.* += 1; next_id.* += 1;
return wl_shm_pool_id; return wl_shm_pool_id;
} }

View File

@ -6,7 +6,10 @@ pub const xdg = @import("./xdg.zig");
pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 { pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 {
const xdg_runtime_dir_path = try std.process.getEnvVarOwned(gpa, "XDG_RUNTIME_DIR"); const xdg_runtime_dir_path = try std.process.getEnvVarOwned(gpa, "XDG_RUNTIME_DIR");
defer gpa.free(xdg_runtime_dir_path); defer gpa.free(xdg_runtime_dir_path);
const display_name = try std.process.getEnvVarOwned(gpa, "WAYLAND_DISPLAY"); const display_name = std.process.getEnvVarOwned(gpa, "WAYLAND_DISPLAY") catch |err| switch (err) {
error.EnvironmentVariableNotFound => return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, "wayland-0" }),
else => return err,
};
defer gpa.free(display_name); defer gpa.free(display_name);
return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, display_name }); return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, display_name });