158 lines
5.4 KiB
Zig
158 lines
5.4 KiB
Zig
// SPDX-FileCopyrightText: 2026 Ben Buhse <me@benbuhse.email>
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
const XkbBindings = @This();
|
|
|
|
pub const Command = union(enum) {
|
|
spawn: []const []const u8,
|
|
focus_next,
|
|
focus_prev,
|
|
fullscreen,
|
|
close_window,
|
|
exit,
|
|
};
|
|
|
|
const XkbBinding = struct {
|
|
xkb_binding_v1: *river.XkbBindingV1,
|
|
command: Command,
|
|
context: *Context,
|
|
link: wl.list.Link,
|
|
|
|
fn init(xkb_binding: *XkbBinding, xkb_binding_v1: *river.XkbBindingV1, command: Command, context: *Context) void {
|
|
xkb_binding.* = .{
|
|
.xkb_binding_v1 = xkb_binding_v1,
|
|
.command = command,
|
|
.context = context,
|
|
.link = undefined, // Handled by the wl.list
|
|
};
|
|
|
|
xkb_binding.xkb_binding_v1.setListener(*XkbBinding, xkbBindingListener, xkb_binding);
|
|
}
|
|
|
|
fn xkbBindingListener(xkb_binding_v1: *river.XkbBindingV1, event: river.XkbBindingV1.Event, xkb_binding: *XkbBinding) void {
|
|
assert(xkb_binding.xkb_binding_v1 == xkb_binding_v1);
|
|
switch (event) {
|
|
.pressed => {
|
|
xkb_binding.executeCommand();
|
|
},
|
|
.released => {},
|
|
else => |ev| {
|
|
log.debug("unhandled event: {s}", .{@tagName(ev)});
|
|
},
|
|
}
|
|
}
|
|
|
|
fn executeCommand(xkb_binding: *XkbBinding) void {
|
|
const context = xkb_binding.context;
|
|
switch (xkb_binding.command) {
|
|
.spawn => |cmd| {
|
|
var child = std.process.Child.init(cmd, std.heap.c_allocator);
|
|
_ = child.spawn() catch |err| {
|
|
log.err("Failed to spawn \"{s}\": {}", .{ cmd[0], err });
|
|
};
|
|
},
|
|
.focus_next => {
|
|
const seat = context.wm.seats.first() orelse return;
|
|
const pending_focus = if (seat.focused) |current|
|
|
context.wm.getNextWindow(current)
|
|
else
|
|
// No window focused, focus the first one
|
|
context.wm.windows.first();
|
|
|
|
if (pending_focus) |window| {
|
|
seat.pending_manage.pending_focus = .{ .window = window };
|
|
seat.pending_manage.should_warp_pointer = true;
|
|
} else {
|
|
seat.pending_manage.pending_focus = .clear_focus;
|
|
}
|
|
// context.wm.window_manager_v1.manageDirty();
|
|
},
|
|
.focus_prev => {
|
|
const seat = context.wm.seats.first() orelse return;
|
|
const pending_focus = if (seat.focused) |current|
|
|
context.wm.getPrevWindow(current)
|
|
else
|
|
// No window focused, focus the last one
|
|
context.wm.windows.last();
|
|
|
|
if (pending_focus) |window| {
|
|
seat.pending_manage.pending_focus = .{ .window = window };
|
|
seat.pending_manage.should_warp_pointer = true;
|
|
} else {
|
|
seat.pending_manage.pending_focus = .clear_focus;
|
|
}
|
|
// context.wm.window_manager_v1.manageDirty();
|
|
},
|
|
.fullscreen => {
|
|
const seat = context.wm.seats.first() orelse return;
|
|
const window = seat.focused orelse return;
|
|
window.pending_manage.fullscreen = !window.fullscreen;
|
|
// context.wm.window_manager_v1.manageDirty();
|
|
},
|
|
.close_window => {
|
|
const seat = context.wm.seats.first() orelse return;
|
|
if (seat.focused) |window| {
|
|
window.window_v1.close();
|
|
}
|
|
},
|
|
.exit => {
|
|
// TODO: Disabled while I'm working with river within river :P
|
|
// _ = std.process.Child.init(&.{"killall river"}, std.heap.c_allocator);
|
|
},
|
|
}
|
|
}
|
|
};
|
|
|
|
context: *Context,
|
|
|
|
xkb_bindings_v1: *river.XkbBindingsV1,
|
|
|
|
bindings: wl.list.Head(XkbBinding, .link),
|
|
|
|
pub fn create(context: *Context, xkb_bindings_v1: *river.XkbBindingsV1) !*XkbBindings {
|
|
const xkb_bindings = try utils.allocator.create(XkbBindings);
|
|
errdefer xkb_bindings.destroy();
|
|
|
|
xkb_bindings.* = .{
|
|
.context = context,
|
|
.xkb_bindings_v1 = xkb_bindings_v1,
|
|
.bindings = undefined, // we will initialize this shortly
|
|
};
|
|
|
|
xkb_bindings.bindings.init();
|
|
|
|
return xkb_bindings;
|
|
}
|
|
|
|
pub fn destroy(xkb_bindings: *XkbBindings) void {
|
|
xkb_bindings.destroy();
|
|
}
|
|
|
|
pub fn addBinding(xkb_bindings: *XkbBindings, river_seat_v1: *river.SeatV1, keysym: u32, modifiers: river.SeatV1.Modifiers, command: Command) void {
|
|
const xkb_binding_v1 = xkb_bindings.xkb_bindings_v1.getXkbBinding(river_seat_v1, keysym, modifiers) catch |err| {
|
|
log.err("Failed to get river xkb binding: {}", .{err});
|
|
return;
|
|
};
|
|
|
|
const xkb_binding = utils.allocator.create(XkbBinding) catch @panic("out-of-memory");
|
|
xkb_binding.init(xkb_binding_v1, command, xkb_bindings.context);
|
|
xkb_bindings.bindings.append(xkb_binding);
|
|
|
|
xkb_binding_v1.enable();
|
|
}
|
|
|
|
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
|
|
const wayland = @import("wayland");
|
|
const wl = wayland.client.wl;
|
|
const river = wayland.client.river;
|
|
|
|
const xkbcommon = @import("xkbcommon");
|
|
|
|
const utils = @import("utils.zig");
|
|
const Context = @import("Context.zig");
|
|
const Seat = @import("Seat.zig");
|
|
|
|
const log = std.log.scoped(.XkbBindings);
|