diff --git a/src/config/parse_config.h b/src/config/parse_config.h index fda401d9..0d1fc20d 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -158,6 +158,15 @@ typedef struct { Arg arg; } GestureBinding; +typedef struct { + uint32_t swipe; + uint32_t edge; + uint32_t distance; + uint32_t fingers_count; + int32_t (*func)(const Arg *); + Arg arg; +} TouchGestureBinding; + typedef struct { int32_t id; char *layout_name; @@ -282,6 +291,14 @@ typedef struct { double axis_scroll_factor; + double touch_distance_threshold; + double touch_degrees_leniency; + uint32_t touch_timeoutms; + double touch_edge_size_left; + double touch_edge_size_top; + double touch_edge_size_right; + double touch_edge_size_bottom; + int32_t blur; int32_t blur_layer; int32_t blur_optimized; @@ -344,6 +361,9 @@ typedef struct { GestureBinding *gesture_bindings; int32_t gesture_bindings_count; + TouchGestureBinding *touch_gesture_bindings; + int32_t touch_gesture_bindings_count; + ConfigEnv **env; int32_t env_count; @@ -532,6 +552,92 @@ int32_t parse_direction(const char *str) { } } +int32_t parse_touch_direction(const char *str) { + char lowerStr[11]; + int32_t i = 0; + while (str[i] && i < 10) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; + + if (strcmp(lowerStr, "up") == 0) { + return TOUCH_SWIPE_UP; + } else if (strcmp(lowerStr, "down") == 0) { + return TOUCH_SWIPE_DOWN; + } else if (strcmp(lowerStr, "left") == 0) { + return TOUCH_SWIPE_LEFT; + } else if (strcmp(lowerStr, "right") == 0) { + return TOUCH_SWIPE_RIGHT; + } else if (strcmp(lowerStr, "up_left") == 0) { + return TOUCH_SWIPE_UP_LEFT; + } else if (strcmp(lowerStr, "up_right") == 0) { + return TOUCH_SWIPE_UP_RIGHT; + } else if (strcmp(lowerStr, "down_left") == 0) { + return TOUCH_SWIPE_DOWN_LEFT; + } else if (strcmp(lowerStr, "down_right") == 0) { + return TOUCH_SWIPE_DOWN_RIGHT; + } else { + return TOUCH_SWIPE_NONE; + } +} + +int32_t parse_touch_edge(const char *str) { + char lowerStr[13]; + int32_t i = 0; + while (str[i] && i < 12) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; + + if (strcmp(lowerStr, "any") == 0) { + return EDGE_ANY; + } else if (strcmp(lowerStr, "none") == 0) { + return EDGE_NONE; + } else if (strcmp(lowerStr, "left") == 0) { + return EDGE_LEFT; + } else if (strcmp(lowerStr, "right") == 0) { + return EDGE_RIGHT; + } else if (strcmp(lowerStr, "top") == 0) { + return EDGE_TOP; + } else if (strcmp(lowerStr, "bottom") == 0) { + return EDGE_BOTTOM; + } else if (strcmp(lowerStr, "top_left") == 0) { + return CORNER_TOP_LEFT; + } else if (strcmp(lowerStr, "top_right") == 0) { + return CORNER_TOP_RIGHT; + } else if (strcmp(lowerStr, "bottom_left") == 0) { + return CORNER_BOTTOM_LEFT; + } else if (strcmp(lowerStr, "bottom_right") == 0) { + return CORNER_BOTTOM_RIGHT; + } else { + return EDGE_ANY; + } +} + +int32_t parse_distance(const char *str) { + char lowerStr[7]; + int32_t i = 0; + while (str[i] && i < 6) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; + + if (strcmp(lowerStr, "any") == 0) { + return DISTANCE_ANY; + } else if (strcmp(lowerStr, "short") == 0) { + return DISTANCE_SHORT; + } else if (strcmp(lowerStr, "medium") == 0) { + return DISTANCE_MEDIUM; + } else if (strcmp(lowerStr, "long") == 0) { + return DISTANCE_LONG; + } else { + return DISTANCE_ANY; + } +} + int32_t parse_fold_state(const char *str) { // 将输入字符串转换为小写 char lowerStr[10]; @@ -1677,6 +1783,20 @@ bool parse_option(Config *config, char *key, char *value) { config->button_map = atoi(value); } else if (strcmp(key, "axis_scroll_factor") == 0) { config->axis_scroll_factor = atof(value); + } else if (strcmp(key, "touch_distance_threshold") == 0) { + config->touch_distance_threshold = atof(value); + } else if (strcmp(key, "touch_degrees_leniency") == 0) { + config->touch_degrees_leniency = atof(value); + } else if (strcmp(key, "touch_timeoutms") == 0) { + config->touch_timeoutms = atoi(value); + } else if (strcmp(key, "touch_edge_size_left") == 0) { + config->touch_edge_size_left = atof(value); + } else if (strcmp(key, "touch_edge_size_top") == 0) { + config->touch_edge_size_top = atof(value); + } else if (strcmp(key, "touch_edge_size_right") == 0) { + config->touch_edge_size_right = atof(value); + } else if (strcmp(key, "touch_edge_size_bottom") == 0) { + config->touch_edge_size_bottom = atof(value); } else if (strcmp(key, "gappih") == 0) { config->gappih = atoi(value); } else if (strcmp(key, "gappiv") == 0) { @@ -2699,6 +2819,91 @@ bool parse_option(Config *config, char *key, char *value) { config->gesture_bindings_count++; } + } else if (strncmp(key, "touchgesturebind", 16) == 0) { + config->touch_gesture_bindings = + realloc(config->touch_gesture_bindings, + (config->touch_gesture_bindings_count + 1) * + sizeof(TouchGestureBinding)); + if (!config->touch_gesture_bindings) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for axis touchgesturebind\n"); + return false; + } + + TouchGestureBinding *binding = + &config + ->touch_gesture_bindings[config->touch_gesture_bindings_count]; + memset(binding, 0, sizeof(TouchGestureBinding)); + + char swipe_str[256], edge_str[256], distance_str[256], + fingers_count_str[256], func_name[256], + arg_value[256] = "0\0", arg_value2[256] = "0\0", + arg_value3[256] = "0\0", arg_value4[256] = "0\0", + arg_value5[256] = "0\0"; + if (sscanf(value, + "%255[^,],%255[^,],%255[^,],%255[^,],%255[" + "^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[^\n]", + swipe_str, edge_str, distance_str, fingers_count_str, + func_name, arg_value, arg_value2, arg_value3, arg_value4, + arg_value5) < 4) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid touchgesturebind " + "format: %s\n", + value); + return false; + } + + trim_whitespace(swipe_str); + trim_whitespace(edge_str); + trim_whitespace(distance_str); + trim_whitespace(fingers_count_str); + trim_whitespace(func_name); + trim_whitespace(arg_value); + trim_whitespace(arg_value2); + trim_whitespace(arg_value3); + trim_whitespace(arg_value4); + trim_whitespace(arg_value5); + + binding->swipe = parse_touch_direction(swipe_str); + binding->edge = parse_touch_edge(edge_str); + binding->distance = parse_distance(distance_str); + binding->fingers_count = atoi(fingers_count_str); + binding->arg.i = 0; + binding->arg.i2 = 0; + binding->arg.f = 0.0f; + binding->arg.f2 = 0.0f; + binding->arg.ui = 0; + binding->arg.ui2 = 0; + binding->arg.v = NULL; + binding->arg.v2 = NULL; + binding->arg.v3 = NULL; + binding->func = + parse_func_name(func_name, &binding->arg, arg_value, arg_value2, + arg_value3, arg_value4, arg_value5); + + if (!binding->func) { + if (binding->arg.v) { + free(binding->arg.v); + binding->arg.v = NULL; + } + if (binding->arg.v2) { + free(binding->arg.v2); + binding->arg.v2 = NULL; + } + if (binding->arg.v3) { + free(binding->arg.v3); + binding->arg.v3 = NULL; + } + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "dispatch in " + "touchgesturebind: \033[1m\033[31m%s\n", + func_name); + return false; + } else { + config->touch_gesture_bindings_count++; + } } else if (strncmp(key, "source-optional", 15) == 0) { parse_config_file(config, value, false); } else if (strncmp(key, "source", 6) == 0) { @@ -2995,6 +3200,27 @@ void free_config(void) { config.gesture_bindings_count = 0; } + // release touch_gesture_bindings + if (config.touch_gesture_bindings) { + for (i = 0; i < config.touch_gesture_bindings_count; i++) { + if (config.touch_gesture_bindings[i].arg.v) { + free((void *)config.touch_gesture_bindings[i].arg.v); + config.touch_gesture_bindings[i].arg.v = NULL; + } + if (config.touch_gesture_bindings[i].arg.v2) { + free((void *)config.touch_gesture_bindings[i].arg.v2); + config.touch_gesture_bindings[i].arg.v2 = NULL; + } + if (config.touch_gesture_bindings[i].arg.v3) { + free((void *)config.touch_gesture_bindings[i].arg.v3); + config.touch_gesture_bindings[i].arg.v3 = NULL; + } + } + free(config.touch_gesture_bindings); + config.touch_gesture_bindings = NULL; + config.touch_gesture_bindings_count = 0; + } + // 释放 tag_rules if (config.tag_rules) { for (int32_t i = 0; i < config.tag_rules_count; i++) { @@ -3219,6 +3445,19 @@ void override_config(void) { config.button_map = CLAMP_INT(config.button_map, 0, 1); config.axis_scroll_factor = CLAMP_FLOAT(config.axis_scroll_factor, 0.1f, 10.0f); + config.touch_distance_threshold = + CLAMP_FLOAT(config.touch_distance_threshold, 1.0f, 10000.0f); + config.touch_degrees_leniency = + CLAMP_FLOAT(config.touch_degrees_leniency, 0.0f, 45.0f); + config.touch_timeoutms = CLAMP_INT(config.touch_timeoutms, 1, 60000); + config.touch_edge_size_left = + CLAMP_FLOAT(config.touch_edge_size_left, 1.0f, 10000.0f); + config.touch_edge_size_top = + CLAMP_FLOAT(config.touch_edge_size_top, 1.0f, 10000.0f); + config.touch_edge_size_right = + CLAMP_FLOAT(config.touch_edge_size_right, 1.0f, 10000.0f); + config.touch_edge_size_bottom = + CLAMP_FLOAT(config.touch_edge_size_bottom, 1.0f, 10000.0f); config.gappih = CLAMP_INT(config.gappih, 0, 1000); config.gappiv = CLAMP_INT(config.gappiv, 0, 1000); config.gappoh = CLAMP_INT(config.gappoh, 0, 1000); @@ -3311,6 +3550,13 @@ void set_value_default() { config.scratchpad_cross_monitor = 0; config.focus_cross_tag = 0; config.axis_scroll_factor = 1.0; + config.touch_distance_threshold = 50.0; + config.touch_degrees_leniency = 15.0; + config.touch_timeoutms = 800; + config.touch_edge_size_left = 50.0; + config.touch_edge_size_top = 50.0; + config.touch_edge_size_right = 50.0; + config.touch_edge_size_bottom = 50.0; config.view_current_to_back = 0; config.single_scratchpad = 1; config.xwayland_persistence = 1; @@ -3505,6 +3751,8 @@ bool parse_config(void) { config.switch_bindings_count = 0; config.gesture_bindings = NULL; config.gesture_bindings_count = 0; + config.touch_gesture_bindings = NULL; + config.touch_gesture_bindings_count = 0; config.env = NULL; config.env_count = 0; config.exec = NULL; diff --git a/src/dispatch/gesture.h b/src/dispatch/gesture.h new file mode 100644 index 00000000..c2d63c56 --- /dev/null +++ b/src/dispatch/gesture.h @@ -0,0 +1,237 @@ +int32_t gesture_calculate_swipe_within_degrees(double gestdegrees, + double wantdegrees) { + return (gestdegrees >= wantdegrees - config.touch_degrees_leniency && + gestdegrees <= wantdegrees + config.touch_degrees_leniency); +} + +uint32_t gesture_calculate_swipe(double x0, double y0, double x1, double y1) { + double t, degrees, distance; + + t = atan2(x1 - x0, y0 - y1); + degrees = 57.2957795130823209 * (t < 0 ? t + 6.2831853071795865 : t); + distance = sqrt(pow(x1 - x0, 2) + pow(y1 - y0, 2)); + + wlr_log(WLR_DEBUG, "Swipe distance=[%.2f]; degrees=[%.2f]", distance, + degrees); + + if (distance < config.touch_distance_threshold) + return TOUCH_SWIPE_NONE; + else if (gesture_calculate_swipe_within_degrees(degrees, 0)) + return TOUCH_SWIPE_UP; + else if (gesture_calculate_swipe_within_degrees(degrees, 45)) + return TOUCH_SWIPE_UP_RIGHT; + else if (gesture_calculate_swipe_within_degrees(degrees, 90)) + return TOUCH_SWIPE_RIGHT; + else if (gesture_calculate_swipe_within_degrees(degrees, 135)) + return TOUCH_SWIPE_DOWN_RIGHT; + else if (gesture_calculate_swipe_within_degrees(degrees, 180)) + return TOUCH_SWIPE_DOWN; + else if (gesture_calculate_swipe_within_degrees(degrees, 225)) + return TOUCH_SWIPE_DOWN_LEFT; + else if (gesture_calculate_swipe_within_degrees(degrees, 270)) + return TOUCH_SWIPE_LEFT; + else if (gesture_calculate_swipe_within_degrees(degrees, 315)) + return TOUCH_SWIPE_UP_LEFT; + else if (gesture_calculate_swipe_within_degrees(degrees, 360)) + return TOUCH_SWIPE_UP; + + return TOUCH_SWIPE_NONE; +} + +uint32_t gesture_calculate_distance(Monitor *m, double x0, double y0, double x1, + double y1, int32_t swipe) { + double distance = sqrt(pow(x1 - x0, 2) + pow(y1 - y0, 2)); + double diag = sqrt(pow(m->m.width, 2) + pow(m->m.height, 2)); + switch (swipe) { + case TOUCH_SWIPE_UP: + case TOUCH_SWIPE_DOWN: + if (distance >= m->m.height * 0.66) { + return DISTANCE_LONG; + } else if (distance >= m->m.height * 0.33) { + return DISTANCE_MEDIUM; + } else { + return DISTANCE_SHORT; + } + break; + case TOUCH_SWIPE_RIGHT: + case TOUCH_SWIPE_LEFT: + if (distance >= m->m.width * 0.66) { + return DISTANCE_LONG; + } else if (distance >= m->m.width * 0.33) { + return DISTANCE_MEDIUM; + } else { + return DISTANCE_SHORT; + } + break; + case TOUCH_SWIPE_UP_RIGHT: + case TOUCH_SWIPE_UP_LEFT: + case TOUCH_SWIPE_DOWN_RIGHT: + case TOUCH_SWIPE_DOWN_LEFT: + if (distance >= diag * 0.66) { + return DISTANCE_LONG; + } else if (distance >= diag * 0.33) { + return DISTANCE_MEDIUM; + } else { + return DISTANCE_SHORT; + } + break; + } + + return 0; // shouldn't happen +} + +uint32_t gesture_calculate_edge(Monitor *m, double x0, double y0, double x1, + double y1) { + uint32_t horizontal = EDGE_NONE; + uint32_t vertical = EDGE_NONE; + + if (x0 <= config.touch_edge_size_left) { + horizontal = EDGE_LEFT; + } else if (x0 >= m->m.width - config.touch_edge_size_right) { + horizontal = EDGE_RIGHT; + } else if (x1 <= config.touch_edge_size_left) { + horizontal = EDGE_LEFT; + } else if (x1 >= m->m.width - config.touch_edge_size_right) { + horizontal = EDGE_RIGHT; + } + if (y0 <= config.touch_edge_size_top) { + vertical = EDGE_TOP; + } else if (y0 >= m->m.height - config.touch_edge_size_bottom) { + vertical = EDGE_BOTTOM; + } else if (y1 <= config.touch_edge_size_top) { + vertical = EDGE_TOP; + } else if (y1 >= m->m.height - config.touch_edge_size_bottom) { + vertical = EDGE_BOTTOM; + } + if (horizontal == EDGE_LEFT && vertical == EDGE_TOP) { + return CORNER_TOP_LEFT; + } else if (horizontal == EDGE_RIGHT && vertical == EDGE_TOP) { + return CORNER_TOP_RIGHT; + } else if (horizontal == EDGE_LEFT && vertical == EDGE_BOTTOM) { + return CORNER_BOTTOM_LEFT; + } else if (horizontal == EDGE_RIGHT && vertical == EDGE_BOTTOM) { + return CORNER_BOTTOM_RIGHT; + } else if (horizontal != EDGE_NONE) { + return horizontal; + } else { + return vertical; + } +} + +int32_t gesture_execute(int32_t nfingers, uint32_t swipe, uint32_t edge, + uint32_t distance) { + int32_t i; + int32_t handled = 0; + + wlr_log(WLR_DEBUG, "f:%d s:%d e:%d d:%d", nfingers, swipe, edge, distance); + + TouchGestureBinding *g; + for (i = 0; i < config.touch_gesture_bindings_count; i++) { + g = &config.touch_gesture_bindings[i]; + if (swipe == g->swipe && nfingers == g->fingers_count && + distance >= g->distance && + (g->edge == EDGE_ANY || edge == g->edge || + ((edge == CORNER_TOP_LEFT || edge == CORNER_TOP_RIGHT) && + g->edge == EDGE_TOP) || + ((edge == CORNER_BOTTOM_LEFT || edge == CORNER_BOTTOM_RIGHT) && + g->edge == EDGE_BOTTOM) || + ((edge == CORNER_TOP_LEFT || edge == CORNER_BOTTOM_LEFT) && + g->edge == EDGE_LEFT) || + ((edge == CORNER_TOP_RIGHT || edge == CORNER_BOTTOM_RIGHT) && + g->edge == EDGE_RIGHT)) && + g->func) { + g->func(&g->arg); + handled = 1; + } + } + return handled; +} + +void gesture_consume(TouchGroup *tg, TouchPoint *t) { + if (t->consumed_by_gesture) + return; + + struct timespec now; + struct wlr_touch_cancel_event *event = + ecalloc(1, sizeof(struct wlr_touch_cancel_event)); + + clock_gettime(CLOCK_MONOTONIC_RAW, &now); + + event->touch_id = t->touch_id; + event->touch = tg->touch; + event->time_msec = now.tv_sec * 1000 + now.tv_nsec / 1000000; + + wlr_log(WLR_DEBUG, "gesture_consume id: %d", t->touch_id); + + t->consumed_by_gesture = true; + + handle_touchcancel(event); + + free(event); +} + +void gesture_touch_down(TouchGroup *tg, TouchPoint *t, double x, double y) { + wlr_log(WLR_DEBUG, "touch_down id: %d", t->touch_id); + + t->start_x = x; + t->start_y = y; + t->end_x = x; + t->end_y = y; + + if (wl_list_empty(&tg->touch_points)) + clock_gettime(CLOCK_MONOTONIC_RAW, &tg->time_down); +} + +void gesture_touch_motion(TouchGroup *tg, TouchPoint *t, double x, double y) { + t->end_x = x; + t->end_y = y; +} + +void gesture_touch_up(TouchGroup *tg, TouchPoint *t) { + struct timespec now; + + wlr_log(WLR_DEBUG, "touch_up id: %d", t->touch_id); + wlr_log(WLR_DEBUG, "len: %d", wl_list_length(&tg->touch_points)); + + clock_gettime(CLOCK_MONOTONIC_RAW, &now); + + uint32_t swipe = + gesture_calculate_swipe(t->start_x, t->start_y, t->end_x, t->end_y); + + if (swipe == TOUCH_SWIPE_NONE) { + goto cleanup; + } + + if (tg->touch_points_pending_swipe == 0) { + tg->pending_swipe = swipe; + } + if (tg->pending_swipe != swipe) { + goto cleanup; + } + + tg->touch_points_pending_swipe++; + + // All fingers up - check if within millisecond limit, exec, & reset + // (we are the last finger) + if (wl_list_length(&tg->touch_points) == 1) { + uint32_t edge = gesture_calculate_edge(tg->m, t->start_x, t->start_y, + t->end_x, t->end_y); + uint32_t distance = gesture_calculate_distance( + tg->m, t->start_x, t->start_y, t->end_x, t->end_y, swipe); + + if (config.touch_timeoutms > + ((now.tv_sec - tg->time_down.tv_sec) * 1000 + + (now.tv_nsec - tg->time_down.tv_nsec) / 1000000)) { + if (gesture_execute(tg->touch_points_pending_swipe, + tg->pending_swipe, edge, distance)) + gesture_consume(tg, t); + + tg->touch_points_pending_swipe = 0; + } + return; + } + +cleanup: + if (wl_list_length(&tg->touch_points) == 1) + tg->touch_points_pending_swipe = 0; +} diff --git a/src/mango.c b/src/mango.c index 576ee6eb..f9bebda4 100644 --- a/src/mango.c +++ b/src/mango.c @@ -149,6 +149,32 @@ enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; enum { VERTICAL, HORIZONTAL }; enum { SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT }; +enum { + TOUCH_SWIPE_UP, + TOUCH_SWIPE_DOWN, + TOUCH_SWIPE_RIGHT, + TOUCH_SWIPE_LEFT, + TOUCH_SWIPE_UP_RIGHT, + TOUCH_SWIPE_UP_LEFT, + TOUCH_SWIPE_DOWN_LEFT, + TOUCH_SWIPE_DOWN_RIGHT, + TOUCH_SWIPE_NONE +}; + +enum { + EDGE_ANY, + EDGE_NONE, + EDGE_LEFT, + EDGE_RIGHT, + EDGE_TOP, + EDGE_BOTTOM, + CORNER_TOP_LEFT, + CORNER_TOP_RIGHT, + CORNER_BOTTOM_LEFT, + CORNER_BOTTOM_RIGHT, +}; + +enum { DISTANCE_ANY, DISTANCE_SHORT, DISTANCE_MEDIUM, DISTANCE_LONG }; enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ enum { XDGShell, LayerShell, X11 }; /* client types */ enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 @@ -455,12 +481,17 @@ typedef struct { typedef struct { struct wl_list link; int32_t touch_id; + double start_x, start_y, end_x, end_y; + bool consumed_by_gesture; } TouchPoint; typedef struct TouchGroup { struct wl_list link; struct wlr_touch *touch; struct wl_list touch_points; + struct timespec time_down; + uint32_t pending_swipe; + uint32_t touch_points_pending_swipe; Monitor *m; } TouchGroup; @@ -700,6 +731,7 @@ static void touchup(struct wl_listener *listener, void *data); static void touchframe(struct wl_listener *listener, void *data); static void touchmotion(struct wl_listener *listener, void *data); static void touchcancel(struct wl_listener *listener, void *data); +static void handle_touchcancel(struct wlr_touch_cancel_event *event); static void unlocksession(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); @@ -1034,6 +1066,7 @@ static struct wl_event_source *sync_keymap; #include "animation/layer.h" #include "animation/tag.h" #include "dispatch/bind_define.h" +#include "dispatch/gesture.h" #include "ext-protocol/all.h" #include "fetch/fetch.h" #include "layout/arrange.h" @@ -5886,10 +5919,8 @@ void touchdown(struct wl_listener *listener, void *data) { double dx, dy; struct wlr_surface *surface; Client *c = NULL; - Monitor *m; - - t->touch_id = event->touch_id; - wl_list_insert(&tg->touch_points, &t->link); + Monitor *m = NULL; + Monitor *m_iter; wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); @@ -5899,8 +5930,8 @@ void touchdown(struct wl_listener *listener, void *data) { // Map the input to the appropriate output, to ensure that rotation is // handled. - wl_list_for_each(m, &mons, link) { - if (m == NULL || m->wlr_output == NULL) { + wl_list_for_each(m_iter, &mons, link) { + if (m_iter == NULL || m_iter->wlr_output == NULL) { continue; } if (event->touch->output_name != NULL && @@ -5909,12 +5940,23 @@ void touchdown(struct wl_listener *listener, void *data) { } wlr_cursor_map_input_to_output(cursor, &event->touch->base, - m->wlr_output); + m_iter->wlr_output); + m = m_iter; + break; } + /* ensure touch group has a monitor */ + if (!tg->m) + tg->m = m; // TODO: properly handle output_name = null, instead of + // falling back to last monitor in the list; + wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly); + t->touch_id = event->touch_id; + gesture_touch_down(tg, t, lx, ly); + wl_list_insert(&tg->touch_points, &t->link); + /* Find the client under the pointer and send the event along. */ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy); if (surface != NULL && wlr_surface_accepts_touch(surface, seat)) { @@ -5942,7 +5984,7 @@ void touchdown(struct wl_listener *listener, void *data) { .time_msec = event->time_msec, .button = BTN_LEFT, .state = WL_POINTER_BUTTON_STATE_PRESSED}; - buttonpress(listener, &button_event); + buttonpress(NULL, &button_event); } } @@ -5960,6 +6002,15 @@ void touchup(struct wl_listener *listener, void *data) { } if (!t) // invalid or cancelled return; + + gesture_touch_up(tg, t); + + if (t->consumed_by_gesture) { + wl_list_remove(&t->link); + free(t); + return; + } + wl_list_remove(&t->link); free(t); @@ -5970,7 +6021,7 @@ void touchup(struct wl_listener *listener, void *data) { .time_msec = event->time_msec, .button = BTN_LEFT, .state = WL_POINTER_BUTTON_STATE_RELEASED}; - buttonpress(listener, &button_event); + buttonpress(NULL, &button_event); emulating_pointer_from_touch = false; } @@ -6017,10 +6068,13 @@ void touchmotion(struct wl_listener *listener, void *data) { if (!t) // invalid or cancelled return; + wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, + event->y, &lx, &ly); + + gesture_touch_motion(tg, t, lx, ly); + if (emulating_pointer_from_touch) { if (emulated_pointer_touch_id == event->touch_id) { - wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, - event->x, event->y, &lx, &ly); dx = lx - cursor->x; dy = ly - cursor->y; motionnotify(event->time_msec, &event->touch->base, dx, dy, dx, dy); @@ -6034,8 +6088,6 @@ void touchmotion(struct wl_listener *listener, void *data) { return; } - wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, - event->y, &lx, &ly); surface = p->surface; if (surface && surface->data) { tree = surface->data; @@ -6066,9 +6118,6 @@ void touchcancel(struct wl_listener *listener, void *data) { TouchGroup *tg = event->touch->data; TouchPoint *t = NULL; TouchPoint *t_iter; - struct wlr_touch_point *p = NULL; - struct wl_client *client = NULL; - struct wlr_seat_client *seat_client = NULL; wl_list_for_each(t_iter, &tg->touch_points, link) { if (t_iter->touch_id == event->touch_id) { @@ -6082,6 +6131,17 @@ void touchcancel(struct wl_listener *listener, void *data) { wl_list_remove(&t->link); free(t); + if (wl_list_length(&tg->touch_points) == 0) + tg->touch_points_pending_swipe = 0; + + handle_touchcancel(event); +} + +void handle_touchcancel(struct wlr_touch_cancel_event *event) { + struct wlr_touch_point *p = NULL; + struct wl_client *client = NULL; + struct wlr_seat_client *seat_client = NULL; + if (emulating_pointer_from_touch) { if (emulated_pointer_touch_id == event->touch_id) { struct wlr_pointer_button_event button_event = { @@ -6089,7 +6149,7 @@ void touchcancel(struct wl_listener *listener, void *data) { .time_msec = event->time_msec, .button = BTN_LEFT, .state = WL_POINTER_BUTTON_STATE_RELEASED}; - buttonpress(listener, &button_event); + buttonpress(NULL, &button_event); emulating_pointer_from_touch = false; }