Introduce read and write abstractions earlier
parent
0042b56b93
commit
c3ca5aa772
|
@ -10,6 +10,9 @@ const XDG_WM_BASE_VERSION = 2;
|
||||||
/// https://wayland.app/protocols/xdg-shell#xdg_surface:request:ack_configure
|
/// https://wayland.app/protocols/xdg-shell#xdg_surface:request:ack_configure
|
||||||
const XDG_SURFACE_REQUEST_ACK_CONFIGURE = 4;
|
const XDG_SURFACE_REQUEST_ACK_CONFIGURE = 4;
|
||||||
|
|
||||||
|
// https://wayland.app/protocols/wayland#wl_registry:event:global
|
||||||
|
const WL_REGISTRY_EVENT_GLOBAL = 0;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var general_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
var general_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = general_allocator.deinit();
|
defer _ = general_allocator.deinit();
|
||||||
|
@ -21,6 +24,7 @@ pub fn main() !void {
|
||||||
const socket = try std.net.connectUnixSocket(display_path);
|
const socket = try std.net.connectUnixSocket(display_path);
|
||||||
defer socket.close();
|
defer socket.close();
|
||||||
|
|
||||||
|
const display_id = 1;
|
||||||
var next_id: u32 = 2;
|
var next_id: u32 = 2;
|
||||||
|
|
||||||
// reserve an object id for the registry
|
// reserve an object id for the registry
|
||||||
|
@ -43,7 +47,6 @@ pub fn main() !void {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// create a sync callback so we know when we are caught up with the server
|
// create a sync callback so we know when we are caught up with the server
|
||||||
const display_id = 1;
|
|
||||||
const registry_done_callback_id = next_id;
|
const registry_done_callback_id = next_id;
|
||||||
next_id += 1;
|
next_id += 1;
|
||||||
|
|
||||||
|
@ -68,38 +71,27 @@ pub fn main() !void {
|
||||||
// How do we know that the opcode for WL_REGISTRY_REQUEST is 0? Because it is the first `request` in the protocol for `wl_registry`.
|
// How do we know that the opcode for WL_REGISTRY_REQUEST is 0? Because it is the first `request` in the protocol for `wl_registry`.
|
||||||
const WL_REGISTRY_REQUEST_BIND = 0;
|
const WL_REGISTRY_REQUEST_BIND = 0;
|
||||||
|
|
||||||
var message_bytes = std.ArrayList(u8).init(gpa);
|
var message_buffer = std.ArrayList(u8).init(gpa);
|
||||||
defer message_bytes.deinit();
|
defer message_buffer.deinit();
|
||||||
while (true) {
|
while (true) {
|
||||||
message_bytes.shrinkRetainingCapacity(0);
|
const event = try Event.read(socket, &message_buffer);
|
||||||
|
|
||||||
var header: Header = undefined;
|
|
||||||
const header_bytes_read = try socket.readAll(std.mem.asBytes(&header));
|
|
||||||
if (header_bytes_read < @sizeOf(Header)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try message_bytes.resize(header.size - @sizeOf(Header));
|
|
||||||
const message_bytes_read = try socket.readAll(message_bytes.items);
|
|
||||||
if (message_bytes_read < message_bytes.items.len) {
|
|
||||||
return error.UnexpectedEOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse event messages based on which object it is for
|
// Parse event messages based on which object it is for
|
||||||
if (header.object_id == registry_done_callback_id) {
|
if (event.header.object_id == registry_done_callback_id) {
|
||||||
// No need to parse the message, there is only one event
|
// No need to parse the message body, there is only one possible opcode
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.object_id == registry_id and header.opcode == 0) {
|
if (event.header.object_id == registry_id and event.header.opcode == WL_REGISTRY_EVENT_GLOBAL) {
|
||||||
// Parse out the fields of the global event
|
// Parse out the fields of the global event
|
||||||
const name: u32 = @bitCast(message_bytes.items[0..4].*);
|
const name: u32 = @bitCast(event.body[0..4].*);
|
||||||
|
|
||||||
const interface_str_len: u32 = @bitCast(message_bytes.items[4..8].*);
|
const interface_str_len: u32 = @bitCast(event.body[4..8].*);
|
||||||
const interface_str: [:0]const u8 = message_bytes.items[8..][0 .. interface_str_len - 1 :0];
|
// The interface_str is `interface_str_len - 1` because `interface_str_len` includes the null pointer
|
||||||
|
const interface_str: [:0]const u8 = event.body[8..][0 .. interface_str_len - 1 :0];
|
||||||
|
|
||||||
const interface_str_len_u32_align = std.mem.alignForward(u32, interface_str_len, @alignOf(u32));
|
const interface_str_len_u32_align = std.mem.alignForward(u32, interface_str_len, @alignOf(u32));
|
||||||
const version: u32 = @bitCast(message_bytes.items[8 + interface_str_len_u32_align ..][0..4].*);
|
const version: u32 = @bitCast(event.body[8 + interface_str_len_u32_align ..][0..4].*);
|
||||||
|
|
||||||
// Check to see if the interface is one of the globals we are looking for
|
// Check to see if the interface is one of the globals we are looking for
|
||||||
if (std.mem.eql(u8, interface_str, "wl_shm")) {
|
if (std.mem.eql(u8, interface_str, "wl_shm")) {
|
||||||
|
@ -110,7 +102,7 @@ pub fn main() !void {
|
||||||
shm_id_opt = next_id;
|
shm_id_opt = next_id;
|
||||||
next_id += 1;
|
next_id += 1;
|
||||||
|
|
||||||
const registry_bind_request_message_body = [_]u32{
|
try writeRequest(socket, registry_id, WL_REGISTRY_REQUEST_BIND, &[_]u32{
|
||||||
// The numeric name of the global we want to bind.
|
// The numeric name of the global we want to bind.
|
||||||
name,
|
name,
|
||||||
|
|
||||||
|
@ -125,16 +117,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
// 3. And the `new_id` part, where we tell it which client id we are giving it
|
// 3. And the `new_id` part, where we tell it which client id we are giving it
|
||||||
shm_id_opt.?,
|
shm_id_opt.?,
|
||||||
};
|
});
|
||||||
|
|
||||||
const registry_bind_request_header = Header{
|
|
||||||
.object_id = registry_id,
|
|
||||||
.opcode = WL_REGISTRY_REQUEST_BIND,
|
|
||||||
.size = @sizeOf(Header) + registry_bind_request_message_body.len * @sizeOf(u32),
|
|
||||||
};
|
|
||||||
|
|
||||||
try socket.writeAll(std.mem.asBytes(®istry_bind_request_header));
|
|
||||||
try socket.writeAll(std.mem.sliceAsBytes(®istry_bind_request_message_body));
|
|
||||||
} else if (std.mem.eql(u8, interface_str, "wl_compositor")) {
|
} else if (std.mem.eql(u8, interface_str, "wl_compositor")) {
|
||||||
if (version < WL_COMPOSITOR_VERSION) {
|
if (version < WL_COMPOSITOR_VERSION) {
|
||||||
std.log.err("compositor supports only {s} version {}, client expected version >= {}", .{ interface_str, version, WL_COMPOSITOR_VERSION });
|
std.log.err("compositor supports only {s} version {}, client expected version >= {}", .{ interface_str, version, WL_COMPOSITOR_VERSION });
|
||||||
|
@ -143,7 +126,7 @@ pub fn main() !void {
|
||||||
compositor_id_opt = next_id;
|
compositor_id_opt = next_id;
|
||||||
next_id += 1;
|
next_id += 1;
|
||||||
|
|
||||||
const registry_bind_request_message_body = [_]u32{
|
try writeRequest(socket, registry_id, WL_REGISTRY_REQUEST_BIND, &[_]u32{
|
||||||
name,
|
name,
|
||||||
"wl_compositor".len + 1, // add one for the required null byte
|
"wl_compositor".len + 1, // add one for the required null byte
|
||||||
@bitCast(@as([4]u8, "wl_c".*)),
|
@bitCast(@as([4]u8, "wl_c".*)),
|
||||||
|
@ -152,16 +135,7 @@ pub fn main() !void {
|
||||||
@bitCast(@as([4]u8, "r\x00\x00\x00".*)),
|
@bitCast(@as([4]u8, "r\x00\x00\x00".*)),
|
||||||
WL_COMPOSITOR_VERSION,
|
WL_COMPOSITOR_VERSION,
|
||||||
compositor_id_opt.?,
|
compositor_id_opt.?,
|
||||||
};
|
});
|
||||||
|
|
||||||
const registry_bind_request_header = Header{
|
|
||||||
.object_id = registry_id,
|
|
||||||
.opcode = WL_REGISTRY_REQUEST_BIND,
|
|
||||||
.size = @sizeOf(Header) + registry_bind_request_message_body.len * @sizeOf(u32),
|
|
||||||
};
|
|
||||||
|
|
||||||
try socket.writeAll(std.mem.asBytes(®istry_bind_request_header));
|
|
||||||
try socket.writeAll(std.mem.sliceAsBytes(®istry_bind_request_message_body));
|
|
||||||
} else if (std.mem.eql(u8, interface_str, "xdg_wm_base")) {
|
} else if (std.mem.eql(u8, interface_str, "xdg_wm_base")) {
|
||||||
if (version < XDG_WM_BASE_VERSION) {
|
if (version < XDG_WM_BASE_VERSION) {
|
||||||
std.log.err("compositor supports only {s} version {}, client expected version >= {}", .{ interface_str, version, XDG_WM_BASE_VERSION });
|
std.log.err("compositor supports only {s} version {}, client expected version >= {}", .{ interface_str, version, XDG_WM_BASE_VERSION });
|
||||||
|
@ -170,7 +144,7 @@ pub fn main() !void {
|
||||||
xdg_wm_base_id_opt = next_id;
|
xdg_wm_base_id_opt = next_id;
|
||||||
next_id += 1;
|
next_id += 1;
|
||||||
|
|
||||||
const registry_bind_request_message_body = [_]u32{
|
try writeRequest(socket, registry_id, WL_REGISTRY_REQUEST_BIND, &[_]u32{
|
||||||
name,
|
name,
|
||||||
"xdg_wm_base".len + 1,
|
"xdg_wm_base".len + 1,
|
||||||
@bitCast(@as([4]u8, "xdg_".*)),
|
@bitCast(@as([4]u8, "xdg_".*)),
|
||||||
|
@ -178,16 +152,7 @@ pub fn main() !void {
|
||||||
@bitCast(@as([4]u8, "ase\x00".*)),
|
@bitCast(@as([4]u8, "ase\x00".*)),
|
||||||
XDG_WM_BASE_VERSION,
|
XDG_WM_BASE_VERSION,
|
||||||
xdg_wm_base_id_opt.?,
|
xdg_wm_base_id_opt.?,
|
||||||
};
|
});
|
||||||
|
|
||||||
const registry_bind_request_header = Header{
|
|
||||||
.object_id = registry_id,
|
|
||||||
.opcode = WL_REGISTRY_REQUEST_BIND,
|
|
||||||
.size = @sizeOf(Header) + registry_bind_request_message_body.len * @sizeOf(u32),
|
|
||||||
};
|
|
||||||
|
|
||||||
try socket.writeAll(std.mem.asBytes(®istry_bind_request_header));
|
|
||||||
try socket.writeAll(std.mem.sliceAsBytes(®istry_bind_request_message_body));
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -243,30 +208,18 @@ pub fn main() !void {
|
||||||
var done = false;
|
var done = false;
|
||||||
var surface_configured = false;
|
var surface_configured = false;
|
||||||
while (!done or !surface_configured) {
|
while (!done or !surface_configured) {
|
||||||
message_bytes.shrinkRetainingCapacity(0);
|
const event = try Event.read(socket, &message_buffer);
|
||||||
|
|
||||||
var header: Header = undefined;
|
if (event.header.object_id == create_surface_done_id) {
|
||||||
const header_bytes_read = try socket.readAll(std.mem.asBytes(&header));
|
|
||||||
if (header_bytes_read < @sizeOf(Header)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try message_bytes.resize(header.size - @sizeOf(Header));
|
|
||||||
const message_bytes_read = try socket.readAll(message_bytes.items);
|
|
||||||
if (message_bytes_read < message_bytes.items.len) {
|
|
||||||
return error.UnexpectedEOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header.object_id == create_surface_done_id) {
|
|
||||||
done = true;
|
done = true;
|
||||||
} else if (header.object_id == xdg_surface_id) {
|
} else if (event.header.object_id == xdg_surface_id) {
|
||||||
switch (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 => {
|
||||||
// The configure event acts as a heartbeat. Every once in a while the compositor will send us
|
// The configure event acts as a heartbeat. Every once in a while the compositor will send us
|
||||||
// a `configure` event, and if our application doesn't respond with an `ack_configure` response
|
// a `configure` event, and if our application doesn't respond with an `ack_configure` response
|
||||||
// it will assume our program has died and destroy the window.
|
// it will assume our program has died and destroy the window.
|
||||||
const serial: u32 = @bitCast(message_bytes.items[0..4].*);
|
const serial: u32 = @bitCast(event.body[0..4].*);
|
||||||
|
|
||||||
try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_ACK_CONFIGURE, &[_]u32{
|
try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_ACK_CONFIGURE, &[_]u32{
|
||||||
// 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.
|
||||||
|
@ -277,27 +230,27 @@ pub fn main() !void {
|
||||||
},
|
},
|
||||||
else => return error.InvalidOpcode,
|
else => return error.InvalidOpcode,
|
||||||
}
|
}
|
||||||
} else if (header.object_id == display_id) {
|
} else if (event.header.object_id == display_id) {
|
||||||
switch (header.opcode) {
|
switch (event.header.opcode) {
|
||||||
// https://wayland.app/protocols/wayland#wl_display:event:error
|
// https://wayland.app/protocols/wayland#wl_display:event:error
|
||||||
0 => {
|
0 => {
|
||||||
const object_id: u32 = @bitCast(message_bytes.items[0..4].*);
|
const object_id: u32 = @bitCast(event.body[0..4].*);
|
||||||
const error_code: u32 = @bitCast(message_bytes.items[4..8].*);
|
const error_code: u32 = @bitCast(event.body[4..8].*);
|
||||||
const error_message_len: u32 = @bitCast(message_bytes.items[8..12].*);
|
const error_message_len: u32 = @bitCast(event.body[8..12].*);
|
||||||
const error_message = message_bytes.items[12 .. error_message_len - 1 :0];
|
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) });
|
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
|
// https://wayland.app/protocols/wayland#wl_display:event:delete_id
|
||||||
1 => {
|
1 => {
|
||||||
// wl_display:delete_id tells us that we can reuse an id. In this article we log it, but
|
// wl_display:delete_id tells us that we can reuse an id. In this article we log it, but
|
||||||
// otherwise ignore it.
|
// otherwise ignore it.
|
||||||
const name: u32 = @bitCast(message_bytes.items[0..4].*);
|
const name: u32 = @bitCast(event.body[0..4].*);
|
||||||
std.log.debug("wl_display:delete_id({})", .{name});
|
std.log.debug("wl_display:delete_id({})", .{name});
|
||||||
},
|
},
|
||||||
else => return error.InvalidOpcode,
|
else => return error.InvalidOpcode,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std.log.warn("unknown event {{ .object_id = {}, .opcode = {x}, .message = \"{}\" }}", .{ header.object_id, header.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(message_bytes.items)) });
|
std.log.warn("unknown event {{ .object_id = {}, .opcode = {x}, .message = \"{}\" }}", .{ event.header.object_id, event.header.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(event.body)) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,28 +348,16 @@ pub fn main() !void {
|
||||||
// Now we finally, finally, get to the main loop of the program.
|
// Now we finally, finally, get to the main loop of the program.
|
||||||
var running = true;
|
var running = true;
|
||||||
while (running) {
|
while (running) {
|
||||||
message_bytes.shrinkRetainingCapacity(0);
|
const event = try Event.read(socket, &message_buffer);
|
||||||
|
|
||||||
var header: Header = undefined;
|
if (event.header.object_id == xdg_surface_id) {
|
||||||
const header_bytes_read = try socket.readAll(std.mem.asBytes(&header));
|
switch (event.header.opcode) {
|
||||||
if (header_bytes_read < @sizeOf(Header)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try message_bytes.resize(header.size - @sizeOf(Header));
|
|
||||||
const message_bytes_read = try socket.readAll(message_bytes.items);
|
|
||||||
if (message_bytes_read < message_bytes.items.len) {
|
|
||||||
return error.UnexpectedEOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header.object_id == xdg_surface_id) {
|
|
||||||
switch (header.opcode) {
|
|
||||||
// https://wayland.app/protocols/xdg-shell#xdg_surface:event:configure
|
// https://wayland.app/protocols/xdg-shell#xdg_surface:event:configure
|
||||||
0 => {
|
0 => {
|
||||||
// The configure event acts as a heartbeat. Every once in a while the compositor will send us
|
// The configure event acts as a heartbeat. Every once in a while the compositor will send us
|
||||||
// a `configure` event, and if our application doesn't respond with an `ack_configure` response
|
// a `configure` event, and if our application doesn't respond with an `ack_configure` response
|
||||||
// it will assume our program has died and destroy the window.
|
// it will assume our program has died and destroy the window.
|
||||||
const serial: u32 = @bitCast(message_bytes.items[0..4].*);
|
const serial: u32 = @bitCast(event.body[0..4].*);
|
||||||
|
|
||||||
try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_ACK_CONFIGURE, &[_]u32{
|
try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_ACK_CONFIGURE, &[_]u32{
|
||||||
// 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.
|
||||||
|
@ -425,16 +366,16 @@ pub fn main() !void {
|
||||||
},
|
},
|
||||||
else => return error.InvalidOpcode,
|
else => return error.InvalidOpcode,
|
||||||
}
|
}
|
||||||
} else if (header.object_id == xdg_toplevel_id) {
|
} else if (event.header.object_id == xdg_toplevel_id) {
|
||||||
switch (header.opcode) {
|
switch (event.header.opcode) {
|
||||||
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:event:configure
|
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:event:configure
|
||||||
0 => {
|
0 => {
|
||||||
// The xdg_toplevel:configure event asks us to resize the window. For now, we will ignore it expect to
|
// The xdg_toplevel:configure event asks us to resize the window. For now, we will ignore it expect to
|
||||||
// log it.
|
// log it.
|
||||||
const width: u32 = @bitCast(message_bytes.items[0..4].*);
|
const width: u32 = @bitCast(event.body[0..4].*);
|
||||||
const height: u32 = @bitCast(message_bytes.items[4..8].*);
|
const height: u32 = @bitCast(event.body[4..8].*);
|
||||||
const states_len: u32 = @bitCast(message_bytes.items[8..12].*);
|
const states_len: u32 = @bitCast(event.body[8..12].*);
|
||||||
const states = @as([*]u32, @ptrCast(@alignCast(message_bytes.items[12..].ptr)))[0..states_len];
|
const states = @as([*]const u32, @ptrCast(@alignCast(event.body[12..].ptr)))[0..states_len];
|
||||||
|
|
||||||
std.log.debug("xdg_toplevel:configure({}, {}, {any})", .{ width, height, states });
|
std.log.debug("xdg_toplevel:configure({}, {}, {any})", .{ width, height, states });
|
||||||
},
|
},
|
||||||
|
@ -450,8 +391,8 @@ pub fn main() !void {
|
||||||
3 => std.log.debug("xdg_toplevel:wm_capabilities()", .{}),
|
3 => std.log.debug("xdg_toplevel:wm_capabilities()", .{}),
|
||||||
else => return error.InvalidOpcode,
|
else => return error.InvalidOpcode,
|
||||||
}
|
}
|
||||||
} else if (header.object_id == wl_buffer_id) {
|
} else if (event.header.object_id == wl_buffer_id) {
|
||||||
switch (header.opcode) {
|
switch (event.header.opcode) {
|
||||||
// https://wayland.app/protocols/wayland#wl_buffer:event:release
|
// https://wayland.app/protocols/wayland#wl_buffer:event:release
|
||||||
0 => {
|
0 => {
|
||||||
// The xdg_toplevel:release event let's us know that it is safe to reuse the buffer now.
|
// The xdg_toplevel:release event let's us know that it is safe to reuse the buffer now.
|
||||||
|
@ -459,27 +400,27 @@ pub fn main() !void {
|
||||||
},
|
},
|
||||||
else => return error.InvalidOpcode,
|
else => return error.InvalidOpcode,
|
||||||
}
|
}
|
||||||
} else if (header.object_id == display_id) {
|
} else if (event.header.object_id == display_id) {
|
||||||
switch (header.opcode) {
|
switch (event.header.opcode) {
|
||||||
// https://wayland.app/protocols/wayland#wl_display:event:error
|
// https://wayland.app/protocols/wayland#wl_display:event:error
|
||||||
0 => {
|
0 => {
|
||||||
const object_id: u32 = @bitCast(message_bytes.items[0..4].*);
|
const object_id: u32 = @bitCast(event.body[0..4].*);
|
||||||
const error_code: u32 = @bitCast(message_bytes.items[4..8].*);
|
const error_code: u32 = @bitCast(event.body[4..8].*);
|
||||||
const error_message_len: u32 = @bitCast(message_bytes.items[8..12].*);
|
const error_message_len: u32 = @bitCast(event.body[8..12].*);
|
||||||
const error_message = message_bytes.items[12 .. error_message_len - 1 :0];
|
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) });
|
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
|
// https://wayland.app/protocols/wayland#wl_display:event:delete_id
|
||||||
1 => {
|
1 => {
|
||||||
// wl_display:delete_id tells us that we can reuse an id. In this article we log it, but
|
// wl_display:delete_id tells us that we can reuse an id. In this article we log it, but
|
||||||
// otherwise ignore it.
|
// otherwise ignore it.
|
||||||
const name: u32 = @bitCast(message_bytes.items[0..4].*);
|
const name: u32 = @bitCast(event.body[0..4].*);
|
||||||
std.log.debug("wl_display:delete_id({})", .{name});
|
std.log.debug("wl_display:delete_id({})", .{name});
|
||||||
},
|
},
|
||||||
else => return error.InvalidOpcode,
|
else => return error.InvalidOpcode,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std.log.warn("unknown event {{ .object_id = {}, .opcode = {x}, .message = \"{}\" }}", .{ header.object_id, header.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(message_bytes.items)) });
|
std.log.warn("unknown event {{ .object_id = {}, .opcode = {x}, .message = \"{}\" }}", .{ event.header.object_id, event.header.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(event.body)) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,13 +434,45 @@ pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 {
|
||||||
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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's turn that manual work above into a struct to make things easier to understand.
|
/// A wayland packet header
|
||||||
const Header = extern struct {
|
const Header = extern struct {
|
||||||
object_id: u32 align(1),
|
object_id: u32 align(1),
|
||||||
opcode: u16 align(1),
|
opcode: u16 align(1),
|
||||||
size: u16 align(1),
|
size: u16 align(1),
|
||||||
|
|
||||||
|
pub fn read(socket: std.net.Stream) !Header {
|
||||||
|
var header: Header = undefined;
|
||||||
|
const header_bytes_read = try socket.readAll(std.mem.asBytes(&header));
|
||||||
|
if (header_bytes_read < @sizeOf(Header)) {
|
||||||
|
return error.UnexpectedEOF;
|
||||||
|
}
|
||||||
|
return header;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This is the general shape of a Wayland `Event` (a message from the compositor to the client).
|
||||||
|
const Event = struct {
|
||||||
|
header: Header,
|
||||||
|
body: []const u8,
|
||||||
|
|
||||||
|
pub fn read(socket: std.net.Stream, body_buffer: *std.ArrayList(u8)) !Event {
|
||||||
|
const header = try Header.read(socket);
|
||||||
|
|
||||||
|
// read bytes until we match the size in the header, not including the bytes in the header.
|
||||||
|
try body_buffer.resize(header.size - @sizeOf(Header));
|
||||||
|
const message_bytes_read = try socket.readAll(body_buffer.items);
|
||||||
|
if (message_bytes_read < body_buffer.items.len) {
|
||||||
|
return error.UnexpectedEOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Event{
|
||||||
|
.header = header,
|
||||||
|
.body = body_buffer.items,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handles creating a header and writing the request to the socket.
|
||||||
pub fn writeRequest(socket: std.net.Stream, object_id: u32, opcode: u16, message: []const u32) !void {
|
pub fn writeRequest(socket: std.net.Stream, object_id: u32, opcode: u16, message: []const u32) !void {
|
||||||
const message_bytes = std.mem.sliceAsBytes(message);
|
const message_bytes = std.mem.sliceAsBytes(message);
|
||||||
const header = Header{
|
const header = Header{
|
||||||
|
|
Loading…
Reference in New Issue