diff --git a/build.zig b/build.zig index 0b38498..cca1496 100644 --- a/build.zig +++ b/build.zig @@ -70,7 +70,7 @@ pub fn build(b: *std.Build) void { dynamic_lib.root_module.link_libc = true; b.installArtifact(dynamic_lib); - // pong service using the c client. + // pong client using the c client. const pong_with_c_bindings = b.addExecutable(.{ .name = "pong-with-c-bindings", // name of the executable .root_module = b.createModule(.{ @@ -96,12 +96,26 @@ pub fn build(b: *std.Build) void { // Statically link the executable to the library. pong_with_c_bindings.linkLibrary(static_lib); + // pong service using the c client. + const pongd_with_c_bindings = b.addExecutable(.{ + .name = "pongd-with-c-bindings", // name of the executable + .root_module = b.createModule(.{ + .root_source_file = b.path("src/examples/pongd-with-c-bindings.zig"), + .target = target, + .optimize = optimize, + + // Again, no need for external modules to import. + }), + }); + b.installArtifact(pongd_with_c_bindings); + pongd_with_c_bindings.linkLibrary(static_lib); + // This creates a top level step. Top level steps have a name and can be // invoked by name when running `zig build` (e.g. `zig build run`). // This will evaluate the `run` step rather than the default step. // For a top level step to actually do something, it must depend on other // steps (e.g. a Run step, as we will see in a moment). - const run_step = b.step("run-pong", "Run the pong client"); + const run_pong_with_c_bindings_step = b.step("run-pong", "Run the pong client"); // This creates a RunArtifact step in the build graph. A RunArtifact step // invokes an executable compiled by Zig. Steps will only be executed by the @@ -109,17 +123,17 @@ pub fn build(b: *std.Build) void { // or if another step depends on it, so it's up to you to define when and // how this Run step will be executed. In our case we want to run it when // the user runs `zig build run`, so we create a dependency link. - const run_cmd = b.addRunArtifact(pong_with_c_bindings); - run_step.dependOn(&run_cmd.step); + const run_pong_with_c_bindings_cmd = b.addRunArtifact(pong_with_c_bindings); + run_pong_with_c_bindings_step.dependOn(&run_pong_with_c_bindings_cmd.step); // By making the run step depend on the default step, it will be run from the // installation directory rather than directly from within the cache directory. - run_cmd.step.dependOn(b.getInstallStep()); + run_pong_with_c_bindings_cmd.step.dependOn(b.getInstallStep()); // This allows the user to pass arguments to the application in the build // command itself, like this: `zig build run -- arg1 arg2 etc` if (b.args) |args| { - run_cmd.addArgs(args); + run_pong_with_c_bindings_cmd.addArgs(args); } // Creates an executable that will run `test` blocks from the provided module. diff --git a/src/examples/pong-with-c-bindings.zig b/src/examples/pong-with-c-bindings.zig index 2eb5036..9dd3b2f 100644 --- a/src/examples/pong-with-c-bindings.zig +++ b/src/examples/pong-with-c-bindings.zig @@ -53,7 +53,7 @@ pub fn main() !u8 { libipc.ipc_context_timer (ctx, 1000); var should_continue : bool = true; - var event_count : u32 = 0; + // var event_count : u32 = 0; std.debug.print("Let's loop over events.\n", .{}); while(should_continue) { @@ -66,8 +66,8 @@ pub fn main() !u8 { return 1; } - event_count += 1; - std.debug.print("EVENT n°{d} type {d}\t", .{ event_count, event_type }); + // event_count += 1; + // std.debug.print("EVENT n°{d} type {d}\t", .{ event_count, event_type }); switch (event_type) { libipc.ERROR => { std.debug.print("Error.", .{}); return 1; }, @@ -84,7 +84,7 @@ pub fn main() !u8 { return 1; } buffer[size] = 0; - std.debug.print("Response (size {d}): {s}.\n", .{ size, buffer }); + std.debug.print("Response (size {d}): {s}.\n", .{ size, buffer[0..size] }); // We received the response, quitting. should_continue = false; }, diff --git a/src/examples/pongd-with-c-bindings.zig b/src/examples/pongd-with-c-bindings.zig new file mode 100644 index 0000000..79eb120 --- /dev/null +++ b/src/examples/pongd-with-c-bindings.zig @@ -0,0 +1,100 @@ +// Example of a `pong` service using the C bindings. +const std = @import("std"); + +// Only bindings are available. +const libipc = @cImport({ + @cInclude("../../../libipc.h"); +}); + +const SERVICE = "pong"; +const SERVICE_LEN = 4; +const MAX_MSG_SIZE = 10000; + +pub fn main() !u8 { + var ctx : *anyopaque = undefined; + var ret : c_int = 0; + var servicefd : c_int = undefined; + var buffer : [MAX_MSG_SIZE]u8 = undefined; + var size : usize = MAX_MSG_SIZE; + var event_type : u8 = undefined; + var index : usize = 0; + var originfd : c_int = undefined; + + std.debug.print("Init context.\n", .{}); + ret = libipc.ipc_context_init (@ptrCast(&ctx)); + + if (ret != 0) { + std.debug.print("Cannot init context.\n", .{}); + return 1; + } + + defer { + std.debug.print("Deinit context.\n", .{}); + libipc.ipc_context_deinit (@ptrCast(&ctx)); + } + + std.debug.print ("Create the 'pong' service.\n", .{}); + ret = libipc.ipc_service_init (ctx, &servicefd, SERVICE, SERVICE_LEN); + + if (ret != 0) { + std.debug.print("Cannot create the service.\n", .{}); + return 1; + } + + std.debug.print ("Listening to the standard input (press enter to quit).\n", .{}); + ret = libipc.ipc_add_external (ctx, 0); + + if (ret != 0) { + std.debug.print ("Cannot listen to standard input.", .{}); + return 1; + } + + std.debug.print("Set the timer to two seconds.\n", .{}); + libipc.ipc_context_timer (ctx, 2000); + + var event_count : u32 = 0; + + std.debug.print("Let's loop over events.\n", .{}); + while(true) { + size = MAX_MSG_SIZE; + var newfd : c_int = undefined; + const pbuffer : [*c]u8 = &buffer; + ret = libipc.ipc_wait_event (ctx, &event_type, &index, &originfd, &newfd, pbuffer, &size); + if (ret != 0) { + std.debug.print("Error while waiting for an event.", .{}); + return 1; + } + + event_count += 1; + + switch (event_type) { + libipc.ERROR => { std.debug.print("Error.", .{}); return 1; }, + libipc.EXTERNAL => { std.debug.print("Quitting.\n", .{}); return 0; }, + libipc.SWITCH_RX => { std.debug.print("Switch RX (shouldn't happen).", .{}); return 1; }, + libipc.SWITCH_TX => { std.debug.print("Switch TX (shouldn't happen).", .{}); return 1; }, + libipc.CONNECTION => { std.debug.print("Connection (fd {d}).\n", .{newfd}); }, + libipc.DISCONNECTION => { std.debug.print("Disconnection (fd {d}).\n", .{originfd}); }, + libipc.TIMER => { std.debug.print("\rTIMER ({d}).\r", .{event_count}); }, + libipc.MESSAGE_TX => { std.debug.print("A message has been sent.\n", .{}); }, + libipc.MESSAGE_RX => { + if (size == 0) { + std.debug.print("No message returned.\n", .{}); + return 1; + } + buffer[size] = 0; + std.debug.print("Message from {d} (size {d}): {s}.\n", .{ originfd, size, buffer[0..size] }); + + std.debug.print ("Schedule a message for {d}.\n", .{originfd}); + ret = libipc.ipc_schedule (ctx, originfd, pbuffer, size); + + if (ret != 0) { + std.debug.print("Cannot schedule a message.\n", .{}); + return 1; + } + }, + else => { std.debug.print("Unknown IPC message type.\n", .{}); return 1; }, + } + } + + return 0; +}