diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index 0fb809b..01b05a1 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -506,7 +506,9 @@ However note that not every input device supports every property. active, the scroll direction is inverted. *input* _name_ *scroll-factor* _factor_ - Set the scroll factor of the input device. Needs a non-negative float. + Set the scroll factor of the input device. Accepts a postive value + greater than 0. For example, a _factor_ of 0.5 will make scrolling twice + as slow while a _factor_ of 3 will make scrolling 3 times as fast. *input* _name_ *left-handed* *enabled*|*disabled* Enable or disable the left handed mode of the input device. diff --git a/river/Cursor.zig b/river/Cursor.zig index 54e17ae..3b4e0a3 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -313,8 +313,16 @@ fn handleAxis(listener: *wl.Listener(*wlr.Pointer.event.Axis), event: *wlr.Point cursor.seat.wlr_seat.pointerNotifyAxis( event.time_msec, event.orientation, - event.delta * device.scroll_factor, - @intFromFloat(@as(f32, @floatFromInt(event.delta_discrete)) * device.scroll_factor), + event.delta * device.config.scroll_factor, + @intFromFloat(math.clamp( + @round(@as(f32, @floatFromInt(event.delta_discrete)) * device.config.scroll_factor), + // It seems that clamping to exactly the bounds of an i32 is insufficient to make the + // @intFromFloat() call safe due to the max/min i32 not being exactly representable + // by an f32. Dividing by 2 is a low effort way to ensure the value is in bounds and + // allow users to set their scroll-factor to inf without crashing river. + math.minInt(i32) / 2, + math.maxInt(i32) / 2, + )), event.source, ); } diff --git a/river/InputConfig.zig b/river/InputConfig.zig index 4e01f21..09250d7 100644 --- a/river/InputConfig.zig +++ b/river/InputConfig.zig @@ -253,10 +253,10 @@ pub const MapToOutput = struct { }; pub const ScrollFactor = struct { - value: ?f32, + value: ?f32 = null, fn apply(scroll_factor: ScrollFactor, device: *InputDevice) void { - device.scroll_factor = scroll_factor.value orelse 1.0; + device.config.scroll_factor = scroll_factor.value orelse 1.0; } }; @@ -272,7 +272,7 @@ drag: ?DragState = null, @"disable-while-trackpointing": ?DwtpState = null, @"middle-emulation": ?MiddleEmulation = null, @"natural-scroll": ?NaturalScroll = null, -@"scroll-factor": ScrollFactor = .{ .value = 1.0 }, +@"scroll-factor": ScrollFactor = .{}, @"left-handed": ?LeftHanded = null, tap: ?TapState = null, @"tap-button-map": ?TapButtonMap = null, @@ -317,9 +317,12 @@ pub fn parse(config: *InputConfig, setting: []const u8, value: []const u8) !void .value = math.clamp(try std.fmt.parseFloat(f32, value), -1.0, 1.0), }; } else if (comptime mem.eql(u8, field.name, "scroll-factor")) { - config.@"scroll-factor" = ScrollFactor{ - .value = @max(try std.fmt.parseFloat(f32, value), 0.0), - }; + const unvalidated = try std.fmt.parseFloat(f32, value); + if (unvalidated > 0) { + config.@"scroll-factor" = ScrollFactor{ .value = unvalidated }; + } else { + return error.OutOfBounds; + } } else if (comptime mem.eql(u8, field.name, "scroll-button")) { const ret = c.libevdev_event_code_from_name(c.EV_KEY, value.ptr); if (ret < 1) return error.InvalidButton; diff --git a/river/InputDevice.zig b/river/InputDevice.zig index 90e7bc2..685a671 100644 --- a/river/InputDevice.zig +++ b/river/InputDevice.zig @@ -34,7 +34,6 @@ const Tablet = @import("Tablet.zig"); const log = std.log.scoped(.input_manager); -scroll_factor: f32, seat: *Seat, wlr_device: *wlr.InputDevice, @@ -45,6 +44,10 @@ destroy: wl.Listener(*wlr.InputDevice) = wl.Listener(*wlr.InputDevice).init(hand /// and name. However identifiers of InputConfigs are unique. identifier: []const u8, +config: struct { + scroll_factor: f32 = 1.0, +} = .{}, + /// InputManager.devices link: wl.list.Link, @@ -74,7 +77,6 @@ pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !vo } device.* = .{ - .scroll_factor = 1.0, .seat = seat, .wlr_device = wlr_device, .identifier = identifier, diff --git a/river/command.zig b/river/command.zig index ad8d21f..d32efce 100644 --- a/river/command.zig +++ b/river/command.zig @@ -105,6 +105,7 @@ pub const Error = error{ UnknownCommand, NotEnoughArguments, TooManyArguments, + OutOfBounds, Overflow, InvalidButton, InvalidCharacter, @@ -150,7 +151,7 @@ pub fn errToMsg(err: Error) [:0]const u8 { Error.ConflictingOptions => "options conflict", Error.NotEnoughArguments => "not enough arguments", Error.TooManyArguments => "too many arguments", - Error.Overflow => "value out of bounds", + Error.OutOfBounds, Error.Overflow => "value out of bounds", Error.InvalidButton => "invalid button", Error.InvalidCharacter => "invalid character in argument", Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'",