command/input: add map-to-output
This commit is contained in:
parent
95da9b5875
commit
0cb7c49cc3
@ -95,7 +95,8 @@ function __riverctl_completion ()
|
|||||||
tap \
|
tap \
|
||||||
tap-button-map \
|
tap-button-map \
|
||||||
scroll-method \
|
scroll-method \
|
||||||
scroll-button"
|
scroll-button \
|
||||||
|
map-to-output"
|
||||||
COMPREPLY=($(compgen -W "${OPTS}" -- "${COMP_WORDS[3]}"))
|
COMPREPLY=($(compgen -W "${OPTS}" -- "${COMP_WORDS[3]}"))
|
||||||
elif [ "${COMP_WORDS[1]}" == "hide-cursor" ]
|
elif [ "${COMP_WORDS[1]}" == "hide-cursor" ]
|
||||||
then
|
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 '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-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 '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'
|
# 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'
|
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'
|
'tap-button-map:Configure the button mapping for tapping'
|
||||||
'scroll-method:Set the scroll method'
|
'scroll-method:Set the scroll method'
|
||||||
'scroll-button:Set the scroll button'
|
'scroll-button:Set the scroll button'
|
||||||
|
'map-to-output:Map to a given output'
|
||||||
)
|
)
|
||||||
|
|
||||||
_describe -t command 'command' input_subcommands
|
_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
|
Set the scroll button of an input device. _button_ is the name of a Linux
|
||||||
input event code.
|
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
|
# EXAMPLES
|
||||||
|
|
||||||
Bind Super+Return in normal mode to spawn a *foot*(1) terminal:
|
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,
|
glob: []const u8,
|
||||||
|
|
||||||
// Note: Field names equal name of the setting in the 'input' command.
|
// Note: Field names equal name of the setting in the 'input' command.
|
||||||
@ -232,9 +266,12 @@ tap: ?TapState = null,
|
|||||||
@"pointer-accel": ?PointerAccel = null,
|
@"pointer-accel": ?PointerAccel = null,
|
||||||
@"scroll-method": ?ScrollMethod = null,
|
@"scroll-method": ?ScrollMethod = null,
|
||||||
@"scroll-button": ?ScrollButton = null,
|
@"scroll-button": ?ScrollButton = null,
|
||||||
|
@"map-to-output": MapToOutput = .{ .output_name = null },
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
util.gpa.free(self.glob);
|
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 {
|
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| {
|
inline for (@typeInfo(Self).Struct.fields) |field| {
|
||||||
if (comptime mem.eql(u8, field.name, "glob")) continue;
|
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});
|
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);
|
setting.apply(libinput_device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse(self: *Self, setting: []const u8, value: []const u8) !void {
|
pub fn parse(self: *Self, setting: []const u8, value: []const u8) !void {
|
||||||
inline for (@typeInfo(Self).Struct.fields) |field| {
|
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);
|
const ret = c.libevdev_event_code_from_name(c.EV_KEY, value.ptr);
|
||||||
if (ret < 1) return error.InvalidButton;
|
if (ret < 1) return error.InvalidButton;
|
||||||
self.@"scroll-button" = ScrollButton{ .button = @intCast(ret) };
|
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 {
|
} else {
|
||||||
const T = @typeInfo(field.type).Optional.child;
|
const T = @typeInfo(field.type).Optional.child;
|
||||||
if (@typeInfo(T) != .Enum) {
|
if (@typeInfo(T) != .Enum) {
|
||||||
@ -286,7 +338,11 @@ pub fn write(self: *Self, writer: anytype) !void {
|
|||||||
|
|
||||||
inline for (@typeInfo(Self).Struct.fields) |field| {
|
inline for (@typeInfo(Self).Struct.fields) |field| {
|
||||||
if (comptime mem.eql(u8, field.name, "glob")) continue;
|
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.
|
// Special-case the settings which are not enums.
|
||||||
if (comptime mem.eql(u8, field.name, "pointer-accel")) {
|
if (comptime mem.eql(u8, field.name, "pointer-accel")) {
|
||||||
try writer.print("\tpointer-accel: {d}\n", .{setting.value});
|
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", .{
|
try writer.print("\tscroll-button: {s}\n", .{
|
||||||
mem.sliceTo(c.libevdev_event_code_get_name(c.EV_KEY, setting.button), 0),
|
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 {
|
} else {
|
||||||
const T = @typeInfo(field.type).Optional.child;
|
const T = @typeInfo(field.type).Optional.child;
|
||||||
if (@typeInfo(T) != .Enum) {
|
if (@typeInfo(T) != .Enum) {
|
||||||
|
@ -138,6 +138,19 @@ pub fn inputAllowed(self: Self, wlr_surface: *wlr.Surface) bool {
|
|||||||
true;
|
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 {
|
fn handleNewInput(listener: *wl.Listener(*wlr.InputDevice), wlr_device: *wlr.InputDevice) void {
|
||||||
const self = @fieldParentPtr(Self, "new_input", listener);
|
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();
|
wlr_output.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
server.input_manager.reconfigureDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the output from root.active_outputs and the output layout.
|
/// 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();
|
root.notifyLayoutDemandDone();
|
||||||
}
|
}
|
||||||
while (output.layouts.first) |node| node.data.destroy();
|
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
|
/// 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
|
// add an input configuration at an arbitrary position in the generality
|
||||||
// ordered list, so the simplest way to ensure the device is configured
|
// ordered list, so the simplest way to ensure the device is configured
|
||||||
// correctly is to apply all input configurations again, in order.
|
// correctly is to apply all input configurations again, in order.
|
||||||
var it = server.input_manager.devices.iterator(.forward);
|
server.input_manager.reconfigureDevices();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lessThan(_: void, a: InputConfig, b: InputConfig) bool {
|
fn lessThan(_: void, a: InputConfig, b: InputConfig) bool {
|
||||||
|
Loading…
Reference in New Issue
Block a user