diff --git a/config.conf b/config.conf index 260f930..70591e9 100644 --- a/config.conf +++ b/config.conf @@ -58,6 +58,7 @@ natural_scrolling=0 disable_while_typing=1 left_handed=0 middle_button_emulation=0 +swipe_min_threshold=20 # Appearance gappih=5 @@ -282,3 +283,14 @@ mousebind=NONE,btn_right,killclient,0 # Axis Bindings axisbind=SUPER,UP,viewtoleft_have_client axisbind=SUPER,DOWN,viewtoright_have_client + +# Gesture bind +gesturebind=none,left,3,focusdir,left +gesturebind=none,right,3,focusdir,right +gesturebind=none,up,3,focusdir,up +gesturebind=none,down,3,focusdir,down +gesturebind=none,left,4,viewtoleft_have_client +gesturebind=none,right,4,viewtoright_have_client +gesturebind=none,up,4,toggleoverview +gesturebind=none,down,4,toggleoverview + diff --git a/maomao.c b/maomao.c index bcf40d1..df6eea4 100644 --- a/maomao.c +++ b/maomao.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,7 @@ /* enums */ /* enums */ +enum { SWIPE_UP,SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT }; enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ enum { XDGShell, LayerShell, X11 }; /* client types */ enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 @@ -394,6 +396,15 @@ static void axisnotify(struct wl_listener *listener, void *data); // 滚轮事件处理 static void buttonpress(struct wl_listener *listener, void *data); // 鼠标按键事件处理 +static int ongesture(struct wlr_pointer_swipe_end_event *event); +static void swipe_begin(struct wl_listener *listener, void *data); +static void swipe_update(struct wl_listener *listener, void *data); +static void swipe_end(struct wl_listener *listener, void *data); +static void pinch_begin(struct wl_listener *listener, void *data); +static void pinch_update(struct wl_listener *listener, void *data); +static void pinch_end(struct wl_listener *listener, void *data); +static void hold_begin(struct wl_listener *listener, void *data); +static void hold_end(struct wl_listener *listener, void *data); static void checkidleinhibitor(struct wlr_surface *exclude); static void cleanup(void); // 退出清理 static void cleanupkeyboard(struct wl_listener *listener, @@ -620,6 +631,7 @@ static struct wlr_output_manager_v1 *output_mgr; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; static struct wlr_output_power_manager_v1 *power_mgr; +static struct wlr_pointer_gestures_v1 *pointer_gestures; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -652,6 +664,10 @@ static int axis_apply_time = 0; static int axis_apply_dir = 0; static int scroller_focus_lock = 0; +static uint32_t swipe_fingers = 0; +static double swipe_dx = 0; +static double swipe_dy = 0; + /* global event handlers */ static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = { .release = dwl_ipc_manager_release, @@ -688,7 +704,6 @@ static Atom netatom[NetLast]; #include "IM.h" #endif -/* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; @@ -1981,6 +1996,174 @@ axisnotify(struct wl_listener *listener, void *data) { event->source); } + +int +ongesture(struct wlr_pointer_swipe_end_event *event) +{ + struct wlr_keyboard *keyboard; + uint32_t mods; + const GestureBinding *g; + unsigned int motion; + unsigned int adx = (int)round(fabs(swipe_dx)); + unsigned int ady = (int)round(fabs(swipe_dy)); + int handled = 0; + int ji; + + if (event->cancelled) { + return handled; + } + + // Require absolute distance movement beyond a small thresh-hold + if (adx * adx + ady * ady < swipe_min_threshold * swipe_min_threshold) { + return handled; + } + + if (adx > ady) { + motion = swipe_dx < 0 ? SWIPE_LEFT : SWIPE_RIGHT; + } else { + motion = swipe_dy < 0 ? SWIPE_UP : SWIPE_DOWN; + } + + keyboard = wlr_seat_get_keyboard(seat); + mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + + for (ji = 0; ji < config.gesture_bindings_count; ji++) { + if (config.gesture_bindings_count < 1) + break; + g = &config.gesture_bindings[ji]; + if (CLEANMASK(mods) == CLEANMASK(g->mod) && + swipe_fingers == g->fingers_count && + motion == g->motion && g->func) { + g->func(&g->arg); + handled = 1; + } + } + return handled; +} + +void +swipe_begin(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_swipe_begin_event *event = data; + + // Forward swipe begin event to client + wlr_pointer_gestures_v1_send_swipe_begin( + pointer_gestures, + seat, + event->time_msec, + event->fingers + ); +} + +void +swipe_update(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_swipe_update_event *event = data; + + swipe_fingers = event->fingers; + // Accumulate swipe distance + swipe_dx += event->dx; + swipe_dy += event->dy; + + // Forward swipe update event to client + wlr_pointer_gestures_v1_send_swipe_update( + pointer_gestures, + seat, + event->time_msec, + event->dx, + event->dy + ); +} + +void +swipe_end(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_swipe_end_event *event = data; + ongesture(event); + swipe_dx = 0; + swipe_dy = 0; + // Forward swipe end event to client + wlr_pointer_gestures_v1_send_swipe_end( + pointer_gestures, + seat, + event->time_msec, + event->cancelled + ); +} + +void +pinch_begin(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_pinch_begin_event *event = data; + + // Forward pinch begin event to client + wlr_pointer_gestures_v1_send_pinch_begin( + pointer_gestures, + seat, + event->time_msec, + event->fingers + ); +} + +void +pinch_update(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_pinch_update_event *event = data; + + // Forward pinch update event to client + wlr_pointer_gestures_v1_send_pinch_update( + pointer_gestures, + seat, + event->time_msec, + event->dx, + event->dy, + event->scale, + event->rotation + ); +} + +void +pinch_end(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_pinch_end_event *event = data; + + // Forward pinch end event to client + wlr_pointer_gestures_v1_send_pinch_end( + pointer_gestures, + seat, + event->time_msec, + event->cancelled + ); +} + +void +hold_begin(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_hold_begin_event *event = data; + + // Forward hold begin event to client + wlr_pointer_gestures_v1_send_hold_begin( + pointer_gestures, + seat, + event->time_msec, + event->fingers + ); +} + +void +hold_end(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_hold_end_event *event = data; + + // Forward hold end event to client + wlr_pointer_gestures_v1_send_hold_end( + pointer_gestures, + seat, + event->time_msec, + event->cancelled + ); +} + void // 鼠标按键事件 buttonpress(struct wl_listener *listener, void *data) { struct wlr_pointer_button_event *event = data; @@ -5282,6 +5465,16 @@ void setup(void) { LISTEN_STATIC(&virtual_pointer_mgr->events.new_virtual_pointer, virtualpointer); + pointer_gestures = wlr_pointer_gestures_v1_create(dpy); + LISTEN_STATIC(&cursor->events.swipe_begin, swipe_begin); + LISTEN_STATIC(&cursor->events.swipe_update, swipe_update); + LISTEN_STATIC(&cursor->events.swipe_end, swipe_end); + LISTEN_STATIC(&cursor->events.pinch_begin, pinch_begin); + LISTEN_STATIC(&cursor->events.pinch_update, pinch_update); + LISTEN_STATIC(&cursor->events.pinch_end, pinch_end); + LISTEN_STATIC(&cursor->events.hold_begin, hold_begin); + LISTEN_STATIC(&cursor->events.hold_end, hold_end); + seat = wlr_seat_create(dpy, "seat0"); LISTEN_STATIC(&seat->events.request_set_cursor, setcursor); LISTEN_STATIC(&seat->events.request_set_selection, setsel); diff --git a/parse_config.h b/parse_config.h index 50bfa25..9ed3e70 100644 --- a/parse_config.h +++ b/parse_config.h @@ -73,6 +73,13 @@ typedef struct { Arg arg; } AxisBinding; +typedef struct { + unsigned int mod; + unsigned int motion; + unsigned int fingers_count; + void (*func)(const Arg *); + Arg arg; +} GestureBinding; typedef struct { int animations; char animation_type_open[10]; @@ -95,6 +102,7 @@ typedef struct { int scroller_focus_center; int scroller_prefer_center; int focus_cross_monitor; + unsigned int swipe_min_threshold; float *scroller_proportion_preset; int scroller_proportion_preset_count; @@ -168,6 +176,9 @@ typedef struct { AxisBinding *axis_bindings; int axis_bindings_count; + GestureBinding *gesture_bindings; + int gesture_bindings_count; + } Config; typedef void (*FuncType)(const Arg *); @@ -558,6 +569,8 @@ void parse_config_line(Config *config, const char *line) { config->scroller_prefer_center = atoi(value); } else if (strcmp(key, "focus_cross_monitor") == 0) { config->focus_cross_monitor = atoi(value); + } else if (strcmp(key, "swipe_min_threshold") == 0) { + config->swipe_min_threshold = atoi(value); } else if (strcmp(key, "scroller_proportion_preset") == 0) { // 1. 统计 value 中有多少个逗号,确定需要解析的浮点数个数 int count = 0; // 初始化为 0 @@ -1016,6 +1029,37 @@ void parse_config_line(Config *config, const char *line) { config->axis_bindings_count++; } + } else if (strncmp(key, "gesturebind", 8) == 0) { + config->gesture_bindings = + realloc(config->gesture_bindings, + (config->gesture_bindings_count + 1) * sizeof(GestureBinding)); + if (!config->gesture_bindings) { + fprintf(stderr, "Error: Failed to allocate memory for axis gesturebind\n"); + return; + } + + GestureBinding *binding = &config->gesture_bindings[config->gesture_bindings_count]; + memset(binding, 0, sizeof(GestureBinding)); + + char mod_str[256], motion_str[256], fingers_count_str[256] , func_name[256], arg_value[256] = "none"; + if (sscanf(value, "%[^,],%[^,],%[^,],%[^,],%[^\n]", mod_str, motion_str, fingers_count_str, func_name, + arg_value) < 4) { + fprintf(stderr, "Error: Invalid gesturebind format: %s\n", value); + return; + } + + binding->mod = parse_mod(mod_str); + binding->motion = parse_direction(motion_str); + binding->fingers_count = atoi(fingers_count_str); + binding->arg.v = NULL; + binding->func = parse_func_name(func_name, &binding->arg, arg_value); + + if (!binding->func) { + fprintf(stderr, "Error: Unknown function in axisbind: %s\n", func_name); + } else { + config->gesture_bindings_count++; + } + } else { fprintf(stderr, "Error: Unknown key: %s\n", key); } @@ -1103,6 +1147,14 @@ void free_config(void) { } free(config.axis_bindings); + for (i = 0; i < config.gesture_bindings_count; i++) { + if (config.gesture_bindings[i].arg.v) { + free((void *)config.gesture_bindings[i].arg.v); + config.gesture_bindings[i].arg.v = NULL; // 避免重复释放 + } + } + free(config.gesture_bindings); + free(config.scroller_proportion_preset); config.scroller_proportion_preset = NULL; config.scroller_proportion_preset_count = 0; @@ -1137,6 +1189,7 @@ void override_config(void) { scroller_default_proportion = config.scroller_default_proportion; scroller_focus_center = config.scroller_focus_center; focus_cross_monitor = config.focus_cross_monitor; + swipe_min_threshold = config.swipe_min_threshold; scroller_prefer_center = config.scroller_prefer_center; new_is_master = config.new_is_master; @@ -1218,6 +1271,7 @@ void set_value_default() { config.scroller_focus_center = scroller_focus_center; config.scroller_prefer_center = scroller_prefer_center; config.focus_cross_monitor = focus_cross_monitor; + config.swipe_min_threshold = swipe_min_threshold; config.bypass_surface_visibility = bypass_surface_visibility; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ @@ -1273,6 +1327,8 @@ void parse_config(void) { config.mouse_bindings_count = 0; config.axis_bindings = NULL; config.axis_bindings_count = 0; + config.gesture_bindings = NULL; + config.gesture_bindings_count = 0; // 获取 MAOMAOCONFIG 环境变量 const char *maomaoconfig = getenv("MAOMAOCONFIG"); diff --git a/preset_config.h b/preset_config.h index 12ea51d..c35f860 100644 --- a/preset_config.h +++ b/preset_config.h @@ -49,6 +49,9 @@ int scroller_focus_center = 0; int scroller_prefer_center = 0; int focus_cross_monitor = 0; +unsigned int swipe_min_threshold = 20; + + int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */