tmp: testing out touch gestures

This commit is contained in:
ernestoCruz05 2026-04-06 11:14:43 +01:00
commit a95e78f58a
6 changed files with 872 additions and 15 deletions

View file

@ -261,3 +261,22 @@ axisbind=SUPER,DOWN,viewtoright_have_client
# layer rule # layer rule
layerrule=animation_type_open:zoom,layer_name:rofi layerrule=animation_type_open:zoom,layer_name:rofi
layerrule=animation_type_close:zoom,layer_name:rofi layerrule=animation_type_close:zoom,layer_name:rofi
# Touch gesture settings
touch_distance_threshold = 100
touch_degrees_leniency = 30
touch_timeoutms = 1000
touch_edge_size_left = 50
touch_edge_size_top = 50
touch_edge_size_right = 50
touch_edge_size_bottom = 50
# Canvas touch gestures
touchgesturebind=up,any,any,2,canvas_zoom_resize,1.3
touchgesturebind=down,any,any,2,canvas_zoom_resize,0.7
# 3-finger swipe: pan
touchgesturebind=right,any,any,3,canvas_pan,300,0
touchgesturebind=left,any,any,3,canvas_pan,-300,0
touchgesturebind=up,any,any,3,canvas_pan,0,300
touchgesturebind=down,any,any,3,canvas_pan,0,-300

View file

