*<mouse><doubleClickTime>*
Set double click time in milliseconds. Default is 500.
-*<mouse><context name=""><mousebind button=""><action>*
+*<mouse><context name=""><mousebind button="" direction=""><action>*
Multiple *<mousebind>* can exist within one *<context>*; and multiple
*<action>* can exist within one *<mousebind>*
- Left
- Middle
- Right
- - Up (scroll up)
- - Down (scroll down)
+
+ Supported scroll directions are:
+ - Up
+ - Down
+ - Left
+ - Right
Supported mouse actions include:
- Press: Pressing the specified button down in the context.
- DoubleClick: Two presses within the doubleClickTime.
- Drag: Pressing the button within the context, then moving the cursor
- Scroll: Scrolling up or down in the context.
+ Scroll actions must have a *direction* specified instead of *button*.
*<mouse><default />*
Load default mousebinds. This is an addition to the openbox
<mousebind button="Middle" action="Press">
<action name="ShowMenu"><menu>root-menu</menu></action>
</mousebind>
- <mousebind button="Up" action="Scroll">
+ <mousebind direction="Up" action="Scroll">
<action name="GoToDesktop" to="left"/>
</mousebind>
- <mousebind button="Down" action="Scroll">
+ <mousebind direction="Down" action="Scroll">
<action name="GoToDesktop" to="right"/>
</mousebind>
</context>
MOUSE_ACTION_SCROLL,
};
+enum direction {
+ LAB_DIRECTION_INVALID = 0,
+ LAB_DIRECTION_LEFT,
+ LAB_DIRECTION_RIGHT,
+ LAB_DIRECTION_UP,
+ LAB_DIRECTION_DOWN,
+};
+
struct mousebind {
enum ssd_part_type context;
/* ex: BTN_LEFT, BTN_RIGHT from linux/input_event_codes.h */
uint32_t button;
+ /* scroll direction; considered instead of button for scroll events */
+ enum direction direction;
+
/* ex: WLR_MODIFIER_SHIFT | WLR_MODIFIER_LOGO */
uint32_t modifiers;
enum mouse_event mousebind_event_from_str(const char *str);
uint32_t mousebind_button_from_str(const char *str, uint32_t *modifiers);
+enum direction mousebind_direction_from_str(const char *str, uint32_t *modifiers);
struct mousebind *mousebind_create(const char *context);
#endif /* __LABWC_MOUSEBIND_H */
enum lab_cursors server_cursor;
struct wlr_cursor *cursor;
struct wlr_xcursor_manager *xcursor_manager;
+ struct {
+ double x, y;
+ } smooth_scroll_offset;
struct wlr_pointer_constraint_v1 *current_constraint;
struct wlr_idle *wlr_idle;
return BTN_RIGHT;
} else if (!strcasecmp(str, "Middle")) {
return BTN_MIDDLE;
- } else if (!strcasecmp(str, "Up")) {
- return BTN_GEAR_UP;
- } else if (!strcasecmp(str, "Down")) {
- return BTN_GEAR_DOWN;
}
invalid:
wlr_log(WLR_ERROR, "unknown button (%s)", str);
return UINT32_MAX;
}
+enum direction
+mousebind_direction_from_str(const char *str, uint32_t *modifiers)
+{
+ assert(str);
+
+ if (modifiers) {
+ *modifiers = 0;
+ while (strlen(str) >= 2 && str[1] == '-') {
+ char modname[2] = {str[0], 0};
+ uint32_t parsed_modifier = parse_modifier(modname);
+ if (!parsed_modifier) {
+ goto invalid;
+ }
+ *modifiers |= parsed_modifier;
+ str += 2;
+ }
+ }
+
+ if (!strcasecmp(str, "Left")) {
+ return LAB_DIRECTION_LEFT;
+ } else if (!strcasecmp(str, "Right")) {
+ return LAB_DIRECTION_RIGHT;
+ } else if (!strcasecmp(str, "Up")) {
+ return LAB_DIRECTION_UP;
+ } else if (!strcasecmp(str, "Down")) {
+ return LAB_DIRECTION_DOWN;
+ }
+invalid:
+ wlr_log(WLR_ERROR, "unknown direction (%s)", str);
+ return LAB_DIRECTION_INVALID;
+}
+
enum mouse_event
mousebind_event_from_str(const char *str)
{
} else if (!strcmp(nodename, "button")) {
current_mousebind->button = mousebind_button_from_str(content,
¤t_mousebind->modifiers);
+ } else if (!strcmp(nodename, "direction")) {
+ current_mousebind->direction = mousebind_direction_from_str(content,
+ ¤t_mousebind->modifiers);
} else if (!strcmp(nodename, "action")) {
/* <mousebind button="" action="EVENT"> */
current_mousebind->mouse_event =
|| strcmp(current->event, mouse_combos[i - 1].event)) {
/* Create new mousebind */
m = mousebind_create(current->context);
- m->button = mousebind_button_from_str(current->button,
- &m->modifiers);
m->mouse_event = mousebind_event_from_str(current->event);
+ if (m->mouse_event == MOUSE_ACTION_SCROLL) {
+ m->direction = mousebind_direction_from_str(current->button,
+ &m->modifiers);
+ } else {
+ m->button = mousebind_button_from_str(current->button,
+ &m->modifiers);
+ }
count++;
}
}
if (existing->context == current->context
&& existing->button == current->button
+ && existing->direction == current->direction
&& existing->mouse_event == current->mouse_event) {
wl_list_remove(&existing->link);
action_list_free(&existing->actions);
}
}
+static int
+compare_delta(const struct wlr_pointer_axis_event *event, double *accum)
+{
+ /*
+ * Smooth scroll deltas are in surface space, so treating each unit as a
+ * scroll event would result in too-fast scrolling.
+ *
+ * This fudge factor (inherited from various historic projects, incl. Weston)
+ * produces events at a more reasonable rate.
+ *
+ * For historic context, see:
+ * https://lists.freedesktop.org/archives/wayland-devel/2019-April/040377.html
+ */
+ const double SCROLL_THRESHOLD = 10.0;
+
+ if (event->delta == 0.0) {
+ /* Delta 0 marks the end of a scroll */
+ *accum = 0.0;
+ } else {
+ /* Accumulate smooth scrolling until we hit threshold */
+ *accum += event->delta;
+ }
+ if (event->delta_discrete < 0 || *accum < -SCROLL_THRESHOLD) {
+ *accum = fmod(*accum, SCROLL_THRESHOLD);
+ return -1;
+ } else if (event->delta_discrete > 0 || *accum > SCROLL_THRESHOLD) {
+ *accum = fmod(*accum, SCROLL_THRESHOLD);
+ return 1;
+ }
+ return 0;
+}
+
bool
handle_cursor_axis(struct server *server, struct cursor_context *ctx,
struct wlr_pointer_axis_event *event)
uint32_t modifiers = wlr_keyboard_get_modifiers(
&server->seat.keyboard_group->keyboard);
- uint32_t button = 0;
- if (event->delta_discrete < 0) {
- button = BTN_GEAR_UP;
- } else if (event->delta_discrete > 0) {
- button = BTN_GEAR_DOWN;
+ enum direction direction = LAB_DIRECTION_INVALID;
+ if (event->orientation == WLR_AXIS_ORIENTATION_HORIZONTAL) {
+ int rel = compare_delta(event, &server->seat.smooth_scroll_offset.x);
+ if (rel < 0) {
+ direction = LAB_DIRECTION_LEFT;
+ } else if (rel > 0) {
+ direction = LAB_DIRECTION_RIGHT;
+ }
+ } else if (event->orientation == WLR_AXIS_ORIENTATION_VERTICAL) {
+ int rel = compare_delta(event, &server->seat.smooth_scroll_offset.y);
+ if (rel < 0) {
+ direction = LAB_DIRECTION_UP;
+ } else if (rel > 0) {
+ direction = LAB_DIRECTION_DOWN;
+ }
+ } else {
+ wlr_log(WLR_DEBUG, "Failed to handle cursor axis event");
}
- if (!button || event->source != WLR_AXIS_SOURCE_WHEEL
- || event->orientation != WLR_AXIS_ORIENTATION_VERTICAL) {
- wlr_log(WLR_DEBUG, "Failed to handle cursor axis event");
+ if (direction == LAB_DIRECTION_INVALID) {
return false;
}
wl_list_for_each(mousebind, &rc.mousebinds, link) {
if (ssd_part_contains(mousebind->context, ctx->type)
- && mousebind->button == button
+ && mousebind->direction == direction
&& modifiers == mousebind->modifiers
&& mousebind->mouse_event == MOUSE_ACTION_SCROLL) {
handled = true;