Add callback to command request for error handling

This commit is contained in:
Isaac Freund 2020-05-24 15:18:57 +02:00
parent 9f35984c62
commit 4d68043045
4 changed files with 105 additions and 15 deletions

View file

@ -29,12 +29,10 @@
</description> </description>
<arg name="command" type="array" summary="the command to run as a series <arg name="command" type="array" summary="the command to run as a series
of null-terminated strings"/> of null-terminated strings"/>
<arg name="callback" type="new_id" interface="zriver_command_callback_v1"
summary="callback object to recieve success/error events"/>
</request> </request>
<enum name="error">
<entry name="invalid_command" value="0" summary="the command is invalid"/>
</enum>
<event name="focus"> <event name="focus">
<description summary="sent when a view gains focus"> <description summary="sent when a view gains focus">
</description> </description>
@ -56,4 +54,27 @@
summary="the current tags of each view on the output"/> summary="the current tags of each view on the output"/>
</event> </event>
</interface> </interface>
<interface name="zriver_command_callback_v1" version="1">
<description summary="callback object">
Exactly one of the success or failure events will be sent.
</description>
<event name="success">
<description summary="command successful">
Send when the command has been successfully received and validated by
the server and will be carried out.
</description>
</event>
<event name="failure">
<description summary="command failed">
Sent when the command could not be carried out. This could be due to
sending a non-existent command, no command, not enough arguments, too
many arguments, invalid arguments, etc.
</description>
<arg name="failure_message" type="string"
summary="a message explaining why failure occurred"/>
</event>
</interface>
</protocol> </protocol>

View file

@ -117,10 +117,21 @@ const str_to_read_fn = [_]Definition{
}; };
// zig fmt: on // zig fmt: on
pub const Error = error{
NoCommand,
UnknownCommand,
NotEnoughArguments,
TooManyArguments,
Overflow,
InvalidCharacter,
InvalidDirection,
OutOfMemory,
};
impl: ImplFn, impl: ImplFn,
arg: Arg, arg: Arg,
pub fn init(args: []const []const u8, allocator: *std.mem.Allocator) !Self { pub fn init(args: []const []const u8, allocator: *std.mem.Allocator) Error!Self {
if (args.len == 0) return error.NoCommand; if (args.len == 0) return error.NoCommand;
const name = args[0]; const name = args[0];

View file

@ -74,7 +74,12 @@ fn resourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
// TODO // TODO
} }
fn runCommand(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource, wl_array: ?*c.wl_array) callconv(.C) void { fn runCommand(
wl_client: ?*c.wl_client,
wl_resource: ?*c.wl_resource,
wl_array: ?*c.wl_array,
callback_id: u32,
) callconv(.C) void {
const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource))); const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
const allocator = self.server.allocator; const allocator = self.server.allocator;
@ -89,11 +94,34 @@ fn runCommand(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource, wl_array:
i += slice.len + 1; i += slice.len + 1;
} }
for (args.items) |x| { const callback_resource = c.wl_resource_create(
std.debug.warn("{}\n", .{x}); wl_client,
} &c.zriver_command_callback_v1_interface,
protocol_version,
callback_id,
) orelse {
c.wl_client_post_no_memory(wl_client);
return;
};
// TODO: send the error event on failure instead of crashing c.wl_resource_set_implementation(callback_resource, null, null, null);
const command = Command.init(args.items, allocator) catch unreachable;
const command = Command.init(args.items, allocator) catch |err| {
c.zriver_command_callback_v1_send_failure(
callback_resource,
switch (err) {
Command.Error.NoCommand => "no command given",
Command.Error.UnknownCommand => "unknown command",
Command.Error.NotEnoughArguments => "not enough arguments",
Command.Error.TooManyArguments => "too many arguments",
Command.Error.Overflow => "value out of bounds",
Command.Error.InvalidCharacter => "invalid character in argument",
Command.Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'",
Command.Error.OutOfMemory => unreachable,
},
);
return;
};
c.zriver_command_callback_v1_send_success(callback_resource);
command.run(self.server.input_manager.default_seat); command.run(self.server.input_manager.default_seat);
} }

View file

@ -27,14 +27,20 @@ const wl_registry_listener = c.wl_registry_listener{
.global_remove = handleGlobalRemove, .global_remove = handleGlobalRemove,
}; };
const command_callback_listener = c.zriver_command_callback_v1_listener{
.success = handleSuccess,
.failure = handleFailure,
};
var river_window_manager: ?*c.zriver_window_manager_v1 = null; var river_window_manager: ?*c.zriver_window_manager_v1 = null;
pub fn main() !void { pub fn main() !void {
const wl_display = c.wl_display_connect(null) orelse return error.CantConnectToDisplay; const wl_display = c.wl_display_connect(null) orelse return error.CantConnectToDisplay;
const wl_registry = c.wl_display_get_registry(wl_display); const wl_registry = c.wl_display_get_registry(wl_display);
_ = c.wl_registry_add_listener(wl_registry, &wl_registry_listener, null); if (c.wl_registry_add_listener(wl_registry, &wl_registry_listener, null) < 0)
if (c.wl_display_roundtrip(wl_display) == -1) return error.RoundtripFailed; return error.FailedToAddListener;
if (c.wl_display_roundtrip(wl_display) < 0) return error.RoundtripFailed;
const wm = river_window_manager orelse return error.RiverWMNotAdvertised; const wm = river_window_manager orelse return error.RiverWMNotAdvertised;
@ -51,8 +57,15 @@ pub fn main() !void {
ptr[arg.len] = 0; ptr[arg.len] = 0;
} }
c.zriver_window_manager_v1_run_command(wm, &command); const command_callback = c.zriver_window_manager_v1_run_command(wm, &command);
if (c.wl_display_roundtrip(wl_display) == -1) return error.RoundtripFailed; if (c.zriver_command_callback_v1_add_listener(
command_callback,
&command_callback_listener,
null,
) < 0) return error.FailedToAddListener;
// Loop until our callback is called and we exit.
while (true) if (c.wl_display_dispatch(wl_display) < 0) return error.DispatchFailed;
} }
fn handleGlobal( fn handleGlobal(
@ -77,3 +90,20 @@ fn handleGlobal(
/// Ignore the event /// Ignore the event
fn handleGlobalRemove(data: ?*c_void, wl_registry: ?*c.wl_registry, name: u32) callconv(.C) void {} fn handleGlobalRemove(data: ?*c_void, wl_registry: ?*c.wl_registry, name: u32) callconv(.C) void {}
/// On success we simply exit with a clean exit code
fn handleSuccess(data: ?*c_void, callback: ?*c.zriver_command_callback_v1) callconv(.C) void {
std.os.exit(0);
}
/// Print the failure message and exit non-zero
fn handleFailure(
data: ?*c_void,
callback: ?*c.zriver_command_callback_v1,
failure_message: ?[*:0]const u8,
) callconv(.C) void {
if (failure_message) |message| {
std.debug.warn("Error: {}\n", .{failure_message});
}
std.os.exit(1);
}