@ -128,18 +128,8 @@ typedef struct {
// 默认按键绑定数组 // 默认按键绑定数组
KeyBinding default_key_bindings[] = { KeyBinding default_key_bindings[] = {
CHVT(1), CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
CHVT(2), CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
CHVT(3),
CHVT(4),
CHVT(5),
CHVT(6),
CHVT(7),
CHVT(8),
CHVT(9),
CHVT(10),
CHVT(11),
CHVT(12),
}; };
typedef struct { typedef struct {
@ -170,6 +160,15 @@ typedef struct {
Arg arg; Arg arg;
} GestureBinding; } 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 { typedef struct {
int32_t id; int32_t id;
char *layout_name; char *layout_name;
@ -296,6 +295,13 @@ typedef struct {
char *tablet_map_to_mon; char *tablet_map_to_mon;
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;
int32_t blur_layer; int32_t blur_layer;
int32_t blur_optimized; int32_t blur_optimized;
@ -361,6 +367,9 @@ typedef struct {
GestureBinding *gesture_bindings; GestureBinding *gesture_bindings;
int32_t gesture_bindings_count; int32_t gesture_bindings_count;
TouchGestureBinding *touch_gesture_bindings;
int32_t touch_gesture_bindings_count;
ConfigEnv **env; ConfigEnv **env;
int32_t env_count; int32_t env_count;
@ -549,6 +558,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) { int32_t parse_fold_state(const char *str) {
// 将输入字符串转换为小写 // 将输入字符串转换为小写
char lowerStr[10]; char lowerStr[10];
@ -1226,6 +1321,10 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
} else if (strcmp(func_name, "canvas_zoom_resize") == 0) { } else if (strcmp(func_name, "canvas_zoom_resize") == 0) {
func = canvas_zoom_resize; func = canvas_zoom_resize;
(*arg).f = atof(arg_value); (*arg).f = atof(arg_value);
} else if (strcmp(func_name, "canvas_pan") == 0) {
func = canvas_pan;
(*arg).f = atof(arg_value);
(*arg).f2 = atof(arg_value2);
} else if (strcmp(func_name, "canvas_overview_toggle") == 0) { } else if (strcmp(func_name, "canvas_overview_toggle") == 0) {
func = canvas_overview_toggle; func = canvas_overview_toggle;
} else if (strcmp(func_name, "canvas_fill_viewport") == 0) { } else if (strcmp(func_name, "canvas_fill_viewport") == 0) {
@ -1709,6 +1808,20 @@ bool parse_option(Config *config, char *key, char *value) {
if (config->tablet_map_to_mon) if (config->tablet_map_to_mon)
free(config->tablet_map_to_mon); free(config->tablet_map_to_mon);
config->tablet_map_to_mon = strdup(value); config->tablet_map_to_mon = strdup(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) { } else if (strcmp(key, "gappih") == 0) {
config->gappih = atoi(value); config->gappih = atoi(value);
} else if (strcmp(key, "gappiv") == 0) { } else if (strcmp(key, "gappiv") == 0) {
@ -2740,6 +2853,91 @@ bool parse_option(Config *config, char *key, char *value) {
config->gesture_bindings_count++; 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) { } else if (strncmp(key, "source-optional", 15) == 0) {
parse_config_file(config, value, false); parse_config_file(config, value, false);
} else if (strncmp(key, "source", 6) == 0) { } else if (strncmp(key, "source", 6) == 0) {
@ -3036,6 +3234,26 @@ void free_config(void) {
config.gesture_bindings_count = 0; config.gesture_bindings_count = 0;
} }
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 // 释放 tag_rules
if (config.tag_rules) { if (config.tag_rules) {
for (int32_t i = 0; i < config.tag_rules_count; i++) { for (int32_t i = 0; i < config.tag_rules_count; i++) {
@ -3265,6 +3483,19 @@ void override_config(void) {
config.button_map = CLAMP_INT(config.button_map, 0, 1); config.button_map = CLAMP_INT(config.button_map, 0, 1);
config.axis_scroll_factor = config.axis_scroll_factor =
CLAMP_FLOAT(config.axis_scroll_factor, 0.1f, 10.0f); 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.gappih = CLAMP_INT(config.gappih, 0, 1000);
config.gappiv = CLAMP_INT(config.gappiv, 0, 1000); config.gappiv = CLAMP_INT(config.gappiv, 0, 1000);
config.gappoh = CLAMP_INT(config.gappoh, 0, 1000); config.gappoh = CLAMP_INT(config.gappoh, 0, 1000);
@ -3357,6 +3588,13 @@ void set_value_default() {
config.scratchpad_cross_monitor = 0; config.scratchpad_cross_monitor = 0;
config.focus_cross_tag = 0; config.focus_cross_tag = 0;
config.axis_scroll_factor = 1.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.view_current_to_back = 0;
config.single_scratchpad = 1; config.single_scratchpad = 1;
config.xwayland_persistence = 1; config.xwayland_persistence = 1;
@ -3554,6 +3792,8 @@ bool parse_config(void) {
config.switch_bindings_count = 0; config.switch_bindings_count = 0;
config.gesture_bindings = NULL; config.gesture_bindings = NULL;
config.gesture_bindings_count = 0; config.gesture_bindings_count = 0;
config.touch_gesture_bindings = NULL;
config.touch_gesture_bindings_count = 0;
config.env = NULL; config.env = NULL;
config.env_count = 0; config.env_count = 0;
config.exec = NULL; config.exec = NULL;

View file

@ -72,6 +72,7 @@ int32_t enable_monitor(const Arg *arg);
int32_t toggle_monitor(const Arg *arg); int32_t toggle_monitor(const Arg *arg);
int32_t scroller_stack(const Arg *arg); int32_t scroller_stack(const Arg *arg);
int32_t canvas_zoom_resize(const Arg *arg); int32_t canvas_zoom_resize(const Arg *arg);
int32_t canvas_pan(const Arg *arg);
int32_t canvas_overview_toggle(const Arg *arg); int32_t canvas_overview_toggle(const Arg *arg);
int32_t canvas_fill_viewport(const Arg *arg); int32_t canvas_fill_viewport(const Arg *arg);
int32_t canvas_centerview(const Arg *arg); int32_t canvas_centerview(const Arg *arg);

View file

@ -1957,6 +1957,24 @@ int32_t canvas_zoom_resize(const Arg *arg) {
return 0; return 0;
} }
int32_t canvas_pan(const Arg *arg) {
if (!selmon || !is_canvas_layout(selmon))
return 0;
Client *fs = focustop(selmon);
if (fs && fs->isfullscreen)
return 0;
uint32_t tag = selmon->pertag->curtag;
float zoom = selmon->pertag->canvas_zoom[tag];
selmon->pertag->canvas_pan_x[tag] -= arg->f / zoom;
selmon->pertag->canvas_pan_y[tag] -= arg->f2 / zoom;
canvas_reposition(selmon);
return 0;
}
int32_t canvas_overview_toggle(const Arg *arg) { int32_t canvas_overview_toggle(const Arg *arg) {
if (!selmon || !is_canvas_layout(selmon)) if (!selmon || !is_canvas_layout(selmon))
return 0; return 0;

233
src/dispatch/gesture.h Normal file
View file

@ -0,0 +1,233 @@
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;
}
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->distance == DISTANCE_ANY) &&
(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->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++;
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;
}

View file

@ -75,6 +75,7 @@
#include <wlr/types/wlr_tablet_pad.h> #include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_tablet_tool.h> #include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_tablet_v2.h> #include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_viewporter.h> #include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h> #include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h> #include <wlr/types/wlr_virtual_pointer_v1.h>
@ -156,6 +157,32 @@ enum { SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT };
enum { CurNormal, CurPressed, CurMove, CurResize, CurPan }; /* cursor */ enum { CurNormal, CurPressed, CurMove, CurResize, CurPan }; /* cursor */
enum { XDGShell, LayerShell, X11 }; /* client types */ enum { XDGShell, LayerShell, X11 }; /* client types */
enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向
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 { enum {
LyrBg, LyrBg,
LyrBlur, LyrBlur,
@ -460,6 +487,23 @@ typedef struct {
struct wl_listener destroy; struct wl_listener destroy;
} KeyboardGroup; } KeyboardGroup;
typedef struct {
struct wl_list link;
int32_t touch_id;
double start_x, start_y, end_x, end_y, start_surface_x, start_surface_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;
typedef struct { typedef struct {
struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor; struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor;
struct wl_listener destroy; struct wl_listener destroy;
@ -624,6 +668,7 @@ static void createpointerconstraint(struct wl_listener *listener, void *data);
static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
static void commitpopup(struct wl_listener *listener, void *data); static void commitpopup(struct wl_listener *listener, void *data);
static void createpopup(struct wl_listener *listener, void *data); static void createpopup(struct wl_listener *listener, void *data);
static void createtouch(struct wlr_touch *touch);
static void cursorframe(struct wl_listener *listener, void *data); static void cursorframe(struct wl_listener *listener, void *data);
static void cursorwarptohint(void); static void cursorwarptohint(void);
static void destroydecoration(struct wl_listener *listener, void *data); static void destroydecoration(struct wl_listener *listener, void *data);
@ -700,6 +745,13 @@ static void setsel(struct wl_listener *listener, void *data);
static void setup(void); static void setup(void);
static void startdrag(struct wl_listener *listener, void *data); static void startdrag(struct wl_listener *listener, void *data);
static void touchdown(struct wl_listener *listener, void *data);
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 unlocksession(struct wl_listener *listener, void *data);
static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
static void unmapnotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data);
@ -913,6 +965,10 @@ static struct wlr_output_layout *output_layout;
static struct wlr_box sgeom; static struct wlr_box sgeom;
static struct wl_list mons; static struct wl_list mons;
static Monitor *selmon; static Monitor *selmon;
static struct wl_list touch_groups;
static bool emulating_pointer_from_touch = false;
static int32_t emulated_pointer_touch_id;
static int32_t enablegaps = 1; /* enables gaps, used by togglegaps */ static int32_t enablegaps = 1; /* enables gaps, used by togglegaps */
static int32_t axis_apply_time = 0; static int32_t axis_apply_time = 0;
@ -1019,6 +1075,11 @@ static struct wl_listener request_set_sel = {.notify = setsel};
static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
static struct wl_listener request_start_drag = {.notify = requeststartdrag}; static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_listener start_drag = {.notify = startdrag}; static struct wl_listener start_drag = {.notify = startdrag};
static struct wl_listener touch_down = {.notify = touchdown};
static struct wl_listener touch_frame = {.notify = touchframe};
static struct wl_listener touch_motion = {.notify = touchmotion};
static struct wl_listener touch_up = {.notify = touchup};
static struct wl_listener touch_cancel = {.notify = touchcancel};
static struct wl_listener new_session_lock = {.notify = locksession}; static struct wl_listener new_session_lock = {.notify = locksession};
static struct wl_listener drm_lease_request = {.notify = requestdrmlease}; static struct wl_listener drm_lease_request = {.notify = requestdrmlease};
static struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor = { static struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor = {
@ -1050,12 +1111,13 @@ static struct wl_event_source *sync_keymap;
#include "animation/tag.h" #include "animation/tag.h"
static void canvas_reposition(Monitor *m); static void canvas_reposition(Monitor *m);
static void canvas_pan_to_client(Monitor *m, Client *c); static void canvas_pan_to_client(Monitor *m, Client *c);
#include "layout/dwindle.h"
#include "dispatch/bind_define.h" #include "dispatch/bind_define.h"
#include "dispatch/gesture.h"
#include "ext-protocol/all.h" #include "ext-protocol/all.h"
#include "fetch/fetch.h" #include "fetch/fetch.h"
#include "layout/arrange.h" #include "layout/arrange.h"
#include "layout/canvas.h" #include "layout/canvas.h"
#include "layout/dwindle.h"
#include "layout/horizontal.h" #include "layout/horizontal.h"
#include "layout/vertical.h" #include "layout/vertical.h"
@ -2227,7 +2289,11 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) {
struct wlr_surface *old_pointer_focus_surface = struct wlr_surface *old_pointer_focus_surface =
seat->pointer_state.focused_surface; seat->pointer_state.focused_surface;
handlecursoractivity(); if (!event->pointer ||
event->pointer->base.type != WLR_INPUT_DEVICE_TOUCH) {
handlecursoractivity();
}
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
if (event->pointer && check_trackpad_disabled(event->pointer)) { if (event->pointer && check_trackpad_disabled(event->pointer)) {
@ -2501,6 +2567,11 @@ void cleanuplisteners(void) {
wl_list_remove(&request_set_cursor_shape.link); wl_list_remove(&request_set_cursor_shape.link);
wl_list_remove(&request_start_drag.link); wl_list_remove(&request_start_drag.link);
wl_list_remove(&start_drag.link); wl_list_remove(&start_drag.link);
wl_list_remove(&touch_down.link);
wl_list_remove(&touch_frame.link);
wl_list_remove(&touch_motion.link);
wl_list_remove(&touch_up.link);
wl_list_remove(&touch_cancel.link);
wl_list_remove(&new_session_lock.link); wl_list_remove(&new_session_lock.link);
wl_list_remove(&tearing_new_object.link); wl_list_remove(&tearing_new_object.link);
wl_list_remove(&keyboard_shortcuts_inhibit_new_inhibitor.link); wl_list_remove(&keyboard_shortcuts_inhibit_new_inhibitor.link);
@ -3596,6 +3667,16 @@ void createpointerconstraint(struct wl_listener *listener, void *data) {
&pointer_constraint->destroy, destroypointerconstraint); &pointer_constraint->destroy, destroypointerconstraint);
} }
void createtouch(struct wlr_touch *wlr_touch) {
TouchGroup *touch = ecalloc(1, sizeof(TouchGroup));
touch->touch = wlr_touch;
wl_list_init(&touch->touch_points);
wl_list_insert(&touch_groups, &touch->link);
wlr_touch->data = touch;
wlr_cursor_attach_input_device(cursor, &wlr_touch->base);
}
void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) { void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) {
if (active_constraint == constraint) if (active_constraint == constraint)
return; return;
@ -3962,6 +4043,9 @@ void inputdevice(struct wl_listener *listener, void *data) {
case WLR_INPUT_DEVICE_SWITCH: case WLR_INPUT_DEVICE_SWITCH:
createswitch(wlr_switch_from_input_device(device)); createswitch(wlr_switch_from_input_device(device));
break; break;
case WLR_INPUT_DEVICE_TOUCH:
createtouch(wlr_touch_from_input_device(device));
break;
default: default:
/* TODO handle other input device types */ /* TODO handle other input device types */
break; break;
@ -3975,6 +4059,8 @@ void inputdevice(struct wl_listener *listener, void *data) {
caps = WL_SEAT_CAPABILITY_POINTER; caps = WL_SEAT_CAPABILITY_POINTER;
if (!wl_list_empty(&kb_group->wlr_group->devices)) if (!wl_list_empty(&kb_group->wlr_group->devices))
caps |= WL_SEAT_CAPABILITY_KEYBOARD; caps |= WL_SEAT_CAPABILITY_KEYBOARD;
if (!wl_list_empty(&touch_groups))
caps |= WL_SEAT_CAPABILITY_TOUCH;
wlr_seat_set_capabilities(seat, caps); wlr_seat_set_capabilities(seat, caps);
} }
@ -4721,7 +4807,9 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
} }
wlr_cursor_move(cursor, device, dx, dy); wlr_cursor_move(cursor, device, dx, dy);
handlecursoractivity(); if (!device || device->type != WLR_INPUT_DEVICE_TOUCH) {
handlecursoractivity();
}
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
/* Update selmon (even while dragging a window) */ /* Update selmon (even while dragging a window) */
@ -6666,6 +6754,14 @@ void setup(void) {
wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button); wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button);
wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip); wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip);
wl_list_init(&touch_groups);
wl_signal_add(&cursor->events.touch_down, &touch_down);
wl_signal_add(&cursor->events.touch_frame, &touch_frame);
wl_signal_add(&cursor->events.touch_motion, &touch_motion);
wl_signal_add(&cursor->events.touch_up, &touch_up);
wl_signal_add(&cursor->events.touch_cancel, &touch_cancel);
// 这两句代码会造成obs窗口里的鼠标光标消失,不知道注释有什么影响 // 这两句代码会造成obs窗口里的鼠标光标消失,不知道注释有什么影响
cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
wl_signal_add(&cursor_shape_mgr->events.request_set_shape, wl_signal_add(&cursor_shape_mgr->events.request_set_shape,
@ -6782,6 +6878,256 @@ void startdrag(struct wl_listener *listener, void *data) {
LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
} }
void touchdown(struct wl_listener *listener, void *data) {
struct wlr_touch_down_event *event = data;
TouchGroup *tg = event->touch->data;
TouchPoint *t = ecalloc(1, sizeof(TouchPoint));
double lx, ly;
double sx, sy;
double dx, dy;
struct wlr_surface *surface;
Client *c = NULL;
Monitor *m = NULL;
Monitor *m_iter;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
if (!cursor_hidden) {
hidecursor(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 &&
0 != strcmp(event->touch->output_name, m->wlr_output->name)) {
continue;
}
wlr_cursor_map_input_to_output(cursor, &event->touch->base,
m_iter->wlr_output);
m = m_iter;
break;
}
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;
t->start_x = lx;
t->start_y = ly;
gesture_touch_down(tg, t, lx, ly);
wl_list_insert(&tg->touch_points, &t->link);
xytonode(lx, ly, &surface, &c, NULL, &sx, &sy);
t->start_surface_x = sx;
t->start_surface_y = sy;
if (surface != NULL && wlr_surface_accepts_touch(surface, seat)) {
if (c)
focusclient(c, 0);
wlr_seat_touch_notify_down(seat, surface, event->time_msec,
event->touch_id, sx, sy);
emulating_pointer_from_touch = false;
return;
}
if (!emulating_pointer_from_touch) {
emulating_pointer_from_touch = true;
emulated_pointer_touch_id = event->touch_id;
wlr_cursor_warp_closest(cursor, &event->touch->base, lx, ly);
dx = lx - cursor->x;
dy = ly - cursor->y;
motionnotify(event->time_msec, &event->touch->base, dx, dy, dx, dy);
struct wlr_pointer_button_event button_event = {
.pointer = (struct wlr_pointer *)event->touch,
.time_msec = event->time_msec,
.button = BTN_LEFT,
.state = WL_POINTER_BUTTON_STATE_PRESSED};
buttonpress(NULL, &button_event);
}
}
void touchup(struct wl_listener *listener, void *data) {
struct wlr_touch_up_event *event = data;
TouchGroup *tg = event->touch->data;
TouchPoint *t = NULL;
TouchPoint *t_iter;
wl_list_for_each(t_iter, &tg->touch_points, link) {
if (t_iter->touch_id == event->touch_id) {
t = t_iter;
break;
}
}
if (!t)
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);
if (emulating_pointer_from_touch) {
if (emulated_pointer_touch_id == event->touch_id) {
struct wlr_pointer_button_event button_event = {
.pointer = (struct wlr_pointer *)event->touch,
.time_msec = event->time_msec,
.button = BTN_LEFT,
.state = WL_POINTER_BUTTON_STATE_RELEASED};
buttonpress(NULL, &button_event);
emulating_pointer_from_touch = false;
}
return;
}
if (!wlr_seat_touch_get_point(seat, event->touch_id)) {
return;
}
wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id);
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
}
void touchframe(struct wl_listener *listener, void *data) {
if (emulating_pointer_from_touch) {
wlr_seat_pointer_notify_frame(seat);
} else {
wlr_seat_touch_notify_frame(seat);
}
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
}
void touchmotion(struct wl_listener *listener, void *data) {
struct wlr_touch_motion_event *event = data;
TouchGroup *tg = event->touch->data;
TouchPoint *t = NULL;
TouchPoint *t_iter;
double lx, ly;
double sx, sy;
double dx, dy;
struct wlr_surface *surface;
Client *c = NULL;
struct wlr_touch_point *p = NULL;
wl_list_for_each(t_iter, &tg->touch_points, link) {
if (t_iter->touch_id == event->touch_id) {
t = t_iter;
break;
}
}
if (!t)
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) {
dx = lx - cursor->x;
dy = ly - cursor->y;
motionnotify(event->time_msec, &event->touch->base, dx, dy, dx, dy);
}
return;
}
p = wlr_seat_touch_get_point(seat, event->touch_id);
if (!p) {
return;
}
sx = t->start_surface_x + (lx - t->start_x);
sy = t->start_surface_y + (ly - t->start_y);
surface = p->surface;
if (surface && surface->data) {
toplevel_from_wlr_surface(surface, &c, NULL);
if (c)
focusclient(c, 0);
}
wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, sx,
sy);
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
}
void touchcancel(struct wl_listener *listener, void *data) {
struct wlr_touch_cancel_event *event = data;
TouchGroup *tg = event->touch->data;
TouchPoint *t = NULL;
TouchPoint *t_iter;
wl_list_for_each(t_iter, &tg->touch_points, link) {
if (t_iter->touch_id == event->touch_id) {
t = t_iter;
break;
}
}
if (!t)
return;
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 = {
.pointer = (struct wlr_pointer *)event->touch,
.time_msec = event->time_msec,
.button = BTN_LEFT,
.state = WL_POINTER_BUTTON_STATE_RELEASED};
buttonpress(NULL, &button_event);
emulating_pointer_from_touch = false;
}
return;
}
p = wlr_seat_touch_get_point(seat, event->touch_id);
if (!p) {
return;
}
if (p->surface) {
client = wl_resource_get_client(p->surface->resource);
seat_client = wlr_seat_client_for_wl_client(seat, client);
if (seat_client != NULL) {
wlr_seat_touch_notify_cancel(seat, seat_client);
}
}
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
}
void tag_client(const Arg *arg, Client *target_client) { void tag_client(const Arg *arg, Client *target_client) {
Client *fc = NULL; Client *fc = NULL;
if (target_client && arg->ui & TAGMASK) { if (target_client && arg->ui & TAGMASK) {