aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig20
-rw-r--r--example/status.zig166
2 files changed, 185 insertions, 1 deletions
diff --git a/build.zig b/build.zig
index bc405f3..89f9d9d 100644
--- a/build.zig
+++ b/build.zig
@@ -17,6 +17,12 @@ pub fn build(b: *std.build.Builder) !void {
"Set to true to enable xwayland support",
) orelse false;
+ const examples = b.option(
+ bool,
+ "examples",
+ "Set to true to build examples",
+ ) orelse false;
+
const scan_protocols = ScanProtocolsStep.create(b);
{
@@ -45,12 +51,24 @@ pub fn build(b: *std.build.Builder) !void {
addProtocolDeps(riverctl, &scan_protocols.step);
riverctl.linkLibC();
-
riverctl.linkSystemLibrary("wayland-client");
riverctl.install();
}
+ if (examples) {
+ const status = b.addExecutable("status", "example/status.zig");
+ status.setTarget(target);
+ status.setBuildMode(mode);
+
+ addProtocolDeps(status, &scan_protocols.step);
+
+ status.linkLibC();
+ status.linkSystemLibrary("wayland-client");
+
+ status.install();
+ }
+
{
const river_test = b.addTest("river/test_main.zig");
river_test.setTarget(target);
diff --git a/example/status.zig b/example/status.zig
new file mode 100644
index 0000000..98fd808
--- /dev/null
+++ b/example/status.zig
@@ -0,0 +1,166 @@
+// This file is part of river, a dynamic tiling wayland compositor.
+//
+// Copyright 2020 Isaac Freund
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+const std = @import("std");
+
+const c = @cImport({
+ @cInclude("wayland-client.h");
+ @cInclude("river-status-unstable-v1-client-protocol.h");
+});
+
+const wl_registry_listener = c.wl_registry_listener{
+ .global = handleGlobal,
+ .global_remove = handleGlobalRemove,
+};
+
+const river_output_status_listener = c.zriver_output_status_v1_listener{
+ .focused_tags = handleFocusedTags,
+ .view_tags = handleViewTags,
+};
+
+const river_seat_status_listener = c.zriver_seat_status_v1_listener{
+ .focused_output = handleFocusedOutput,
+ .unfocused_output = handleUnfocusedOutput,
+ .focused_view = handleFocusedView,
+};
+
+var river_status_manager: ?*c.zriver_status_manager_v1 = null;
+
+var outputs = std.ArrayList(*c.wl_output).init(std.heap.c_allocator);
+var seats = std.ArrayList(*c.wl_seat).init(std.heap.c_allocator);
+
+pub fn main() !void {
+ const wl_display = c.wl_display_connect(null) orelse return error.CantConnectToDisplay;
+ const wl_registry = c.wl_display_get_registry(wl_display);
+
+ if (c.wl_registry_add_listener(wl_registry, &wl_registry_listener, null) < 0)
+ return error.FailedToAddListener;
+ if (c.wl_display_roundtrip(wl_display) < 0) return error.RoundtripFailed;
+
+ if (river_status_manager == null) return error.RiverStatusManagerNotAdvertised;
+
+ // Loop forever, listening for new events.
+ while (true) if (c.wl_display_dispatch(wl_display) < 0) return error.DispatchFailed;
+}
+
+fn handleGlobal(
+ data: ?*c_void,
+ wl_registry: ?*c.wl_registry,
+ name: u32,
+ interface: ?[*:0]const u8,
+ version: u32,
+) callconv(.C) void {
+ // Global advertisement order is not defined, so save any outputs or seats
+ // advertised before the river_status_manager.
+ if (std.cstr.cmp(interface.?, @ptrCast([*:0]const u8, c.zriver_status_manager_v1_interface.name.?)) == 0) {
+ river_status_manager = @ptrCast(
+ *c.zriver_status_manager_v1,
+ c.wl_registry_bind(wl_registry, name, &c.zriver_status_manager_v1_interface, version),
+ );
+ for (outputs.items) |wl_output| createOutputStatus(wl_output);
+ for (seats.items) |wl_seat| createSeatStatus(wl_seat);
+ outputs.deinit();
+ seats.deinit();
+ } else if (std.cstr.cmp(interface.?, @ptrCast([*:0]const u8, c.wl_output_interface.name.?)) == 0) {
+ const wl_output = @ptrCast(
+ *c.wl_output,
+ c.wl_registry_bind(wl_registry, name, &c.wl_output_interface, version),
+ );
+ if (river_status_manager != null)
+ createOutputStatus(wl_output)
+ else
+ outputs.append(wl_output) catch @panic("out of memory");
+ } else if (std.cstr.cmp(interface.?, @ptrCast([*:0]const u8, c.wl_seat_interface.name.?)) == 0) {
+ const wl_seat = @ptrCast(
+ *c.wl_seat,
+ c.wl_registry_bind(wl_registry, name, &c.wl_seat_interface, version),
+ );
+ if (river_status_manager != null)
+ createSeatStatus(wl_seat)
+ else
+ seats.append(wl_seat) catch @panic("out of memory");
+ }
+}
+
+fn createOutputStatus(wl_output: *c.wl_output) void {
+ const river_output_status = c.zriver_status_manager_v1_get_river_output_status(
+ river_status_manager.?,
+ wl_output,
+ );
+ _ = c.zriver_output_status_v1_add_listener(
+ river_output_status,
+ &river_output_status_listener,
+ null,
+ );
+}
+
+fn createSeatStatus(wl_seat: *c.wl_seat) void {
+ const river_seat_status = c.zriver_status_manager_v1_get_river_seat_status(
+ river_status_manager.?,
+ wl_seat,
+ );
+ _ = c.zriver_seat_status_v1_add_listener(river_seat_status, &river_seat_status_listener, null);
+}
+
+fn handleGlobalRemove(data: ?*c_void, wl_registry: ?*c.wl_registry, name: u32) callconv(.C) void {
+ // Ignore the event
+}
+
+fn handleFocusedTags(
+ data: ?*c_void,
+ output_status: ?*c.zriver_output_status_v1,
+ tags: u32,
+) callconv(.C) void {
+ std.debug.warn("Focused tags: {b:0>10}\n", .{tags});
+}
+
+fn handleViewTags(
+ data: ?*c_void,
+ output_status: ?*c.zriver_output_status_v1,
+ tags: ?*c.wl_array,
+) callconv(.C) void {
+ std.debug.warn("View tags:\n", .{});
+ var offset: usize = 0;
+ while (offset < tags.?.size) : (offset += @sizeOf(u32)) {
+ const ptr = @ptrCast([*]u8, tags.?.data) + offset;
+ std.debug.warn("{b:0>10}\n", .{std.mem.bytesToValue(u32, ptr[0..4])});
+ }
+}
+
+fn handleFocusedOutput(
+ data: ?*c_void,
+ seat_status: ?*c.zriver_seat_status_v1,
+ wl_output: ?*c.wl_output,
+) callconv(.C) void {
+ std.debug.warn("Output id {} focused\n", .{c.wl_proxy_get_id(@ptrCast(*c.wl_proxy, wl_output))});
+}
+
+fn handleUnfocusedOutput(
+ data: ?*c_void,
+ seat_status: ?*c.zriver_seat_status_v1,
+ wl_output: ?*c.wl_output,
+) callconv(.C) void {
+ std.debug.warn("Output id {} unfocused\n", .{c.wl_proxy_get_id(@ptrCast(*c.wl_proxy, wl_output))});
+}
+
+fn handleFocusedView(
+ data: ?*c_void,
+ seat_status: ?*c.zriver_seat_status_v1,
+ title: ?[*:0]const u8,
+) callconv(.C) void {
+ std.debug.warn("Focused view title: {}\n", .{title.?});
+}