sway/swaybar/input.c
Brian Ashworth 02bbefda20 bar_cmd_tray_bind: Use mouse button helpers
This modifies `bar_cmd_tray_bindsym` to use `get_mouse_bindsym` for
parsing mouse buttons. This also introduces `bar_cmd_tray_bindcode`,
which will use `get_mouse_bindcode` for parsing mouse buttons. Like with
sway bindings, the two commands are encapsulated in a single file to
maximize shared code.

This also modifies tray bindings to work off of events codes rather than
x11 buttons, which allows for any mouse buttons to be used.

For `get_bar_config`, `event_code` has been added to the `tray_bindings`
section and will include to event code for the button. If the event code
can be mapped to a x11 button, `input_code` will still be the x11 button
number. Otherwise, `input_code` will be `0`.
2019-01-16 11:12:45 +01:00

293 lines
8.1 KiB
C

#include <assert.h>
#include <linux/input-event-codes.h>
#include <stdlib.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <wlr/util/log.h>
#include "list.h"
#include "log.h"
#include "swaybar/bar.h"
#include "swaybar/config.h"
#include "swaybar/input.h"
#include "swaybar/ipc.h"
void free_hotspots(struct wl_list *list) {
struct swaybar_hotspot *hotspot, *tmp;
wl_list_for_each_safe(hotspot, tmp, list, link) {
wl_list_remove(&hotspot->link);
if (hotspot->destroy) {
hotspot->destroy(hotspot->data);
}
free(hotspot);
}
}
uint32_t event_to_x11_button(uint32_t event) {
switch (event) {
case BTN_LEFT:
return 1;
case BTN_MIDDLE:
return 2;
case BTN_RIGHT:
return 3;
case SWAY_SCROLL_UP:
return 4;
case SWAY_SCROLL_DOWN:
return 5;
case SWAY_SCROLL_LEFT:
return 6;
case SWAY_SCROLL_RIGHT:
return 7;
case BTN_SIDE:
return 8;
case BTN_EXTRA:
return 9;
default:
return 0;
}
}
static uint32_t wl_axis_to_button(uint32_t axis, wl_fixed_t value) {
bool negative = wl_fixed_to_double(value) < 0;
switch (axis) {
case WL_POINTER_AXIS_VERTICAL_SCROLL:
return negative ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
return negative ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
default:
wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll");
return 0;
}
}
void update_cursor(struct swaybar *bar) {
struct swaybar_pointer *pointer = &bar->pointer;
if (pointer->cursor_theme) {
wl_cursor_theme_destroy(pointer->cursor_theme);
}
int scale = pointer->current ? pointer->current->scale : 1;
pointer->cursor_theme = wl_cursor_theme_load(NULL, 24 * scale, bar->shm);
struct wl_cursor *cursor;
cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr");
pointer->cursor_image = cursor->images[0];
wl_surface_set_buffer_scale(pointer->cursor_surface, scale);
wl_surface_attach(pointer->cursor_surface,
wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);
wl_pointer_set_cursor(pointer->pointer, pointer->serial,
pointer->cursor_surface,
pointer->cursor_image->hotspot_x / scale,
pointer->cursor_image->hotspot_y / scale);
wl_surface_damage_buffer(pointer->cursor_surface, 0, 0,
INT32_MAX, INT32_MAX);
wl_surface_commit(pointer->cursor_surface);
}
static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t surface_x, wl_fixed_t surface_y) {
struct swaybar *bar = data;
struct swaybar_pointer *pointer = &bar->pointer;
pointer->serial = serial;
struct swaybar_output *output;
wl_list_for_each(output, &bar->outputs, link) {
if (output->surface == surface) {
pointer->current = output;
break;
}
}
update_cursor(bar);
}
static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface) {
struct swaybar *bar = data;
bar->pointer.current = NULL;
}
static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
struct swaybar *bar = data;
bar->pointer.x = wl_fixed_to_int(surface_x);
bar->pointer.y = wl_fixed_to_int(surface_y);
}
static bool check_bindings(struct swaybar *bar, uint32_t button,
uint32_t state) {
bool released = state == WL_POINTER_BUTTON_STATE_RELEASED;
for (int i = 0; i < bar->config->bindings->length; i++) {
struct swaybar_binding *binding = bar->config->bindings->items[i];
if (binding->button == button && binding->release == released) {
ipc_execute_binding(bar, binding);
return true;
}
}
return false;
}
static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
struct swaybar *bar = data;
struct swaybar_pointer *pointer = &bar->pointer;
struct swaybar_output *output = pointer->current;
if (!sway_assert(output, "button with no active output")) {
return;
}
if (check_bindings(bar, button, state)) {
return;
}
if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
return;
}
struct swaybar_hotspot *hotspot;
wl_list_for_each(hotspot, &output->hotspots, link) {
double x = pointer->x * output->scale;
double y = pointer->y * output->scale;
if (x >= hotspot->x
&& y >= hotspot->y
&& x < hotspot->x + hotspot->width
&& y < hotspot->y + hotspot->height) {
if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot,
pointer->x, pointer->y, button, hotspot->data)) {
return;
}
}
}
}
static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) {
struct swaybar *bar = data;
struct swaybar_pointer *pointer = &bar->pointer;
struct swaybar_output *output = pointer->current;
if (!sway_assert(output, "axis with no active output")) {
return;
}
// If there is a button press binding, execute it, skip default behavior,
// and check button release bindings
uint32_t button = wl_axis_to_button(axis, value);
if (check_bindings(bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
return;
}
struct swaybar_hotspot *hotspot;
wl_list_for_each(hotspot, &output->hotspots, link) {
double x = pointer->x * output->scale;
double y = pointer->y * output->scale;
if (x >= hotspot->x
&& y >= hotspot->y
&& x < hotspot->x + hotspot->width
&& y < hotspot->y + hotspot->height) {
if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot,
pointer->x, pointer->y, button, hotspot->data)) {
return;
}
}
}
struct swaybar_config *config = bar->config;
double amt = wl_fixed_to_double(value);
if (amt == 0.0 || !config->workspace_buttons) {
check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
return;
}
if (!sway_assert(!wl_list_empty(&output->workspaces), "axis with no workspaces")) {
return;
}
struct swaybar_workspace *first =
wl_container_of(output->workspaces.next, first, link);
struct swaybar_workspace *last =
wl_container_of(output->workspaces.prev, last, link);
struct swaybar_workspace *active;
wl_list_for_each(active, &output->workspaces, link) {
if (active->visible) {
break;
}
}
if (!sway_assert(active->visible, "axis with null workspace")) {
return;
}
struct swaybar_workspace *new;
if (amt < 0.0) {
if (active == first) {
new = config->wrap_scroll ? last : NULL;
} else {
new = wl_container_of(active->link.prev, new, link);
}
} else {
if (active == last) {
new = config->wrap_scroll ? first : NULL;
} else {
new = wl_container_of(active->link.next, new, link);
}
}
if (new) {
ipc_send_workspace_command(bar, new->name);
}
// Check button release bindings
check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
}
static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
// Who cares
}
static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
uint32_t axis_source) {
// Who cares
}
static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis) {
// Who cares
}
static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
uint32_t axis, int32_t discrete) {
// Who cares
}
struct wl_pointer_listener pointer_listener = {
.enter = wl_pointer_enter,
.leave = wl_pointer_leave,
.motion = wl_pointer_motion,
.button = wl_pointer_button,
.axis = wl_pointer_axis,
.frame = wl_pointer_frame,
.axis_source = wl_pointer_axis_source,
.axis_stop = wl_pointer_axis_stop,
.axis_discrete = wl_pointer_axis_discrete,
};
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps) {
struct swaybar *bar = data;
if (bar->pointer.pointer != NULL) {
wl_pointer_release(bar->pointer.pointer);
bar->pointer.pointer = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
bar->pointer.pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar);
}
}
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
const char *name) {
// Who cares
}
const struct wl_seat_listener seat_listener = {
.capabilities = seat_handle_capabilities,
.name = seat_handle_name,
};