command/input: add map-to-output
This commit is contained in:
parent
95da9b5875
commit
0cb7c49cc3
@ -95,7 +95,8 @@ function __riverctl_completion ()
|
||||
tap \
|
||||
tap-button-map \
|
||||
scroll-method \
|
||||
scroll-button"
|
||||
scroll-button \
|
||||
map-to-output"
|
||||
COMPREPLY=($(compgen -W "${OPTS}" -- "${COMP_WORDS[3]}"))
|
||||
elif [ "${COMP_WORDS[1]}" == "hide-cursor" ]
|
||||
then
|
||||
|
@ -116,6 +116,7 @@ complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_
|
||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'tap-button-map' -d 'Configure the button mapping for tapping'
|
||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'scroll-method' -d 'Set the scroll method'
|
||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'scroll-button' -d 'Set the scroll button'
|
||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'map-to-output' -d 'Map to a given output'
|
||||
|
||||
# Subcommands for the subcommands of 'input'
|
||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from drag drag-lock disable-while-typing disable-while-trackpointing middle-emulation natural-scroll left-handed tap' -a 'enabled disabled'
|
||||
|
@ -123,6 +123,7 @@ _riverctl()
|
||||
'tap-button-map:Configure the button mapping for tapping'
|
||||
'scroll-method:Set the scroll method'
|
||||
'scroll-button:Set the scroll button'
|
||||
'map-to-output:Map to a given output'
|
||||
)
|
||||
|
||||
_describe -t command 'command' input_subcommands
|
||||
|
2
deps/zig-wlroots
vendored
2
deps/zig-wlroots
vendored
@ -1 +1 @@
|
||||
Subproject commit 2149026047c95b3cad2eb2bf1e3e9f66447dae97
|
||||
Subproject commit e7e49951662d346f0f522124f2138ecf9c44ded9
|
@ -527,6 +527,11 @@ However note that not every input device supports every property.
|
||||
Set the scroll button of an input device. _button_ is the name of a Linux
|
||||
input event code.
|
||||
|
||||
*input* _name_ *map-to-output* _output_|*disabled*
|
||||
Maps the input to a given output. This is valid even if the output isn't
|
||||
currently active and will lead to the device being mapped once it is
|
||||
connected.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
Bind Super+Return in normal mode to spawn a *foot*(1) terminal:
|
||||
|
@ -214,6 +214,40 @@ pub const ScrollButton = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const MapToOutput = struct {
|
||||
output_name: ?[]const u8,
|
||||
|
||||
fn apply(map_to_output: MapToOutput, device: *wlr.InputDevice) void {
|
||||
const output = out: {
|
||||
if (map_to_output.output_name) |name| {
|
||||
var it = server.root.active_outputs.iterator(.forward);
|
||||
while (it.next()) |outp| {
|
||||
if (mem.eql(u8, mem.span(outp.wlr_output.name), name)) {
|
||||
break :out outp.wlr_output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break :out null;
|
||||
};
|
||||
|
||||
switch (device.type) {
|
||||
.pointer, .touch, .tablet_tool => {
|
||||
log.debug("mapping input '{s}' -> '{s}'", .{
|
||||
device.name,
|
||||
if (output) |o| o.name else "<no output>",
|
||||
});
|
||||
|
||||
// TODO: support multiple seats
|
||||
server.input_manager.defaultSeat().cursor.wlr_cursor.mapInputToOutput(device, output);
|
||||
},
|
||||
|
||||
// These devices do not support being mapped to outputs.
|
||||
.keyboard, .tablet_pad, .switch_device => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
glob: []const u8,
|
||||
|
||||
// Note: Field names equal name of the setting in the 'input' command.
|
||||
@ -232,9 +266,12 @@ tap: ?TapState = null,
|
||||
@"pointer-accel": ?PointerAccel = null,
|
||||
@"scroll-method": ?ScrollMethod = null,
|
||||
@"scroll-button": ?ScrollButton = null,
|
||||
@"map-to-output": MapToOutput = .{ .output_name = null },
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
util.gpa.free(self.glob);
|
||||
if (self.@"map-to-output".output_name) |name|
|
||||
util.gpa.free(name);
|
||||
}
|
||||
|
||||
pub fn apply(self: *const Self, device: *InputDevice) void {
|
||||
@ -244,12 +281,20 @@ pub fn apply(self: *const Self, device: *InputDevice) void {
|
||||
inline for (@typeInfo(Self).Struct.fields) |field| {
|
||||
if (comptime mem.eql(u8, field.name, "glob")) continue;
|
||||
|
||||
if (@field(self, field.name)) |setting| {
|
||||
if (@as(if (@typeInfo(field.type) == .Optional)
|
||||
field.type
|
||||
else
|
||||
?field.type, @field(self, field.name))) |setting|
|
||||
{
|
||||
log.debug("applying setting: {s}", .{field.name});
|
||||
if (comptime mem.eql(u8, field.name, "map-to-output")) {
|
||||
setting.apply(device.wlr_device);
|
||||
} else {
|
||||
setting.apply(libinput_device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(self: *Self, setting: []const u8, value: []const u8) !void {
|
||||
inline for (@typeInfo(Self).Struct.fields) |field| {
|
||||
@ -265,6 +310,13 @@ pub fn parse(self: *Self, setting: []const u8, value: []const u8) !void {
|
||||
const ret = c.libevdev_event_code_from_name(c.EV_KEY, value.ptr);
|
||||
if (ret < 1) return error.InvalidButton;
|
||||
self.@"scroll-button" = ScrollButton{ .button = @intCast(ret) };
|
||||
} else if (comptime mem.eql(u8, field.name, "map-to-output")) {
|
||||
if (self.@"map-to-output".output_name) |name|
|
||||
util.gpa.free(name);
|
||||
|
||||
self.@"map-to-output" = MapToOutput{
|
||||
.output_name = if (std.mem.eql(u8, value, "disabled")) null else try util.gpa.dupe(u8, value),
|
||||
};
|
||||
} else {
|
||||
const T = @typeInfo(field.type).Optional.child;
|
||||
if (@typeInfo(T) != .Enum) {
|
||||
@ -286,7 +338,11 @@ pub fn write(self: *Self, writer: anytype) !void {
|
||||
|
||||
inline for (@typeInfo(Self).Struct.fields) |field| {
|
||||
if (comptime mem.eql(u8, field.name, "glob")) continue;
|
||||
if (@field(self, field.name)) |setting| {
|
||||
if (@as(if (@typeInfo(field.type) == .Optional)
|
||||
field.type
|
||||
else
|
||||
?field.type, @field(self, field.name))) |setting|
|
||||
{
|
||||
// Special-case the settings which are not enums.
|
||||
if (comptime mem.eql(u8, field.name, "pointer-accel")) {
|
||||
try writer.print("\tpointer-accel: {d}\n", .{setting.value});
|
||||
@ -294,6 +350,9 @@ pub fn write(self: *Self, writer: anytype) !void {
|
||||
try writer.print("\tscroll-button: {s}\n", .{
|
||||
mem.sliceTo(c.libevdev_event_code_get_name(c.EV_KEY, setting.button), 0),
|
||||
});
|
||||
} else if (comptime mem.eql(u8, field.name, "map-to-output")) {
|
||||
if (setting.output_name) |outp|
|
||||
try writer.print("\tmap-to-output: {s}\n", .{outp});
|
||||
} else {
|
||||
const T = @typeInfo(field.type).Optional.child;
|
||||
if (@typeInfo(T) != .Enum) {
|
||||
|
@ -138,6 +138,19 @@ pub fn inputAllowed(self: Self, wlr_surface: *wlr.Surface) bool {
|
||||
true;
|
||||
}
|
||||
|
||||
/// Reconfigures all devices' libinput configuration as well as their output mapping.
|
||||
/// This is called on outputs being added or removed and on the input configuration being changed.
|
||||
pub fn reconfigureDevices(self: *Self) void {
|
||||
var it = self.devices.iterator(.forward);
|
||||
while (it.next()) |device| {
|
||||
for (self.configs.items) |config| {
|
||||
if (@import("globber").match(device.identifier, config.glob)) {
|
||||
config.apply(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handleNewInput(listener: *wl.Listener(*wlr.InputDevice), wlr_device: *wlr.InputDevice) void {
|
||||
const self = @fieldParentPtr(Self, "new_input", listener);
|
||||
|
||||
|
@ -246,6 +246,8 @@ fn handleNewOutput(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
|
||||
}
|
||||
wlr_output.destroy();
|
||||
};
|
||||
|
||||
server.input_manager.reconfigureDevices();
|
||||
}
|
||||
|
||||
/// Remove the output from root.active_outputs and the output layout.
|
||||
@ -334,6 +336,10 @@ pub fn deactivateOutput(root: *Self, output: *Output) void {
|
||||
root.notifyLayoutDemandDone();
|
||||
}
|
||||
while (output.layouts.first) |node| node.data.destroy();
|
||||
|
||||
// We must call reconfigureDevices here to unmap devices that might be mapped to this output
|
||||
// in order to prevent a segfault in wlroots.
|
||||
server.input_manager.reconfigureDevices();
|
||||
}
|
||||
|
||||
/// Add the output to root.active_outputs and the output layout if it has not
|
||||
|
@ -112,19 +112,7 @@ pub fn input(
|
||||
// add an input configuration at an arbitrary position in the generality
|
||||
// ordered list, so the simplest way to ensure the device is configured
|
||||
// correctly is to apply all input configurations again, in order.
|
||||
var it = server.input_manager.devices.iterator(.forward);
|
||||
while (it.next()) |device| {
|
||||
// Device does not match the glob given in the command, so its
|
||||
// configuration state after applying all configs again would be
|
||||
// the same.
|
||||
if (!globber.match(device.identifier, args[1])) continue;
|
||||
|
||||
for (server.input_manager.configs.items) |config| {
|
||||
if (globber.match(device.identifier, config.glob)) {
|
||||
config.apply(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
server.input_manager.reconfigureDevices();
|
||||
}
|
||||
|
||||
fn lessThan(_: void, a: InputConfig, b: InputConfig) bool {
|
||||
|
Loading…
Reference in New Issue
Block a user