river-status: implement example client
This commit is contained in:
		
							
								
								
									
										20
									
								
								build.zig
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								build.zig
									
									
									
									
									
								
							@ -17,6 +17,12 @@ pub fn build(b: *std.build.Builder) !void {
 | 
				
			|||||||
        "Set to true to enable xwayland support",
 | 
					        "Set to true to enable xwayland support",
 | 
				
			||||||
    ) orelse false;
 | 
					    ) orelse false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const examples = b.option(
 | 
				
			||||||
 | 
					        bool,
 | 
				
			||||||
 | 
					        "examples",
 | 
				
			||||||
 | 
					        "Set to true to build examples",
 | 
				
			||||||
 | 
					    ) orelse false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const scan_protocols = ScanProtocolsStep.create(b);
 | 
					    const scan_protocols = ScanProtocolsStep.create(b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -45,12 +51,24 @@ pub fn build(b: *std.build.Builder) !void {
 | 
				
			|||||||
        addProtocolDeps(riverctl, &scan_protocols.step);
 | 
					        addProtocolDeps(riverctl, &scan_protocols.step);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        riverctl.linkLibC();
 | 
					        riverctl.linkLibC();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        riverctl.linkSystemLibrary("wayland-client");
 | 
					        riverctl.linkSystemLibrary("wayland-client");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        riverctl.install();
 | 
					        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");
 | 
					        const river_test = b.addTest("river/test_main.zig");
 | 
				
			||||||
        river_test.setTarget(target);
 | 
					        river_test.setTarget(target);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										166
									
								
								example/status.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								example/status.zig
									
									
									
									
									
										Normal file
									
								
							@ -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.?});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user