Added gesture support
This commit is contained in:
parent
6552778dd6
commit
0c6d69b7fe
|
@ -50,6 +50,8 @@ char *gesture_parse(const char *input, struct gesture *output) {
|
||||||
output->type = GESTURE_TYPE_PINCH;
|
output->type = GESTURE_TYPE_PINCH;
|
||||||
} else if (strcmp(split->items[0], "swipe") == 0) {
|
} else if (strcmp(split->items[0], "swipe") == 0) {
|
||||||
output->type = GESTURE_TYPE_SWIPE;
|
output->type = GESTURE_TYPE_SWIPE;
|
||||||
|
} else if (strcmp(split->items[0], "workspace_swipe") == 0) {
|
||||||
|
output->type = GESTURE_TYPE_WORKSPACE_SWIPE;
|
||||||
} else {
|
} else {
|
||||||
return strformat("expected hold|pinch|swipe, got %s",
|
return strformat("expected hold|pinch|swipe, got %s",
|
||||||
split->items[0]);
|
split->items[0]);
|
||||||
|
@ -117,11 +119,22 @@ const char *gesture_type_string(enum gesture_type type) {
|
||||||
return "pinch";
|
return "pinch";
|
||||||
case GESTURE_TYPE_SWIPE:
|
case GESTURE_TYPE_SWIPE:
|
||||||
return "swipe";
|
return "swipe";
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE:
|
||||||
|
return "workspace_swipe";
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int gesture_workspace_swipe_command_parse(char *cmd) {
|
||||||
|
if (strcmp(cmd, "normal") == 0) {
|
||||||
|
return -1;
|
||||||
|
} else if (strcmp(cmd, "invert") == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const char *gesture_direction_string(enum gesture_direction direction) {
|
const char *gesture_direction_string(enum gesture_direction direction) {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case GESTURE_DIRECTION_NONE:
|
case GESTURE_DIRECTION_NONE:
|
||||||
|
@ -314,6 +327,7 @@ struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
|
||||||
}
|
}
|
||||||
__attribute__ ((fallthrough));
|
__attribute__ ((fallthrough));
|
||||||
// Gestures with dx and dy
|
// Gestures with dx and dy
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE:
|
||||||
case GESTURE_TYPE_SWIPE:
|
case GESTURE_TYPE_SWIPE:
|
||||||
if (fabs(tracker->dx) > fabs(tracker->dy)) {
|
if (fabs(tracker->dx) > fabs(tracker->dy)) {
|
||||||
if (tracker->dx > 0) {
|
if (tracker->dx > 0) {
|
||||||
|
|
|
@ -12,11 +12,14 @@ enum gesture_type {
|
||||||
GESTURE_TYPE_HOLD,
|
GESTURE_TYPE_HOLD,
|
||||||
GESTURE_TYPE_PINCH,
|
GESTURE_TYPE_PINCH,
|
||||||
GESTURE_TYPE_SWIPE,
|
GESTURE_TYPE_SWIPE,
|
||||||
|
GESTURE_TYPE_WORKSPACE_SWIPE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Turns single type enum value to constant string representation.
|
// Turns single type enum value to constant string representation.
|
||||||
const char *gesture_type_string(enum gesture_type direction);
|
const char *gesture_type_string(enum gesture_type direction);
|
||||||
|
|
||||||
|
int gesture_workspace_swipe_command_parse(char *cmd);
|
||||||
|
|
||||||
// Value to use to accept any finger count
|
// Value to use to accept any finger count
|
||||||
extern const uint8_t GESTURE_FINGERS_ANY;
|
extern const uint8_t GESTURE_FINGERS_ANY;
|
||||||
|
|
||||||
|
|
|
@ -202,6 +202,12 @@ void handle_output_manager_test(struct wl_listener *listener, void *data);
|
||||||
void handle_output_power_manager_set_mode(struct wl_listener *listener,
|
void handle_output_power_manager_set_mode(struct wl_listener *listener,
|
||||||
void *data);
|
void *data);
|
||||||
|
|
||||||
|
void update_workspace_scroll_percent(int dx, int invert);
|
||||||
|
|
||||||
|
void snap_workspace_scroll_percent(int dx, int invert);
|
||||||
|
|
||||||
|
void reset_workspace_scroll_percent();
|
||||||
|
|
||||||
struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
|
struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -154,6 +154,19 @@ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, boo
|
||||||
return gesture_binding_remove(binding, argv[0]);
|
return gesture_binding_remove(binding, argv[0]);
|
||||||
}
|
}
|
||||||
binding->command = join_args(argv + 1, argc - 1);
|
binding->command = join_args(argv + 1, argc - 1);
|
||||||
|
// Make sure that the gesture command is valid
|
||||||
|
switch (binding->gesture.type) {
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE:
|
||||||
|
if (gesture_workspace_swipe_command_parse(binding->command) == 0) {
|
||||||
|
free(binding);
|
||||||
|
return cmd_results_new(CMD_FAILURE,
|
||||||
|
"Invalid %s command (%s). Either normal or invert",
|
||||||
|
bindtype, errmsg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
return gesture_binding_add(binding, argv[0], warn);
|
return gesture_binding_add(binding, argv[0], warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "sway/tree/root.h"
|
#include "sway/tree/root.h"
|
||||||
#include "sway/tree/view.h"
|
#include "sway/tree/view.h"
|
||||||
#include "sway/tree/workspace.h"
|
#include "sway/tree/workspace.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
struct sway_output *output_by_name_or_id(const char *name_or_id) {
|
struct sway_output *output_by_name_or_id(const char *name_or_id) {
|
||||||
for (int i = 0; i < root->outputs->length; ++i) {
|
for (int i = 0; i < root->outputs->length; ++i) {
|
||||||
|
@ -1103,3 +1104,95 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
|
||||||
oc = store_output_config(oc);
|
oc = store_output_config(oc);
|
||||||
apply_output_config(oc, output);
|
apply_output_config(oc, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void workspace_scroll_mark_dirty() {
|
||||||
|
// Damage all outputs
|
||||||
|
struct sway_output *soutput;
|
||||||
|
wl_list_for_each(soutput, &root->all_outputs, link) {
|
||||||
|
output_damage_whole(soutput);
|
||||||
|
}
|
||||||
|
transaction_commit_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_workspace_scroll_percent(int dx, int invert) {
|
||||||
|
struct sway_seat *seat = input_manager_get_default_seat();
|
||||||
|
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
||||||
|
struct sway_output *output = focused_ws->output;
|
||||||
|
|
||||||
|
int visible_index = list_find(output->workspaces, focused_ws);
|
||||||
|
if (visible_index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dx *= invert;
|
||||||
|
|
||||||
|
// TODO: Make the speed factor configurable?? Works well on my trackpad...
|
||||||
|
// Maybe also take in account the width of the actual trackpad??
|
||||||
|
const int SPEED_FACTOR = 500;
|
||||||
|
float percent = 0;
|
||||||
|
if (dx < 0) {
|
||||||
|
percent = (float) dx / SPEED_FACTOR;
|
||||||
|
} else if (dx > 0) {
|
||||||
|
percent = (float) dx / SPEED_FACTOR;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit the percent depending on if the workspace is the first/last or in
|
||||||
|
// the middle somewhere.
|
||||||
|
int min = -1, max = 1;
|
||||||
|
if (visible_index + 1 >= output->workspaces->length) {
|
||||||
|
max = 0;
|
||||||
|
}
|
||||||
|
if (visible_index == 0) {
|
||||||
|
min = 0;
|
||||||
|
}
|
||||||
|
output->workspace_scroll_percent = MIN(max, MAX(min, percent));
|
||||||
|
|
||||||
|
workspace_scroll_mark_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void snap_workspace_scroll_percent(int dx, int invert) {
|
||||||
|
struct sway_seat *seat = input_manager_get_default_seat();
|
||||||
|
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
||||||
|
struct sway_output *output = focused_ws->output;
|
||||||
|
|
||||||
|
// TODO: Make the threshold configurable??
|
||||||
|
const float THRESHOLD = 0.35;
|
||||||
|
if (ABS(output->workspace_scroll_percent) <= THRESHOLD) {
|
||||||
|
goto reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
dx *= invert;
|
||||||
|
|
||||||
|
int dir = 0;
|
||||||
|
if (dx < 0) {
|
||||||
|
dir = -1;
|
||||||
|
} else if (dx > 0) {
|
||||||
|
dir = 1;
|
||||||
|
} else {
|
||||||
|
goto reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int visible_index = list_find(output->workspaces, focused_ws);
|
||||||
|
|
||||||
|
size_t ws_index = wrap(visible_index + dir, output->workspaces->length);
|
||||||
|
struct sway_workspace *new_ws = output->workspaces->items[ws_index];
|
||||||
|
sway_log(SWAY_DEBUG, "Switched to workspace: %s\n", new_ws->name);
|
||||||
|
workspace_switch(new_ws);
|
||||||
|
seat_consider_warp_to_focus(seat);
|
||||||
|
|
||||||
|
reset:
|
||||||
|
// Reset the state
|
||||||
|
reset_workspace_scroll_percent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_workspace_scroll_percent() {
|
||||||
|
struct sway_seat *seat = input_manager_get_default_seat();
|
||||||
|
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
||||||
|
struct sway_output *output = focused_ws->output;
|
||||||
|
|
||||||
|
output->workspace_scroll_percent = 0;
|
||||||
|
|
||||||
|
workspace_scroll_mark_dirty();
|
||||||
|
}
|
||||||
|
|
|
@ -1011,6 +1011,9 @@ static void handle_swipe_begin(struct sway_seat *seat,
|
||||||
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
|
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
|
||||||
struct seatop_default_event *seatop = seat->seatop_data;
|
struct seatop_default_event *seatop = seat->seatop_data;
|
||||||
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
|
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
|
||||||
|
} else if (gesture_binding_check(bindings, GESTURE_TYPE_WORKSPACE_SWIPE, event->fingers, device)) {
|
||||||
|
struct seatop_default_event *seatop = seat->seatop_data;
|
||||||
|
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE, event->fingers);
|
||||||
} else {
|
} else {
|
||||||
// ... otherwise forward to client
|
// ... otherwise forward to client
|
||||||
struct sway_cursor *cursor = seat->cursor;
|
struct sway_cursor *cursor = seat->cursor;
|
||||||
|
@ -1028,6 +1031,41 @@ static void handle_swipe_update(struct sway_seat *seat,
|
||||||
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
|
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
|
||||||
gesture_tracker_update(&seatop->gestures,
|
gesture_tracker_update(&seatop->gestures,
|
||||||
event->dx, event->dy, NAN, NAN);
|
event->dx, event->dy, NAN, NAN);
|
||||||
|
} else if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE)) {
|
||||||
|
gesture_tracker_update(&seatop->gestures,
|
||||||
|
event->dx, event->dy, NAN, NAN);
|
||||||
|
|
||||||
|
struct gesture_tracker *tracker = &seatop->gestures;
|
||||||
|
|
||||||
|
struct sway_input_device *device =
|
||||||
|
event->pointer ? event->pointer->base.data : NULL;
|
||||||
|
// Determine name of input that received gesture
|
||||||
|
char *input = device
|
||||||
|
? input_device_get_identifier(device->wlr_device)
|
||||||
|
: strdup("*");
|
||||||
|
|
||||||
|
struct gesture gesture = {
|
||||||
|
.fingers = tracker->fingers,
|
||||||
|
.type = tracker->type,
|
||||||
|
};
|
||||||
|
if (fabs(tracker->dx) > fabs(tracker->dy)) {
|
||||||
|
if (tracker->dx > 0) {
|
||||||
|
gesture.directions |= GESTURE_DIRECTION_RIGHT;
|
||||||
|
} else {
|
||||||
|
gesture.directions |= GESTURE_DIRECTION_LEFT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tracker->dy > 0) {
|
||||||
|
gesture.directions |= GESTURE_DIRECTION_DOWN;
|
||||||
|
} else {
|
||||||
|
gesture.directions |= GESTURE_DIRECTION_UP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sway_gesture_binding *current = gesture_binding_match(config->current_mode->gesture_bindings, &gesture, input);
|
||||||
|
|
||||||
|
int invert = gesture_workspace_swipe_command_parse(current->command);
|
||||||
|
update_workspace_scroll_percent(seatop->gestures.dx, invert);
|
||||||
} else {
|
} else {
|
||||||
// ... otherwise forward to client
|
// ... otherwise forward to client
|
||||||
struct sway_cursor *cursor = seat->cursor;
|
struct sway_cursor *cursor = seat->cursor;
|
||||||
|
@ -1041,13 +1079,21 @@ static void handle_swipe_end(struct sway_seat *seat,
|
||||||
struct wlr_pointer_swipe_end_event *event) {
|
struct wlr_pointer_swipe_end_event *event) {
|
||||||
// Ensure gesture is being tracked and was not cancelled
|
// Ensure gesture is being tracked and was not cancelled
|
||||||
struct seatop_default_event *seatop = seat->seatop_data;
|
struct seatop_default_event *seatop = seat->seatop_data;
|
||||||
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
|
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE) &&
|
||||||
|
!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE)) {
|
||||||
struct sway_cursor *cursor = seat->cursor;
|
struct sway_cursor *cursor = seat->cursor;
|
||||||
wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures,
|
wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures,
|
||||||
cursor->seat->wlr_seat, event->time_msec, event->cancelled);
|
cursor->seat->wlr_seat, event->time_msec, event->cancelled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event->cancelled) {
|
if (event->cancelled) {
|
||||||
|
switch (seatop->gestures.type) {
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE:
|
||||||
|
reset_workspace_scroll_percent();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
gesture_tracker_cancel(&seatop->gestures);
|
gesture_tracker_cancel(&seatop->gestures);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1059,7 +1105,15 @@ static void handle_swipe_end(struct sway_seat *seat,
|
||||||
&seatop->gestures, device);
|
&seatop->gestures, device);
|
||||||
|
|
||||||
if (binding) {
|
if (binding) {
|
||||||
gesture_binding_execute(seat, binding);
|
switch (binding->gesture.type) {
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE:;
|
||||||
|
int invert = gesture_workspace_swipe_command_parse(binding->command);
|
||||||
|
snap_workspace_scroll_percent(seatop->gestures.dx, invert);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
gesture_binding_execute(seat, binding);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue