diff --git a/meson.build b/meson.build index 0565609..7c1b507 100644 --- a/meson.build +++ b/meson.build @@ -96,7 +96,16 @@ endif executable('mango', 'src/mango.c', + 'src/animation/client.c', + 'src/animation/common.c', + 'src/animation/layer.c', + 'src/animation/tag.c', + 'src/client/client.c', + 'src/layout/arrange.c', + 'src/layout/layout.c', 'src/common/util.c', + 'src/config/parse_config.c', + 'src/dispatch/bind_define.c', 'src/ext-protocol/wlr_ext_workspace_v1.c', wayland_sources, dependencies : [ diff --git a/mmsg/mmsg.c b/mmsg/mmsg.c index ba073aa..c496525 100644 --- a/mmsg/mmsg.c +++ b/mmsg/mmsg.c @@ -18,71 +18,71 @@ char *argv0; -static enum { +enum { NONE = 0, SET = 1 << 0, GET = 1 << 1, WATCH = 1 << 2 | GET, } mode = NONE; -static int32_t Oflag; -static int32_t Tflag; -static int32_t Lflag; -static int32_t oflag; -static int32_t tflag; -static int32_t lflag; -static int32_t cflag; -static int32_t vflag; -static int32_t mflag; -static int32_t fflag; -static int32_t qflag; -static int32_t dflag; -static int32_t xflag; -static int32_t eflag; -static int32_t kflag; -static int32_t bflag; -static int32_t Aflag; +int32_t Oflag; +int32_t Tflag; +int32_t Lflag; +int32_t oflag; +int32_t tflag; +int32_t lflag; +int32_t cflag; +int32_t vflag; +int32_t mflag; +int32_t fflag; +int32_t qflag; +int32_t dflag; +int32_t xflag; +int32_t eflag; +int32_t kflag; +int32_t bflag; +int32_t Aflag; -static uint32_t occ, seltags, total_clients, urg; +uint32_t occ, seltags, total_clients, urg; -static char *output_name; -static int32_t tagcount; -static char *tagset; -static char *layout_name; -static int32_t layoutcount, layout_idx; -static char *client_tags; -static char *dispatch_cmd; -static char *dispatch_arg1; -static char *dispatch_arg2; -static char *dispatch_arg3; -static char *dispatch_arg4; -static char *dispatch_arg5; +char *output_name; +int32_t tagcount; +char *tagset; +char *layout_name; +int32_t layoutcount, layout_idx; +char *client_tags; +char *dispatch_cmd; +char *dispatch_arg1; +char *dispatch_arg2; +char *dispatch_arg3; +char *dispatch_arg4; +char *dispatch_arg5; struct output { char *output_name; uint32_t name; }; -static DYNARR_DEF(struct output) outputs; +DYNARR_DEF(struct output) outputs; -static struct wl_display *display; -static struct zdwl_ipc_manager_v2 *dwl_ipc_manager; +struct wl_display *display; +struct zdwl_ipc_manager_v2 *dwl_ipc_manager; // 为每个回调定义专用的空函数 -static void noop_geometry(void *data, struct wl_output *wl_output, int32_t x, +void noop_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform) {} -static void noop_mode(void *data, struct wl_output *wl_output, uint32_t flags, +void noop_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {} -static void noop_done(void *data, struct wl_output *wl_output) {} +void noop_done(void *data, struct wl_output *wl_output) {} -static void noop_scale(void *data, struct wl_output *wl_output, +void noop_scale(void *data, struct wl_output *wl_output, int32_t factor) {} -static void noop_description(void *data, struct wl_output *wl_output, +void noop_description(void *data, struct wl_output *wl_output, const char *description) {} // 将 n 转换为 9 位二进制字符串,结果存入 buf(至少长度 10) @@ -93,7 +93,7 @@ void bin_str_9bits(char *buf, uint32_t n) { *buf = '\0'; // 字符串结尾 } -static void dwl_ipc_tags(void *data, +void dwl_ipc_tags(void *data, struct zdwl_ipc_manager_v2 *dwl_ipc_manager, uint32_t count) { tagcount = count; @@ -101,7 +101,7 @@ static void dwl_ipc_tags(void *data, printf("%d\n", tagcount); } -static void dwl_ipc_layout(void *data, +void dwl_ipc_layout(void *data, struct zdwl_ipc_manager_v2 *dwl_ipc_manager, const char *name) { if (lflag && mode & SET && strcmp(layout_name, name) == 0) @@ -111,10 +111,10 @@ static void dwl_ipc_layout(void *data, layoutcount++; } -static const struct zdwl_ipc_manager_v2_listener dwl_ipc_listener = { +const struct zdwl_ipc_manager_v2_listener dwl_ipc_listener = { .tags = dwl_ipc_tags, .layout = dwl_ipc_layout}; -static void +void dwl_ipc_output_toggle_visibility(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output) { if (!vflag) @@ -125,7 +125,7 @@ dwl_ipc_output_toggle_visibility(void *data, printf("toggle\n"); } -static void dwl_ipc_output_active(void *data, +void dwl_ipc_output_active(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, uint32_t active) { if (!oflag) { @@ -139,7 +139,7 @@ static void dwl_ipc_output_active(void *data, printf("selmon %u\n", active ? 1 : 0); } -static void dwl_ipc_output_tag(void *data, +void dwl_ipc_output_tag(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) { @@ -163,11 +163,11 @@ static void dwl_ipc_output_tag(void *data, printf("tag %u %u %u %u\n", tag + 1, state, clients, focused); } -static void dwl_ipc_output_layout(void *data, +void dwl_ipc_output_layout(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, uint32_t layout) {} -static void dwl_ipc_output_layout_symbol( +void dwl_ipc_output_layout_symbol( void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *layout) { if (!(lflag && mode & GET)) return; @@ -177,7 +177,7 @@ static void dwl_ipc_output_layout_symbol( printf("layout %s\n", layout); } -static void dwl_ipc_output_title(void *data, +void dwl_ipc_output_title(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *title) { if (!(cflag && mode & GET)) @@ -188,7 +188,7 @@ static void dwl_ipc_output_title(void *data, printf("title %s\n", title); } -static void dwl_ipc_output_appid(void *data, +void dwl_ipc_output_appid(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *appid) { if (!(cflag && mode & GET)) @@ -199,7 +199,7 @@ static void dwl_ipc_output_appid(void *data, printf("appid %s\n", appid); } -static void dwl_ipc_output_x(void *data, +void dwl_ipc_output_x(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, int32_t x) { if (!xflag) @@ -210,7 +210,7 @@ static void dwl_ipc_output_x(void *data, printf("x %d\n", x); } -static void dwl_ipc_output_y(void *data, +void dwl_ipc_output_y(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, int32_t y) { if (!xflag) @@ -221,7 +221,7 @@ static void dwl_ipc_output_y(void *data, printf("y %d\n", y); } -static void dwl_ipc_output_width(void *data, +void dwl_ipc_output_width(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, int32_t width) { if (!xflag) @@ -232,7 +232,7 @@ static void dwl_ipc_output_width(void *data, printf("width %d\n", width); } -static void dwl_ipc_output_height(void *data, +void dwl_ipc_output_height(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, int32_t height) { if (!xflag) @@ -243,7 +243,7 @@ static void dwl_ipc_output_height(void *data, printf("height %d\n", height); } -static void dwl_ipc_output_last_layer(void *data, +void dwl_ipc_output_last_layer(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *last_layer) { if (!eflag) @@ -254,7 +254,7 @@ static void dwl_ipc_output_last_layer(void *data, printf("last_layer %s\n", last_layer); } -static void dwl_ipc_output_kb_layout(void *data, +void dwl_ipc_output_kb_layout(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *kb_layout) { if (!kflag) @@ -265,7 +265,7 @@ static void dwl_ipc_output_kb_layout(void *data, printf("kb_layout %s\n", kb_layout); } -static void +void dwl_ipc_output_scalefactor(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const uint32_t scalefactor) { @@ -277,7 +277,7 @@ dwl_ipc_output_scalefactor(void *data, printf("scale_factor %f\n", scalefactor / 100.0f); } -static void dwl_ipc_output_keymode(void *data, +void dwl_ipc_output_keymode(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *keymode) { if (!bflag) @@ -288,7 +288,7 @@ static void dwl_ipc_output_keymode(void *data, printf("keymode %s\n", keymode); } -static void dwl_ipc_output_fullscreen(void *data, +void dwl_ipc_output_fullscreen(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, uint32_t is_fullscreen) { if (!mflag) @@ -299,7 +299,7 @@ static void dwl_ipc_output_fullscreen(void *data, printf("fullscreen %u\n", is_fullscreen); } -static void dwl_ipc_output_floating(void *data, +void dwl_ipc_output_floating(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, uint32_t is_floating) { if (!fflag) @@ -310,7 +310,7 @@ static void dwl_ipc_output_floating(void *data, printf("floating %u\n", is_floating); } -static void dwl_ipc_output_frame(void *data, +void dwl_ipc_output_frame(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output) { if (mode & SET) { if (data && (!output_name || strcmp(output_name, (char *)data))) @@ -409,7 +409,7 @@ static void dwl_ipc_output_frame(void *data, fflush(stdout); } -static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = { +const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = { .toggle_visibility = dwl_ipc_output_toggle_visibility, .active = dwl_ipc_output_active, .tag = dwl_ipc_output_tag, @@ -430,7 +430,7 @@ static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = { .frame = dwl_ipc_output_frame, }; -static void wl_output_name(void *data, struct wl_output *output, +void wl_output_name(void *data, struct wl_output *output, const char *name) { if (outputs.arr) { struct output *o = (struct output *)data; @@ -449,7 +449,7 @@ static void wl_output_name(void *data, struct wl_output *output, output_name ? NULL : strdup(name)); } -static const struct wl_output_listener output_listener = { +const struct wl_output_listener output_listener = { .geometry = noop_geometry, .mode = noop_mode, .done = noop_done, @@ -458,7 +458,7 @@ static const struct wl_output_listener output_listener = { .description = noop_description, }; -static void global_add(void *data, struct wl_registry *wl_registry, +void global_add(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, wl_output_interface.name) == 0) { struct wl_output *o = @@ -479,7 +479,7 @@ static void global_add(void *data, struct wl_registry *wl_registry, } } -static void global_remove(void *data, struct wl_registry *wl_registry, +void global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) { if (!outputs.arr) return; @@ -493,12 +493,12 @@ static void global_remove(void *data, struct wl_registry *wl_registry, } } -static const struct wl_registry_listener registry_listener = { +const struct wl_registry_listener registry_listener = { .global = global_add, .global_remove = global_remove, }; -static void usage(void) { +void usage(void) { fprintf(stderr, "usage:" "\t%s [-OTLq]\n" diff --git a/src/animation/client.c b/src/animation/client.c new file mode 100644 index 0000000..32b6ce8 --- /dev/null +++ b/src/animation/client.c @@ -0,0 +1,1206 @@ +#include "../type.h" +#include "../layout/layout.h" +#include "../client/client.h" + +extern Server server; + +void client_actual_size(Client *c, int32_t *width, int32_t *height) { + *width = c->animation.current.width - 2 * (int32_t)c->bw; + + *height = c->animation.current.height - 2 * (int32_t)c->bw; +} + +void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) { + wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height)); +} + +enum corner_location set_client_corner_location(Client *c) { + enum corner_location current_corner_location = CORNER_LOCATION_ALL; + struct wlr_box target_geom = animations ? c->animation.current : c->geom; + if (target_geom.x + border_radius <= c->mon->m.x) { + current_corner_location &= ~CORNER_LOCATION_LEFT; // 清除左标志位 + } + if (target_geom.x + target_geom.width - border_radius >= + c->mon->m.x + c->mon->m.width) { + current_corner_location &= ~CORNER_LOCATION_RIGHT; // 清除右标志位 + } + if (target_geom.y + border_radius <= c->mon->m.y) { + current_corner_location &= ~CORNER_LOCATION_TOP; // 清除上标志位 + } + if (target_geom.y + target_geom.height - border_radius >= + c->mon->m.y + c->mon->m.height) { + current_corner_location &= ~CORNER_LOCATION_BOTTOM; // 清除下标志位 + } + return current_corner_location; +} + +bool is_horizontal_stack_layout(Monitor *m) { + + if (m->pertag->curtag && + (m->pertag->ltidxs[m->pertag->curtag]->id == TILE || + m->pertag->ltidxs[m->pertag->curtag]->id == DECK)) + return true; + + return false; +} + +bool is_horizontal_right_stack_layout(Monitor *m) { + + if (m->pertag->curtag && + (m->pertag->ltidxs[m->pertag->curtag]->id == RIGHT_TILE)) + return true; + + return false; +} + +int32_t is_special_animaiton_rule(Client *c) { + + if (is_scroller_layout(c->mon) && !c->isfloating) { + return DOWN; + } else if (c->mon->visible_tiling_clients == 1 && !c->isfloating) { + return DOWN; + } else if (c->mon->visible_tiling_clients == 2 && !c->isfloating && + !new_is_master && is_horizontal_stack_layout(c->mon)) { + return RIGHT; + } else if (!c->isfloating && new_is_master && + is_horizontal_stack_layout(c->mon)) { + return LEFT; + } else if (c->mon->visible_tiling_clients == 2 && !c->isfloating && + !new_is_master && is_horizontal_right_stack_layout(c->mon)) { + return LEFT; + } else if (!c->isfloating && new_is_master && + is_horizontal_right_stack_layout(c->mon)) { + return RIGHT; + } else { + return UNDIR; + } +} + +void set_client_open_animaiton(Client *c, struct wlr_box geo) { + int32_t slide_direction; + int32_t horizontal, horizontal_value; + int32_t vertical, vertical_value; + int32_t special_direction; + int32_t center_x, center_y; + + if ((!c->animation_type_open && strcmp(animation_type_open, "fade") == 0) || + (c->animation_type_open && + strcmp(c->animation_type_open, "fade") == 0)) { + c->animainit_geom.width = geo.width; + c->animainit_geom.height = geo.height; + c->animainit_geom.x = geo.x; + c->animainit_geom.y = geo.y; + return; + } else if ((!c->animation_type_open && + strcmp(animation_type_open, "zoom") == 0) || + (c->animation_type_open && + strcmp(c->animation_type_open, "zoom") == 0)) { + c->animainit_geom.width = geo.width * zoom_initial_ratio; + c->animainit_geom.height = geo.height * zoom_initial_ratio; + c->animainit_geom.x = geo.x + (geo.width - c->animainit_geom.width) / 2; + c->animainit_geom.y = + geo.y + (geo.height - c->animainit_geom.height) / 2; + return; + } else { + special_direction = is_special_animaiton_rule(c); + center_x = c->geom.x + c->geom.width / 2; + center_y = c->geom.y + c->geom.height / 2; + if (special_direction == UNDIR) { + horizontal = c->mon->w.x + c->mon->w.width - center_x < + center_x - c->mon->w.x + ? RIGHT + : LEFT; + horizontal_value = horizontal == LEFT + ? center_x - c->mon->w.x + : c->mon->w.x + c->mon->w.width - center_x; + vertical = c->mon->w.y + c->mon->w.height - center_y < + center_y - c->mon->w.y + ? DOWN + : UP; + vertical_value = vertical == UP + ? center_y - c->mon->w.y + : c->mon->w.y + c->mon->w.height - center_y; + slide_direction = + horizontal_value < vertical_value ? horizontal : vertical; + } else { + slide_direction = special_direction; + } + c->animainit_geom.width = c->geom.width; + c->animainit_geom.height = c->geom.height; + switch (slide_direction) { + case UP: + c->animainit_geom.x = c->geom.x; + c->animainit_geom.y = c->mon->m.y - c->geom.height; + break; + case DOWN: + c->animainit_geom.x = c->geom.x; + c->animainit_geom.y = + c->geom.y + c->mon->m.height - (c->geom.y - c->mon->m.y); + break; + case LEFT: + c->animainit_geom.x = c->mon->m.x - c->geom.width; + c->animainit_geom.y = c->geom.y; + break; + case RIGHT: + c->animainit_geom.x = + c->geom.x + c->mon->m.width - (c->geom.x - c->mon->m.x); + c->animainit_geom.y = c->geom.y; + break; + default: + c->animainit_geom.x = c->geom.x; + c->animainit_geom.y = 0 - c->geom.height; + } + } +} + +void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, + int32_t sy, void *data) { + BufferData *buffer_data = (BufferData *)data; + wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, + buffer_data->height); +} + +void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, + int32_t sy, void *data) { + BufferData *buffer_data = (BufferData *)data; + + if (buffer_data->should_scale && buffer_data->height_scale < 1 && + buffer_data->width_scale < 1) { + buffer_data->should_scale = false; + } + + if (buffer_data->should_scale && buffer_data->height_scale == 1 && + buffer_data->width_scale < 1) { + buffer_data->should_scale = false; + } + + if (buffer_data->should_scale && buffer_data->height_scale < 1 && + buffer_data->width_scale == 1) { + buffer_data->should_scale = false; + } + + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(buffer); + + if (scene_surface == NULL) + return; + + struct wlr_surface *surface = scene_surface->surface; + + if (buffer_data->should_scale) { + + int32_t surface_width = surface->current.width; + int32_t surface_height = surface->current.height; + + surface_width = buffer_data->width_scale < 1 + ? surface_width + : buffer_data->width_scale * surface_width; + surface_height = buffer_data->height_scale < 1 + ? surface_height + : buffer_data->height_scale * surface_height; + + if (surface_width > buffer_data->width && + wlr_subsurface_try_from_wlr_surface(surface) == NULL) { + surface_width = buffer_data->width; + } + + if (surface_height > buffer_data->height && + wlr_subsurface_try_from_wlr_surface(surface) == NULL) { + surface_height = buffer_data->height; + } + + if (surface_width > buffer_data->width && + wlr_subsurface_try_from_wlr_surface(surface) != NULL) { + return; + } + + if (surface_height > buffer_data->height && + wlr_subsurface_try_from_wlr_surface(surface) != NULL) { + return; + } + + if (surface_height > 0 && surface_width > 0) { + wlr_scene_buffer_set_dest_size(buffer, surface_width, + surface_height); + } + } + // TODO: blur set, opacity set + + if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL) + return; + + wlr_scene_buffer_set_corner_radius(buffer, border_radius, + buffer_data->corner_location); +} + +void buffer_set_effect(Client *c, BufferData data) { + + if (!c || c->iskilling) + return; + + if (c->animation.tagouting || c->animation.tagouted || + c->animation.tagining) { + data.should_scale = false; + } + + if (c == server.grabc) + data.should_scale = false; + + if (c->isnoradius || c->isfullscreen || + (no_radius_when_single && c->mon && + c->mon->visible_tiling_clients == 1)) { + data.corner_location = CORNER_LOCATION_NONE; + } + + wlr_scene_node_for_each_buffer(&c->scene_surface->node, + scene_buffer_apply_effect, &data); +} + +void client_draw_shadow(Client *c) { + + if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow) + return; + + if (!shadows || (!c->isfloating && shadow_only_floating)) { + if (c->shadow->node.enabled) + wlr_scene_node_set_enabled(&c->shadow->node, false); + return; + } else { + if (c->scene_surface->node.enabled && !c->shadow->node.enabled) + wlr_scene_node_set_enabled(&c->shadow->node, true); + } + + bool hit_no_border = check_hit_no_border(c); + enum corner_location current_corner_location = + c->isfullscreen || (no_radius_when_single && c->mon && + c->mon->visible_tiling_clients == 1) + ? CORNER_LOCATION_NONE + : CORNER_LOCATION_ALL; + + int32_t bwoffset = c->bw != 0 && hit_no_border ? (int32_t)c->bw : 0; + + int32_t width, height; + client_actual_size(c, &width, &height); + + int32_t delta = shadows_size + (int32_t)c->bw - bwoffset; + + /* we calculate where to clip the shadow */ + struct wlr_box client_box = { + .x = bwoffset, + .y = bwoffset, + .width = width + (int32_t)c->bw - bwoffset, + .height = height + (int32_t)c->bw - bwoffset, + }; + + struct wlr_box shadow_box = { + .x = shadows_position_x + bwoffset, + .y = shadows_position_y + bwoffset, + .width = width + 2 * delta, + .height = height + 2 * delta, + }; + + struct wlr_box intersection_box; + wlr_box_intersection(&intersection_box, &client_box, &shadow_box); + /* clipped region takes shadow relative coords, so we translate everything + * by its position */ + intersection_box.x -= shadows_position_x + bwoffset; + intersection_box.y -= shadows_position_y + bwoffset; + + struct clipped_region clipped_region = { + .area = intersection_box, + .corner_radius = border_radius, + .corners = current_corner_location, + }; + + struct wlr_box absolute_shadow_box = { + .x = shadow_box.x + c->animation.current.x, + .y = shadow_box.y + c->animation.current.y, + .width = shadow_box.width, + .height = shadow_box.height, + }; + + int32_t right_offset, bottom_offset, left_offset, top_offset; + + if (c == server.grabc) { + right_offset = 0; + bottom_offset = 0; + left_offset = 0; + top_offset = 0; + } else { + right_offset = + GEZERO(absolute_shadow_box.x + absolute_shadow_box.width - + c->mon->m.x - c->mon->m.width); + bottom_offset = + GEZERO(absolute_shadow_box.y + absolute_shadow_box.height - + c->mon->m.y - c->mon->m.height); + + left_offset = GEZERO(c->mon->m.x - absolute_shadow_box.x); + top_offset = GEZERO(c->mon->m.y - absolute_shadow_box.y); + } + + left_offset = MIN(left_offset, shadow_box.width); + right_offset = MIN(right_offset, shadow_box.width); + top_offset = MIN(top_offset, shadow_box.height); + bottom_offset = MIN(bottom_offset, shadow_box.height); + + wlr_scene_node_set_position(&c->shadow->node, shadow_box.x + left_offset, + shadow_box.y + top_offset); + + wlr_scene_shadow_set_size( + c->shadow, GEZERO(shadow_box.width - left_offset - right_offset), + GEZERO(shadow_box.height - top_offset - bottom_offset)); + + clipped_region.area.x = clipped_region.area.x - left_offset; + clipped_region.area.y = clipped_region.area.y - top_offset; + + wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); +} + +void apply_border(Client *c) { + if (!c || c->iskilling || !client_surface(c)->mapped) + return; + + bool hit_no_border = check_hit_no_border(c); + enum corner_location current_corner_location; + if (c->isfullscreen || (no_radius_when_single && c->mon && + c->mon->visible_tiling_clients == 1)) { + current_corner_location = CORNER_LOCATION_NONE; + } else { + current_corner_location = set_client_corner_location(c); + } + + if (hit_no_border && smartgaps) { + c->bw = 0; + c->fake_no_border = true; + } else if (hit_no_border && !smartgaps) { + wlr_scene_rect_set_size(c->border, 0, 0); + wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); + c->fake_no_border = true; + return; + } else if (!c->isfullscreen && VISIBLEON(c, c->mon)) { + c->bw = c->isnoborder ? 0 : borderpx; + c->fake_no_border = false; + } + + struct wlr_box clip_box = c->animation.current; + // 一但在GEZERO如果使用无符号,那么其他数据也会转换为无符号导致没有负数出错 + int32_t bw = (int32_t)c->bw; + + int32_t right_offset, bottom_offset, left_offset, top_offset; + + if (c == server.grabc) { + right_offset = 0; + bottom_offset = 0; + left_offset = 0; + top_offset = 0; + } else { + right_offset = + GEZERO(c->animation.current.x + c->animation.current.width - + c->mon->m.x - c->mon->m.width); + bottom_offset = + GEZERO(c->animation.current.y + c->animation.current.height - + c->mon->m.y - c->mon->m.height); + + left_offset = GEZERO(c->mon->m.x - c->animation.current.x); + top_offset = GEZERO(c->mon->m.y - c->animation.current.y); + } + + int32_t inner_surface_width = GEZERO(clip_box.width - 2 * bw); + int32_t inner_surface_height = GEZERO(clip_box.height - 2 * bw); + + int32_t inner_surface_x = GEZERO(bw - left_offset); + int32_t inner_surface_y = GEZERO(bw - top_offset); + + int32_t rect_x = left_offset; + int32_t rect_y = top_offset; + + int32_t rect_width = + GEZERO(c->animation.current.width - left_offset - right_offset); + int32_t rect_height = + GEZERO(c->animation.current.height - top_offset - bottom_offset); + + if (left_offset > c->bw) + inner_surface_width = + inner_surface_width - left_offset + (int32_t)c->bw; + + if (top_offset > c->bw) + inner_surface_height = + inner_surface_height - top_offset + (int32_t)c->bw; + + if (right_offset > 0) { + inner_surface_width = + MIN(clip_box.width, inner_surface_width + right_offset); + } + + if (bottom_offset > 0) { + inner_surface_height = + MIN(clip_box.height, inner_surface_height + bottom_offset); + } + + struct clipped_region clipped_region = { + .area = {inner_surface_x, inner_surface_y, inner_surface_width, + inner_surface_height}, + .corner_radius = border_radius, + .corners = current_corner_location, + }; + + wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); + wlr_scene_rect_set_size(c->border, rect_width, rect_height); + wlr_scene_node_set_position(&c->border->node, rect_x, rect_y); + wlr_scene_rect_set_corner_radius(c->border, border_radius, + current_corner_location); + wlr_scene_rect_set_clipped_region(c->border, clipped_region); +} + +struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { + int32_t offsetx = 0, offsety = 0, offsetw = 0, offseth = 0; + struct ivec2 offset = {0, 0, 0, 0}; + + if (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouted && + !c->animation.tagouting) + return offset; + + int32_t bottom_out_offset = + GEZERO(c->animation.current.y + c->animation.current.height - + c->mon->m.y - c->mon->m.height); + int32_t right_out_offset = + GEZERO(c->animation.current.x + c->animation.current.width - + c->mon->m.x - c->mon->m.width); + int32_t left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x); + int32_t top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y); + + // 必须转换为int,否计算会没有负数导致判断错误 + int32_t bw = (int32_t)c->bw; + + /* + 计算窗口表面超出屏幕四个方向的偏差,避免窗口超出屏幕 + 需要主要border超出屏幕的时候不计算如偏差之内而是 + 要等窗口表面超出才开始计算偏差 + */ + if (ISSCROLLTILED(c) || c->animation.tagining || c->animation.tagouted || + c->animation.tagouting) { + if (left_out_offset > 0) { + offsetx = GEZERO(left_out_offset - bw); + clip_box->x = clip_box->x + offsetx; + clip_box->width = clip_box->width - offsetx; + } else if (right_out_offset > 0) { + offsetw = GEZERO(right_out_offset - bw); + clip_box->width = clip_box->width - offsetw; + } + + if (top_out_offset > 0) { + offsety = GEZERO(top_out_offset - bw); + clip_box->y = clip_box->y + offsety; + clip_box->height = clip_box->height - offsety; + } else if (bottom_out_offset > 0) { + offseth = GEZERO(bottom_out_offset - bw); + clip_box->height = clip_box->height - offseth; + } + } + + // 窗口表面超出屏幕四个方向的偏差 + offset.x = offsetx; + offset.y = offsety; + offset.width = offsetw; + offset.height = offseth; + + if ((clip_box->width + bw <= 0 || clip_box->height + bw <= 0) && + (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) { + c->is_clip_to_hide = true; + wlr_scene_node_set_enabled(&c->scene->node, false); + } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon)) { + c->is_clip_to_hide = false; + wlr_scene_node_set_enabled(&c->scene->node, true); + } + + return offset; +} + +void client_apply_clip(Client *c, float factor) { + + if (c->iskilling || !client_surface(c)->mapped) + return; + + struct wlr_box clip_box; + bool should_render_client_surface = false; + struct ivec2 offset; + BufferData buffer_data; + + enum corner_location current_corner_location = + set_client_corner_location(c); + + if (!animations) { + c->animation.running = false; + c->need_output_flush = false; + c->animainit_geom = c->current = c->pending = c->animation.current = + c->geom; + + client_get_clip(c, &clip_box); + + offset = clip_to_hide(c, &clip_box); + + apply_border(c); + client_draw_shadow(c); + + if (clip_box.width <= 0 || clip_box.height <= 0) { + return; + } + + wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); + buffer_set_effect(c, (BufferData){1.0f, 1.0f, clip_box.width, + clip_box.height, + current_corner_location, true}); + return; + } + + // 获取窗口动画实时位置矩形 + int32_t width, height; + client_actual_size(c, &width, &height); + + // 计算出除了边框的窗口实际剪切大小 + struct wlr_box geometry; + client_get_geometry(c, &geometry); + clip_box = (struct wlr_box){ + .x = geometry.x, + .y = geometry.y, + .width = width, + .height = height, + }; + + if (client_is_x11(c)) { + clip_box.x = 0; + clip_box.y = 0; + } + + // 检测窗口是否需要剪切超出屏幕部分,如果需要就调整实际要剪切的矩形 + offset = clip_to_hide(c, &clip_box); + + // 应用窗口装饰 + apply_border(c); + client_draw_shadow(c); + + // 如果窗口剪切区域已经剪切到0,则不渲染窗口表面 + if (clip_box.width <= 0 || clip_box.height <= 0) { + should_render_client_surface = false; + wlr_scene_node_set_enabled(&c->scene_surface->node, false); + } else { + should_render_client_surface = true; + wlr_scene_node_set_enabled(&c->scene_surface->node, true); + } + + // 不用在执行下面的窗口表面剪切和缩放等效果操作 + if (!should_render_client_surface) { + return; + } + + // 应用窗口表面剪切 + wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); + + // 获取剪切后的表面的实际大小用于计算缩放 + int32_t acutal_surface_width = geometry.width - offset.x - offset.width; + int32_t acutal_surface_height = geometry.height - offset.y - offset.height; + + if (acutal_surface_width <= 0 || acutal_surface_height <= 0) + return; + + buffer_data.should_scale = true; + buffer_data.width = clip_box.width; + buffer_data.height = clip_box.height; + buffer_data.corner_location = current_corner_location; + + if (factor == 1.0) { + buffer_data.width_scale = 1.0; + buffer_data.height_scale = 1.0; + } else { + buffer_data.width_scale = + (float)buffer_data.width / acutal_surface_width; + buffer_data.height_scale = + (float)buffer_data.height / acutal_surface_height; + } + + buffer_set_effect(c, buffer_data); +} + +void fadeout_client_animation_next_tick(Client *c) { + if (!c) + return; + + BufferData buffer_data; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; + double animation_passed = + c->animation.duration + ? (double)passed_time / (double)c->animation.duration + : 1.0; + + int32_t type = c->animation.action = c->animation.action; + double factor = find_animation_curve_at(animation_passed, type); + + int32_t width = c->animation.initial.width + + (c->current.width - c->animation.initial.width) * factor; + int32_t height = c->animation.initial.height + + (c->current.height - c->animation.initial.height) * factor; + + int32_t x = c->animation.initial.x + + (c->current.x - c->animation.initial.x) * factor; + int32_t y = c->animation.initial.y + + (c->current.y - c->animation.initial.y) * factor; + + wlr_scene_node_set_position(&c->scene->node, x, y); + + c->animation.current = (struct wlr_box){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + + double opacity_eased_progress = + find_animation_curve_at(animation_passed, OPAFADEOUT); + + double percent = fadeout_begin_opacity - + (opacity_eased_progress * fadeout_begin_opacity); + + double opacity = MAX(percent, 0); + + if (animation_fade_out && !c->nofadeout) + wlr_scene_node_for_each_buffer(&c->scene->node, + scene_buffer_apply_opacity, &opacity); + + if ((c->animation_type_close && + strcmp(c->animation_type_close, "zoom") == 0) || + (!c->animation_type_close && + strcmp(animation_type_close, "zoom") == 0)) { + + buffer_data.width = width; + buffer_data.height = height; + buffer_data.width_scale = animation_passed; + buffer_data.height_scale = animation_passed; + + wlr_scene_node_for_each_buffer( + &c->scene->node, snap_scene_buffer_apply_effect, &buffer_data); + } + + if (animation_passed >= 1.0) { + wl_list_remove(&c->fadeout_link); + wlr_scene_node_destroy(&c->scene->node); + free(c); + c = NULL; + } +} + +void client_animation_next_tick(Client *c) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; + double animation_passed = + c->animation.duration + ? (double)passed_time / (double)c->animation.duration + : 1.0; + + int32_t type = c->animation.action == NONE ? MOVE : c->animation.action; + double factor = find_animation_curve_at(animation_passed, type); + + Client *pointer_c = NULL; + double sx = 0, sy = 0; + struct wlr_surface *surface = NULL; + + int32_t width = c->animation.initial.width + + (c->current.width - c->animation.initial.width) * factor; + int32_t height = c->animation.initial.height + + (c->current.height - c->animation.initial.height) * factor; + + int32_t x = c->animation.initial.x + + (c->current.x - c->animation.initial.x) * factor; + int32_t y = c->animation.initial.y + + (c->current.y - c->animation.initial.y) * factor; + + wlr_scene_node_set_position(&c->scene->node, x, y); + c->animation.current = (struct wlr_box){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + + c->is_pending_open_animation = false; + + if (animation_passed >= 1.0) { + + // clear the open action state + // To prevent him from being mistaken that + // it's still in the opening animation in resize + c->animation.action = MOVE; + + c->animation.tagining = false; + c->animation.running = false; + + if (c->animation.tagouting) { + c->animation.tagouting = false; + wlr_scene_node_set_enabled(&c->scene->node, false); + client_set_suspended(c, true); + c->animation.tagouted = true; + c->animation.current = c->geom; + } + + xytonode(server.cursor->x, server.cursor->y, NULL, &pointer_c, NULL, + &sx, &sy); + + surface = + pointer_c && pointer_c == c ? client_surface(pointer_c) : NULL; + if (surface && pointer_c == server.selmon->sel) { + wlr_seat_pointer_notify_enter(server.seat, surface, sx, sy); + } + + // end flush in next frame, not the current frame + c->need_output_flush = false; + } + + client_apply_clip(c, factor); +} + +void init_fadeout_client(Client *c) { + + if (!c->mon || client_is_unmanaged(c)) + return; + + if (!c->scene) { + return; + } + + if ((c->animation_type_close && + strcmp(c->animation_type_close, "none") == 0) || + (!c->animation_type_close && + strcmp(animation_type_close, "none") == 0)) { + return; + } + + Client *fadeout_cient = ecalloc(1, sizeof(*fadeout_cient)); + + wlr_scene_node_set_enabled(&c->scene->node, true); + client_set_border_color(c, bordercolor); + fadeout_cient->scene = + wlr_scene_tree_snapshot(&c->scene->node, server.layers[LyrFadeOut]); + wlr_scene_node_set_enabled(&c->scene->node, false); + + if (!fadeout_cient->scene) { + free(fadeout_cient); + return; + } + + fadeout_cient->animation.duration = animation_duration_close; + fadeout_cient->geom = fadeout_cient->current = + fadeout_cient->animainit_geom = fadeout_cient->animation.initial = + c->animation.current; + fadeout_cient->mon = c->mon; + fadeout_cient->animation_type_close = c->animation_type_close; + fadeout_cient->animation.action = CLOSE; + fadeout_cient->bw = c->bw; + fadeout_cient->nofadeout = c->nofadeout; + + // 这里snap节点的坐标设置是使用的相对坐标,所以不能加上原来坐标 + // 这跟普通node有区别 + + fadeout_cient->animation.initial.x = 0; + fadeout_cient->animation.initial.y = 0; + + if ((!c->animation_type_close && + strcmp(animation_type_close, "fade") == 0) || + (c->animation_type_close && + strcmp(c->animation_type_close, "fade") == 0)) { + fadeout_cient->current.x = 0; + fadeout_cient->current.y = 0; + fadeout_cient->current.width = 0; + fadeout_cient->current.height = 0; + } else if ((c->animation_type_close && + strcmp(c->animation_type_close, "slide") == 0) || + (!c->animation_type_close && + strcmp(animation_type_close, "slide") == 0)) { + fadeout_cient->current.y = + c->geom.y + c->geom.height / 2 > c->mon->m.y + c->mon->m.height / 2 + ? c->mon->m.height - + (c->animation.current.y - c->mon->m.y) // down out + : c->mon->m.y - c->geom.height; // up out + fadeout_cient->current.x = 0; // x无偏差,垂直划出 + } else { + fadeout_cient->current.y = + (fadeout_cient->geom.height - + fadeout_cient->geom.height * zoom_end_ratio) / + 2; + fadeout_cient->current.x = + (fadeout_cient->geom.width - + fadeout_cient->geom.width * zoom_end_ratio) / + 2; + fadeout_cient->current.width = + fadeout_cient->geom.width * zoom_end_ratio; + fadeout_cient->current.height = + fadeout_cient->geom.height * zoom_end_ratio; + } + + fadeout_cient->animation.time_started = get_now_in_ms(); + wlr_scene_node_set_enabled(&fadeout_cient->scene->node, true); + wl_list_insert(&server.fadeout_clients, &fadeout_cient->fadeout_link); + + // 请求刷新屏幕 + request_fresh_all_monitors(); +} + +void client_commit(Client *c) { + c->current = c->pending; // 设置动画的结束位置 + + if (c->animation.should_animate) { + if (!c->animation.running) { + c->animation.current = c->animainit_geom; + } + + c->animation.initial = c->animainit_geom; + c->animation.time_started = get_now_in_ms(); + + // 标记动画开始 + c->animation.running = true; + c->animation.should_animate = false; + } + // 请求刷新屏幕 + request_fresh_all_monitors(); +} + +void client_set_pending_state(Client *c) { + + if (!c || c->iskilling) + return; + + // 判断是否需要动画 + if (!animations) { + c->animation.should_animate = false; + } else if (animations && c->animation.tagining) { + c->animation.should_animate = true; + } else if (!animations || c == server.grabc || + (!c->is_pending_open_animation && + wlr_box_equal(&c->current, &c->pending))) { + c->animation.should_animate = false; + } else { + c->animation.should_animate = true; + } + + if (((c->animation_type_open && + strcmp(c->animation_type_open, "none") == 0) || + (!c->animation_type_open && + strcmp(animation_type_open, "none") == 0)) && + c->animation.action == OPEN) { + c->animation.duration = 0; + } + + if (c->istagswitching) { + c->animation.duration = 0; + c->istagswitching = 0; + } + + if (server.start_drag_window) { + c->animation.should_animate = false; + c->animation.duration = 0; + } + + if (c->isnoanimation) { + c->animation.should_animate = false; + c->animation.duration = 0; + } + + // 开始动画 + client_commit(c); + c->dirty = true; +} + +void resize(Client *c, struct wlr_box geo, int32_t interact) { + + // 动画设置的起始函数,这里用来计算一些动画的起始值 + // 动画起始位置大小是由于c->animainit_geom确定的 + + if (!c || !c->mon || !client_surface(c)->mapped) + return; + + struct wlr_box *bbox; + struct wlr_box clip; + + if (!c->mon) + return; + + c->need_output_flush = true; + c->dirty = true; + + // float_geom = c->geom; + bbox = (interact || c->isfloating || c->isfullscreen) ? &server.sgeom + : &c->mon->w; + + if (is_scroller_layout(c->mon) && (!c->isfloating || c == server.grabc)) { + c->geom = geo; + c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width); + c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height); + } else { // 这里会限制不允许窗口划出屏幕 + c->geom = geo; + applybounds( + c, + bbox); // 去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常 + } + + if (!c->isnosizehint && !c->ismaximizescreen && !c->isfullscreen && + c->isfloating) { + client_set_size_bound(c); + } + + if (!c->is_pending_open_animation) { + c->animation.begin_fade_in = false; + } + + if (c->animation.action == OPEN && !c->animation.tagining && + !c->animation.tagouting && wlr_box_equal(&c->geom, &c->current)) { + c->animation.action = c->animation.action; + } else if (c->animation.tagouting) { + c->animation.duration = animation_duration_tag; + c->animation.action = TAG; + } else if (c->animation.tagining) { + c->animation.duration = animation_duration_tag; + c->animation.action = TAG; + } else if (c->is_pending_open_animation) { + c->animation.duration = animation_duration_open; + c->animation.action = OPEN; + } else { + c->animation.duration = animation_duration_move; + c->animation.action = MOVE; + } + + // 动画起始位置大小设置 + if (c->animation.tagouting) { + c->animainit_geom = c->animation.current; + } else if (c->animation.tagining) { + c->animainit_geom.height = c->animation.current.height; + c->animainit_geom.width = c->animation.current.width; + } else if (c->is_pending_open_animation) { + set_client_open_animaiton(c, c->geom); + } else { + c->animainit_geom = c->animation.current; + } + + if (c->isnoborder || c->iskilling) { + c->bw = 0; + } + + bool hit_no_border = check_hit_no_border(c); + if (hit_no_border && smartgaps) { + c->bw = 0; + c->fake_no_border = true; + } + + // c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局 + c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw, + c->geom.height - 2 * c->bw); + + if (c == server.grabc) { + c->animation.running = false; + c->need_output_flush = false; + + c->animainit_geom = c->current = c->pending = c->animation.current = + c->geom; + wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); + + client_draw_shadow(c); + apply_border(c); + client_get_clip(c, &clip); + wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); + return; + } + // 如果不是工作区切换时划出去的窗口,就让动画的结束位置,就是上面的真实位置和大小 + // c->pending 决定动画的终点,一般在其他调用resize的函数的附近设置了 + if (!c->animation.tagouting && !c->iskilling) { + c->pending = c->geom; + } + + if (c->swallowedby && c->animation.action == OPEN) { + c->animainit_geom = c->swallowedby->animation.current; + } + + if (c->swallowing) { + c->animainit_geom = c->geom; + } + + if ((c->isglobal || c->isunglobal) && c->isfloating && + c->animation.action == TAG) { + c->animainit_geom = c->geom; + } + + if (c->scratchpad_switching_mon && c->isfloating) { + c->animainit_geom = c->geom; + } + + // 开始应用动画设置 + client_set_pending_state(c); + + setborder_color(c); +} + +bool client_draw_fadeout_frame(Client *c) { + if (!c) + return false; + + fadeout_client_animation_next_tick(c); + return true; +} + +void client_set_focused_opacity_animation(Client *c) { + float *border_color = get_border_color(c); + + if (!animations) { + setborder_color(c); + return; + } + + c->opacity_animation.duration = animation_duration_focus; + memcpy(c->opacity_animation.target_border_color, border_color, + sizeof(c->opacity_animation.target_border_color)); + c->opacity_animation.target_opacity = c->focused_opacity; + c->opacity_animation.time_started = get_now_in_ms(); + memcpy(c->opacity_animation.initial_border_color, + c->opacity_animation.current_border_color, + sizeof(c->opacity_animation.initial_border_color)); + c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity; + + c->opacity_animation.running = true; +} + +void client_set_unfocused_opacity_animation(Client *c) { + // Start border color animation to unfocused + float *border_color = get_border_color(c); + + if (!animations) { + setborder_color(c); + return; + } + + c->opacity_animation.duration = animation_duration_focus; + memcpy(c->opacity_animation.target_border_color, border_color, + sizeof(c->opacity_animation.target_border_color)); + // Start opacity animation to unfocused + c->opacity_animation.target_opacity = c->unfocused_opacity; + c->opacity_animation.time_started = get_now_in_ms(); + + memcpy(c->opacity_animation.initial_border_color, + c->opacity_animation.current_border_color, + sizeof(c->opacity_animation.initial_border_color)); + c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity; + + c->opacity_animation.running = true; +} + +bool client_apply_focus_opacity(Client *c) { + // Animate focus transitions (opacity + border color) + float *border_color = get_border_color(c); + if (c->isfullscreen) { + c->opacity_animation.running = false; + client_set_opacity(c, 1); + } else if (c->animation.running && c->animation.action == OPEN) { + c->opacity_animation.running = false; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; + double linear_progress = + c->animation.duration + ? (double)passed_time / (double)c->animation.duration + : 1.0; + + double opacity_eased_progress = + find_animation_curve_at(linear_progress, OPAFADEIN); + + float percent = + animation_fade_in && !c->nofadein ? opacity_eased_progress : 1.0; + float opacity = + c == server.selmon->sel ? c->focused_opacity : c->unfocused_opacity; + + float target_opacity = + percent * (1.0 - fadein_begin_opacity) + fadein_begin_opacity; + if (target_opacity > opacity) { + target_opacity = opacity; + } + memcpy(c->opacity_animation.current_border_color, + c->opacity_animation.target_border_color, + sizeof(c->opacity_animation.current_border_color)); + c->opacity_animation.current_opacity = target_opacity; + client_set_opacity(c, target_opacity); + client_set_border_color(c, c->opacity_animation.target_border_color); + } else if (animations && c->opacity_animation.running) { + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + int32_t passed_time = + timespec_to_ms(&now) - c->opacity_animation.time_started; + double linear_progress = + c->opacity_animation.duration + ? (double)passed_time / (double)c->opacity_animation.duration + : 1.0; + + float eased_progress = find_animation_curve_at(linear_progress, FOCUS); + + c->opacity_animation.current_opacity = + c->opacity_animation.initial_opacity + + (c->opacity_animation.target_opacity - + c->opacity_animation.initial_opacity) * + eased_progress; + client_set_opacity(c, c->opacity_animation.current_opacity); + + // Animate border color + for (int32_t i = 0; i < 4; i++) { + c->opacity_animation.current_border_color[i] = + c->opacity_animation.initial_border_color[i] + + (c->opacity_animation.target_border_color[i] - + c->opacity_animation.initial_border_color[i]) * + eased_progress; + } + client_set_border_color(c, c->opacity_animation.current_border_color); + if (linear_progress >= 1.0f) { + c->opacity_animation.running = false; + } else { + return true; + } + } else if (c == server.selmon->sel) { + c->opacity_animation.running = false; + c->opacity_animation.current_opacity = c->focused_opacity; + memcpy(c->opacity_animation.current_border_color, border_color, + sizeof(c->opacity_animation.current_border_color)); + client_set_opacity(c, c->focused_opacity); + } else { + c->opacity_animation.running = false; + c->opacity_animation.current_opacity = c->unfocused_opacity; + memcpy(c->opacity_animation.current_border_color, border_color, + sizeof(c->opacity_animation.current_border_color)); + client_set_opacity(c, c->unfocused_opacity); + } + + return false; +} + +bool client_draw_frame(Client *c) { + + if (!c || !client_surface(c)->mapped) + return false; + + if (!c->need_output_flush) { + return client_apply_focus_opacity(c); + } + + if (animations && c->animation.running) { + client_animation_next_tick(c); + } else { + wlr_scene_node_set_position(&c->scene->node, c->pending.x, + c->pending.y); + c->animation.current = c->animainit_geom = c->animation.initial = + c->pending = c->current = c->geom; + client_apply_clip(c, 1.0); + c->need_output_flush = false; + } + client_apply_focus_opacity(c); + return true; +} diff --git a/src/animation/client.h b/src/animation/client.h index c2b48e3..033eec3 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -1,1200 +1,31 @@ -void client_actual_size(Client *c, int32_t *width, int32_t *height) { - *width = c->animation.current.width - 2 * (int32_t)c->bw; - - *height = c->animation.current.height - 2 * (int32_t)c->bw; -} - -void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) { - wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height)); -} - -enum corner_location set_client_corner_location(Client *c) { - enum corner_location current_corner_location = CORNER_LOCATION_ALL; - struct wlr_box target_geom = animations ? c->animation.current : c->geom; - if (target_geom.x + border_radius <= c->mon->m.x) { - current_corner_location &= ~CORNER_LOCATION_LEFT; // 清除左标志位 - } - if (target_geom.x + target_geom.width - border_radius >= - c->mon->m.x + c->mon->m.width) { - current_corner_location &= ~CORNER_LOCATION_RIGHT; // 清除右标志位 - } - if (target_geom.y + border_radius <= c->mon->m.y) { - current_corner_location &= ~CORNER_LOCATION_TOP; // 清除上标志位 - } - if (target_geom.y + target_geom.height - border_radius >= - c->mon->m.y + c->mon->m.height) { - current_corner_location &= ~CORNER_LOCATION_BOTTOM; // 清除下标志位 - } - return current_corner_location; -} - -bool is_horizontal_stack_layout(Monitor *m) { - - if (m->pertag->curtag && - (m->pertag->ltidxs[m->pertag->curtag]->id == TILE || - m->pertag->ltidxs[m->pertag->curtag]->id == DECK)) - return true; - - return false; -} - -bool is_horizontal_right_stack_layout(Monitor *m) { - - if (m->pertag->curtag && - (m->pertag->ltidxs[m->pertag->curtag]->id == RIGHT_TILE)) - return true; - - return false; -} - -int32_t is_special_animaiton_rule(Client *c) { - - if (is_scroller_layout(c->mon) && !c->isfloating) { - return DOWN; - } else if (c->mon->visible_tiling_clients == 1 && !c->isfloating) { - return DOWN; - } else if (c->mon->visible_tiling_clients == 2 && !c->isfloating && - !new_is_master && is_horizontal_stack_layout(c->mon)) { - return RIGHT; - } else if (!c->isfloating && new_is_master && - is_horizontal_stack_layout(c->mon)) { - return LEFT; - } else if (c->mon->visible_tiling_clients == 2 && !c->isfloating && - !new_is_master && is_horizontal_right_stack_layout(c->mon)) { - return LEFT; - } else if (!c->isfloating && new_is_master && - is_horizontal_right_stack_layout(c->mon)) { - return RIGHT; - } else { - return UNDIR; - } -} - -void set_client_open_animaiton(Client *c, struct wlr_box geo) { - int32_t slide_direction; - int32_t horizontal, horizontal_value; - int32_t vertical, vertical_value; - int32_t special_direction; - int32_t center_x, center_y; - - if ((!c->animation_type_open && strcmp(animation_type_open, "fade") == 0) || - (c->animation_type_open && - strcmp(c->animation_type_open, "fade") == 0)) { - c->animainit_geom.width = geo.width; - c->animainit_geom.height = geo.height; - c->animainit_geom.x = geo.x; - c->animainit_geom.y = geo.y; - return; - } else if ((!c->animation_type_open && - strcmp(animation_type_open, "zoom") == 0) || - (c->animation_type_open && - strcmp(c->animation_type_open, "zoom") == 0)) { - c->animainit_geom.width = geo.width * zoom_initial_ratio; - c->animainit_geom.height = geo.height * zoom_initial_ratio; - c->animainit_geom.x = geo.x + (geo.width - c->animainit_geom.width) / 2; - c->animainit_geom.y = - geo.y + (geo.height - c->animainit_geom.height) / 2; - return; - } else { - special_direction = is_special_animaiton_rule(c); - center_x = c->geom.x + c->geom.width / 2; - center_y = c->geom.y + c->geom.height / 2; - if (special_direction == UNDIR) { - horizontal = c->mon->w.x + c->mon->w.width - center_x < - center_x - c->mon->w.x - ? RIGHT - : LEFT; - horizontal_value = horizontal == LEFT - ? center_x - c->mon->w.x - : c->mon->w.x + c->mon->w.width - center_x; - vertical = c->mon->w.y + c->mon->w.height - center_y < - center_y - c->mon->w.y - ? DOWN - : UP; - vertical_value = vertical == UP - ? center_y - c->mon->w.y - : c->mon->w.y + c->mon->w.height - center_y; - slide_direction = - horizontal_value < vertical_value ? horizontal : vertical; - } else { - slide_direction = special_direction; - } - c->animainit_geom.width = c->geom.width; - c->animainit_geom.height = c->geom.height; - switch (slide_direction) { - case UP: - c->animainit_geom.x = c->geom.x; - c->animainit_geom.y = c->mon->m.y - c->geom.height; - break; - case DOWN: - c->animainit_geom.x = c->geom.x; - c->animainit_geom.y = - c->geom.y + c->mon->m.height - (c->geom.y - c->mon->m.y); - break; - case LEFT: - c->animainit_geom.x = c->mon->m.x - c->geom.width; - c->animainit_geom.y = c->geom.y; - break; - case RIGHT: - c->animainit_geom.x = - c->geom.x + c->mon->m.width - (c->geom.x - c->mon->m.x); - c->animainit_geom.y = c->geom.y; - break; - default: - c->animainit_geom.x = c->geom.x; - c->animainit_geom.y = 0 - c->geom.height; - } - } -} - -void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, - int32_t sy, void *data) { - BufferData *buffer_data = (BufferData *)data; - wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, - buffer_data->height); -} - -void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, - int32_t sy, void *data) { - BufferData *buffer_data = (BufferData *)data; - - if (buffer_data->should_scale && buffer_data->height_scale < 1 && - buffer_data->width_scale < 1) { - buffer_data->should_scale = false; - } - - if (buffer_data->should_scale && buffer_data->height_scale == 1 && - buffer_data->width_scale < 1) { - buffer_data->should_scale = false; - } - - if (buffer_data->should_scale && buffer_data->height_scale < 1 && - buffer_data->width_scale == 1) { - buffer_data->should_scale = false; - } - - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_try_from_buffer(buffer); - - if (scene_surface == NULL) - return; - - struct wlr_surface *surface = scene_surface->surface; - - if (buffer_data->should_scale) { - - int32_t surface_width = surface->current.width; - int32_t surface_height = surface->current.height; - - surface_width = buffer_data->width_scale < 1 - ? surface_width - : buffer_data->width_scale * surface_width; - surface_height = buffer_data->height_scale < 1 - ? surface_height - : buffer_data->height_scale * surface_height; - - if (surface_width > buffer_data->width && - wlr_subsurface_try_from_wlr_surface(surface) == NULL) { - surface_width = buffer_data->width; - } - - if (surface_height > buffer_data->height && - wlr_subsurface_try_from_wlr_surface(surface) == NULL) { - surface_height = buffer_data->height; - } - - if (surface_width > buffer_data->width && - wlr_subsurface_try_from_wlr_surface(surface) != NULL) { - return; - } - - if (surface_height > buffer_data->height && - wlr_subsurface_try_from_wlr_surface(surface) != NULL) { - return; - } - - if (surface_height > 0 && surface_width > 0) { - wlr_scene_buffer_set_dest_size(buffer, surface_width, - surface_height); - } - } - // TODO: blur set, opacity set - - if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL) - return; - - wlr_scene_buffer_set_corner_radius(buffer, border_radius, - buffer_data->corner_location); -} - -void buffer_set_effect(Client *c, BufferData data) { - - if (!c || c->iskilling) - return; - - if (c->animation.tagouting || c->animation.tagouted || - c->animation.tagining) { - data.should_scale = false; - } - - if (c == server.grabc) - data.should_scale = false; - - if (c->isnoradius || c->isfullscreen || - (no_radius_when_single && c->mon && - c->mon->visible_tiling_clients == 1)) { - data.corner_location = CORNER_LOCATION_NONE; - } - - wlr_scene_node_for_each_buffer(&c->scene_surface->node, - scene_buffer_apply_effect, &data); -} - -void client_draw_shadow(Client *c) { - - if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow) - return; - - if (!shadows || (!c->isfloating && shadow_only_floating)) { - if (c->shadow->node.enabled) - wlr_scene_node_set_enabled(&c->shadow->node, false); - return; - } else { - if (c->scene_surface->node.enabled && !c->shadow->node.enabled) - wlr_scene_node_set_enabled(&c->shadow->node, true); - } - - bool hit_no_border = check_hit_no_border(c); - enum corner_location current_corner_location = - c->isfullscreen || (no_radius_when_single && c->mon && - c->mon->visible_tiling_clients == 1) - ? CORNER_LOCATION_NONE - : CORNER_LOCATION_ALL; - - int32_t bwoffset = c->bw != 0 && hit_no_border ? (int32_t)c->bw : 0; - - int32_t width, height; - client_actual_size(c, &width, &height); - - int32_t delta = shadows_size + (int32_t)c->bw - bwoffset; - - /* we calculate where to clip the shadow */ - struct wlr_box client_box = { - .x = bwoffset, - .y = bwoffset, - .width = width + (int32_t)c->bw - bwoffset, - .height = height + (int32_t)c->bw - bwoffset, - }; - - struct wlr_box shadow_box = { - .x = shadows_position_x + bwoffset, - .y = shadows_position_y + bwoffset, - .width = width + 2 * delta, - .height = height + 2 * delta, - }; - - struct wlr_box intersection_box; - wlr_box_intersection(&intersection_box, &client_box, &shadow_box); - /* clipped region takes shadow relative coords, so we translate everything - * by its position */ - intersection_box.x -= shadows_position_x + bwoffset; - intersection_box.y -= shadows_position_y + bwoffset; - - struct clipped_region clipped_region = { - .area = intersection_box, - .corner_radius = border_radius, - .corners = current_corner_location, - }; - - struct wlr_box absolute_shadow_box = { - .x = shadow_box.x + c->animation.current.x, - .y = shadow_box.y + c->animation.current.y, - .width = shadow_box.width, - .height = shadow_box.height, - }; - - int32_t right_offset, bottom_offset, left_offset, top_offset; - - if (c == server.grabc) { - right_offset = 0; - bottom_offset = 0; - left_offset = 0; - top_offset = 0; - } else { - right_offset = - GEZERO(absolute_shadow_box.x + absolute_shadow_box.width - - c->mon->m.x - c->mon->m.width); - bottom_offset = - GEZERO(absolute_shadow_box.y + absolute_shadow_box.height - - c->mon->m.y - c->mon->m.height); - - left_offset = GEZERO(c->mon->m.x - absolute_shadow_box.x); - top_offset = GEZERO(c->mon->m.y - absolute_shadow_box.y); - } - - left_offset = MIN(left_offset, shadow_box.width); - right_offset = MIN(right_offset, shadow_box.width); - top_offset = MIN(top_offset, shadow_box.height); - bottom_offset = MIN(bottom_offset, shadow_box.height); - - wlr_scene_node_set_position(&c->shadow->node, shadow_box.x + left_offset, - shadow_box.y + top_offset); - - wlr_scene_shadow_set_size( - c->shadow, GEZERO(shadow_box.width - left_offset - right_offset), - GEZERO(shadow_box.height - top_offset - bottom_offset)); - - clipped_region.area.x = clipped_region.area.x - left_offset; - clipped_region.area.y = clipped_region.area.y - top_offset; - - wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); -} - -void apply_border(Client *c) { - if (!c || c->iskilling || !client_surface(c)->mapped) - return; - - bool hit_no_border = check_hit_no_border(c); - enum corner_location current_corner_location; - if (c->isfullscreen || (no_radius_when_single && c->mon && - c->mon->visible_tiling_clients == 1)) { - current_corner_location = CORNER_LOCATION_NONE; - } else { - current_corner_location = set_client_corner_location(c); - } - - if (hit_no_border && smartgaps) { - c->bw = 0; - c->fake_no_border = true; - } else if (hit_no_border && !smartgaps) { - wlr_scene_rect_set_size(c->border, 0, 0); - wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); - c->fake_no_border = true; - return; - } else if (!c->isfullscreen && VISIBLEON(c, c->mon)) { - c->bw = c->isnoborder ? 0 : borderpx; - c->fake_no_border = false; - } - - struct wlr_box clip_box = c->animation.current; - // 一但在GEZERO如果使用无符号,那么其他数据也会转换为无符号导致没有负数出错 - int32_t bw = (int32_t)c->bw; - - int32_t right_offset, bottom_offset, left_offset, top_offset; - - if (c == server.grabc) { - right_offset = 0; - bottom_offset = 0; - left_offset = 0; - top_offset = 0; - } else { - right_offset = - GEZERO(c->animation.current.x + c->animation.current.width - - c->mon->m.x - c->mon->m.width); - bottom_offset = - GEZERO(c->animation.current.y + c->animation.current.height - - c->mon->m.y - c->mon->m.height); - - left_offset = GEZERO(c->mon->m.x - c->animation.current.x); - top_offset = GEZERO(c->mon->m.y - c->animation.current.y); - } - - int32_t inner_surface_width = GEZERO(clip_box.width - 2 * bw); - int32_t inner_surface_height = GEZERO(clip_box.height - 2 * bw); - - int32_t inner_surface_x = GEZERO(bw - left_offset); - int32_t inner_surface_y = GEZERO(bw - top_offset); - - int32_t rect_x = left_offset; - int32_t rect_y = top_offset; - - int32_t rect_width = - GEZERO(c->animation.current.width - left_offset - right_offset); - int32_t rect_height = - GEZERO(c->animation.current.height - top_offset - bottom_offset); - - if (left_offset > c->bw) - inner_surface_width = - inner_surface_width - left_offset + (int32_t)c->bw; - - if (top_offset > c->bw) - inner_surface_height = - inner_surface_height - top_offset + (int32_t)c->bw; - - if (right_offset > 0) { - inner_surface_width = - MIN(clip_box.width, inner_surface_width + right_offset); - } - - if (bottom_offset > 0) { - inner_surface_height = - MIN(clip_box.height, inner_surface_height + bottom_offset); - } - - struct clipped_region clipped_region = { - .area = {inner_surface_x, inner_surface_y, inner_surface_width, - inner_surface_height}, - .corner_radius = border_radius, - .corners = current_corner_location, - }; - - wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); - wlr_scene_rect_set_size(c->border, rect_width, rect_height); - wlr_scene_node_set_position(&c->border->node, rect_x, rect_y); - wlr_scene_rect_set_corner_radius(c->border, border_radius, - current_corner_location); - wlr_scene_rect_set_clipped_region(c->border, clipped_region); -} - -struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { - int32_t offsetx = 0, offsety = 0, offsetw = 0, offseth = 0; - struct ivec2 offset = {0, 0, 0, 0}; - - if (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouted && - !c->animation.tagouting) - return offset; - - int32_t bottom_out_offset = - GEZERO(c->animation.current.y + c->animation.current.height - - c->mon->m.y - c->mon->m.height); - int32_t right_out_offset = - GEZERO(c->animation.current.x + c->animation.current.width - - c->mon->m.x - c->mon->m.width); - int32_t left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x); - int32_t top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y); - - // 必须转换为int,否计算会没有负数导致判断错误 - int32_t bw = (int32_t)c->bw; - - /* - 计算窗口表面超出屏幕四个方向的偏差,避免窗口超出屏幕 - 需要主要border超出屏幕的时候不计算如偏差之内而是 - 要等窗口表面超出才开始计算偏差 - */ - if (ISSCROLLTILED(c) || c->animation.tagining || c->animation.tagouted || - c->animation.tagouting) { - if (left_out_offset > 0) { - offsetx = GEZERO(left_out_offset - bw); - clip_box->x = clip_box->x + offsetx; - clip_box->width = clip_box->width - offsetx; - } else if (right_out_offset > 0) { - offsetw = GEZERO(right_out_offset - bw); - clip_box->width = clip_box->width - offsetw; - } - - if (top_out_offset > 0) { - offsety = GEZERO(top_out_offset - bw); - clip_box->y = clip_box->y + offsety; - clip_box->height = clip_box->height - offsety; - } else if (bottom_out_offset > 0) { - offseth = GEZERO(bottom_out_offset - bw); - clip_box->height = clip_box->height - offseth; - } - } - - // 窗口表面超出屏幕四个方向的偏差 - offset.x = offsetx; - offset.y = offsety; - offset.width = offsetw; - offset.height = offseth; - - if ((clip_box->width + bw <= 0 || clip_box->height + bw <= 0) && - (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) { - c->is_clip_to_hide = true; - wlr_scene_node_set_enabled(&c->scene->node, false); - } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon)) { - c->is_clip_to_hide = false; - wlr_scene_node_set_enabled(&c->scene->node, true); - } - - return offset; -} - -void client_apply_clip(Client *c, float factor) { - - if (c->iskilling || !client_surface(c)->mapped) - return; - - struct wlr_box clip_box; - bool should_render_client_surface = false; - struct ivec2 offset; - BufferData buffer_data; - - enum corner_location current_corner_location = - set_client_corner_location(c); - - if (!animations) { - c->animation.running = false; - c->need_output_flush = false; - c->animainit_geom = c->current = c->pending = c->animation.current = - c->geom; - - client_get_clip(c, &clip_box); - - offset = clip_to_hide(c, &clip_box); - - apply_border(c); - client_draw_shadow(c); - - if (clip_box.width <= 0 || clip_box.height <= 0) { - return; - } - - wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); - buffer_set_effect(c, (BufferData){1.0f, 1.0f, clip_box.width, - clip_box.height, - current_corner_location, true}); - return; - } - - // 获取窗口动画实时位置矩形 - int32_t width, height; - client_actual_size(c, &width, &height); - - // 计算出除了边框的窗口实际剪切大小 - struct wlr_box geometry; - client_get_geometry(c, &geometry); - clip_box = (struct wlr_box){ - .x = geometry.x, - .y = geometry.y, - .width = width, - .height = height, - }; - - if (client_is_x11(c)) { - clip_box.x = 0; - clip_box.y = 0; - } - - // 检测窗口是否需要剪切超出屏幕部分,如果需要就调整实际要剪切的矩形 - offset = clip_to_hide(c, &clip_box); - - // 应用窗口装饰 - apply_border(c); - client_draw_shadow(c); - - // 如果窗口剪切区域已经剪切到0,则不渲染窗口表面 - if (clip_box.width <= 0 || clip_box.height <= 0) { - should_render_client_surface = false; - wlr_scene_node_set_enabled(&c->scene_surface->node, false); - } else { - should_render_client_surface = true; - wlr_scene_node_set_enabled(&c->scene_surface->node, true); - } - - // 不用在执行下面的窗口表面剪切和缩放等效果操作 - if (!should_render_client_surface) { - return; - } - - // 应用窗口表面剪切 - wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); - - // 获取剪切后的表面的实际大小用于计算缩放 - int32_t acutal_surface_width = geometry.width - offset.x - offset.width; - int32_t acutal_surface_height = geometry.height - offset.y - offset.height; - - if (acutal_surface_width <= 0 || acutal_surface_height <= 0) - return; - - buffer_data.should_scale = true; - buffer_data.width = clip_box.width; - buffer_data.height = clip_box.height; - buffer_data.corner_location = current_corner_location; - - if (factor == 1.0) { - buffer_data.width_scale = 1.0; - buffer_data.height_scale = 1.0; - } else { - buffer_data.width_scale = - (float)buffer_data.width / acutal_surface_width; - buffer_data.height_scale = - (float)buffer_data.height / acutal_surface_height; - } - - buffer_set_effect(c, buffer_data); -} - -void fadeout_client_animation_next_tick(Client *c) { - if (!c) - return; - - BufferData buffer_data; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; - double animation_passed = - c->animation.duration - ? (double)passed_time / (double)c->animation.duration - : 1.0; - - int32_t type = c->animation.action = c->animation.action; - double factor = find_animation_curve_at(animation_passed, type); - - int32_t width = c->animation.initial.width + - (c->current.width - c->animation.initial.width) * factor; - int32_t height = c->animation.initial.height + - (c->current.height - c->animation.initial.height) * factor; - - int32_t x = c->animation.initial.x + - (c->current.x - c->animation.initial.x) * factor; - int32_t y = c->animation.initial.y + - (c->current.y - c->animation.initial.y) * factor; - - wlr_scene_node_set_position(&c->scene->node, x, y); - - c->animation.current = (struct wlr_box){ - .x = x, - .y = y, - .width = width, - .height = height, - }; - - double opacity_eased_progress = - find_animation_curve_at(animation_passed, OPAFADEOUT); - - double percent = fadeout_begin_opacity - - (opacity_eased_progress * fadeout_begin_opacity); - - double opacity = MAX(percent, 0); - - if (animation_fade_out && !c->nofadeout) - wlr_scene_node_for_each_buffer(&c->scene->node, - scene_buffer_apply_opacity, &opacity); - - if ((c->animation_type_close && - strcmp(c->animation_type_close, "zoom") == 0) || - (!c->animation_type_close && - strcmp(animation_type_close, "zoom") == 0)) { - - buffer_data.width = width; - buffer_data.height = height; - buffer_data.width_scale = animation_passed; - buffer_data.height_scale = animation_passed; - - wlr_scene_node_for_each_buffer( - &c->scene->node, snap_scene_buffer_apply_effect, &buffer_data); - } - - if (animation_passed >= 1.0) { - wl_list_remove(&c->fadeout_link); - wlr_scene_node_destroy(&c->scene->node); - free(c); - c = NULL; - } -} - -void client_animation_next_tick(Client *c) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; - double animation_passed = - c->animation.duration - ? (double)passed_time / (double)c->animation.duration - : 1.0; - - int32_t type = c->animation.action == NONE ? MOVE : c->animation.action; - double factor = find_animation_curve_at(animation_passed, type); - - Client *pointer_c = NULL; - double sx = 0, sy = 0; - struct wlr_surface *surface = NULL; - - int32_t width = c->animation.initial.width + - (c->current.width - c->animation.initial.width) * factor; - int32_t height = c->animation.initial.height + - (c->current.height - c->animation.initial.height) * factor; - - int32_t x = c->animation.initial.x + - (c->current.x - c->animation.initial.x) * factor; - int32_t y = c->animation.initial.y + - (c->current.y - c->animation.initial.y) * factor; - - wlr_scene_node_set_position(&c->scene->node, x, y); - c->animation.current = (struct wlr_box){ - .x = x, - .y = y, - .width = width, - .height = height, - }; - - c->is_pending_open_animation = false; - - if (animation_passed >= 1.0) { - - // clear the open action state - // To prevent him from being mistaken that - // it's still in the opening animation in resize - c->animation.action = MOVE; - - c->animation.tagining = false; - c->animation.running = false; - - if (c->animation.tagouting) { - c->animation.tagouting = false; - wlr_scene_node_set_enabled(&c->scene->node, false); - client_set_suspended(c, true); - c->animation.tagouted = true; - c->animation.current = c->geom; - } - - xytonode(server.cursor->x, server.cursor->y, NULL, &pointer_c, NULL, - &sx, &sy); - - surface = - pointer_c && pointer_c == c ? client_surface(pointer_c) : NULL; - if (surface && pointer_c == server.selmon->sel) { - wlr_seat_pointer_notify_enter(server.seat, surface, sx, sy); - } - - // end flush in next frame, not the current frame - c->need_output_flush = false; - } - - client_apply_clip(c, factor); -} - -void init_fadeout_client(Client *c) { - - if (!c->mon || client_is_unmanaged(c)) - return; - - if (!c->scene) { - return; - } - - if ((c->animation_type_close && - strcmp(c->animation_type_close, "none") == 0) || - (!c->animation_type_close && - strcmp(animation_type_close, "none") == 0)) { - return; - } - - Client *fadeout_cient = ecalloc(1, sizeof(*fadeout_cient)); - - wlr_scene_node_set_enabled(&c->scene->node, true); - client_set_border_color(c, bordercolor); - fadeout_cient->scene = - wlr_scene_tree_snapshot(&c->scene->node, server.layers[LyrFadeOut]); - wlr_scene_node_set_enabled(&c->scene->node, false); - - if (!fadeout_cient->scene) { - free(fadeout_cient); - return; - } - - fadeout_cient->animation.duration = animation_duration_close; - fadeout_cient->geom = fadeout_cient->current = - fadeout_cient->animainit_geom = fadeout_cient->animation.initial = - c->animation.current; - fadeout_cient->mon = c->mon; - fadeout_cient->animation_type_close = c->animation_type_close; - fadeout_cient->animation.action = CLOSE; - fadeout_cient->bw = c->bw; - fadeout_cient->nofadeout = c->nofadeout; - - // 这里snap节点的坐标设置是使用的相对坐标,所以不能加上原来坐标 - // 这跟普通node有区别 - - fadeout_cient->animation.initial.x = 0; - fadeout_cient->animation.initial.y = 0; - - if ((!c->animation_type_close && - strcmp(animation_type_close, "fade") == 0) || - (c->animation_type_close && - strcmp(c->animation_type_close, "fade") == 0)) { - fadeout_cient->current.x = 0; - fadeout_cient->current.y = 0; - fadeout_cient->current.width = 0; - fadeout_cient->current.height = 0; - } else if ((c->animation_type_close && - strcmp(c->animation_type_close, "slide") == 0) || - (!c->animation_type_close && - strcmp(animation_type_close, "slide") == 0)) { - fadeout_cient->current.y = - c->geom.y + c->geom.height / 2 > c->mon->m.y + c->mon->m.height / 2 - ? c->mon->m.height - - (c->animation.current.y - c->mon->m.y) // down out - : c->mon->m.y - c->geom.height; // up out - fadeout_cient->current.x = 0; // x无偏差,垂直划出 - } else { - fadeout_cient->current.y = - (fadeout_cient->geom.height - - fadeout_cient->geom.height * zoom_end_ratio) / - 2; - fadeout_cient->current.x = - (fadeout_cient->geom.width - - fadeout_cient->geom.width * zoom_end_ratio) / - 2; - fadeout_cient->current.width = - fadeout_cient->geom.width * zoom_end_ratio; - fadeout_cient->current.height = - fadeout_cient->geom.height * zoom_end_ratio; - } - - fadeout_cient->animation.time_started = get_now_in_ms(); - wlr_scene_node_set_enabled(&fadeout_cient->scene->node, true); - wl_list_insert(&server.fadeout_clients, &fadeout_cient->fadeout_link); - - // 请求刷新屏幕 - request_fresh_all_monitors(); -} - -void client_commit(Client *c) { - c->current = c->pending; // 设置动画的结束位置 - - if (c->animation.should_animate) { - if (!c->animation.running) { - c->animation.current = c->animainit_geom; - } - - c->animation.initial = c->animainit_geom; - c->animation.time_started = get_now_in_ms(); - - // 标记动画开始 - c->animation.running = true; - c->animation.should_animate = false; - } - // 请求刷新屏幕 - request_fresh_all_monitors(); -} - -void client_set_pending_state(Client *c) { - - if (!c || c->iskilling) - return; - - // 判断是否需要动画 - if (!animations) { - c->animation.should_animate = false; - } else if (animations && c->animation.tagining) { - c->animation.should_animate = true; - } else if (!animations || c == server.grabc || - (!c->is_pending_open_animation && - wlr_box_equal(&c->current, &c->pending))) { - c->animation.should_animate = false; - } else { - c->animation.should_animate = true; - } - - if (((c->animation_type_open && - strcmp(c->animation_type_open, "none") == 0) || - (!c->animation_type_open && - strcmp(animation_type_open, "none") == 0)) && - c->animation.action == OPEN) { - c->animation.duration = 0; - } - - if (c->istagswitching) { - c->animation.duration = 0; - c->istagswitching = 0; - } - - if (server.start_drag_window) { - c->animation.should_animate = false; - c->animation.duration = 0; - } - - if (c->isnoanimation) { - c->animation.should_animate = false; - c->animation.duration = 0; - } - - // 开始动画 - client_commit(c); - c->dirty = true; -} - -void resize(Client *c, struct wlr_box geo, int32_t interact) { - - // 动画设置的起始函数,这里用来计算一些动画的起始值 - // 动画起始位置大小是由于c->animainit_geom确定的 - - if (!c || !c->mon || !client_surface(c)->mapped) - return; - - struct wlr_box *bbox; - struct wlr_box clip; - - if (!c->mon) - return; - - c->need_output_flush = true; - c->dirty = true; - - // float_geom = c->geom; - bbox = (interact || c->isfloating || c->isfullscreen) ? &server.sgeom - : &c->mon->w; - - if (is_scroller_layout(c->mon) && (!c->isfloating || c == server.grabc)) { - c->geom = geo; - c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width); - c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height); - } else { // 这里会限制不允许窗口划出屏幕 - c->geom = geo; - applybounds( - c, - bbox); // 去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常 - } - - if (!c->isnosizehint && !c->ismaximizescreen && !c->isfullscreen && - c->isfloating) { - client_set_size_bound(c); - } - - if (!c->is_pending_open_animation) { - c->animation.begin_fade_in = false; - } - - if (c->animation.action == OPEN && !c->animation.tagining && - !c->animation.tagouting && wlr_box_equal(&c->geom, &c->current)) { - c->animation.action = c->animation.action; - } else if (c->animation.tagouting) { - c->animation.duration = animation_duration_tag; - c->animation.action = TAG; - } else if (c->animation.tagining) { - c->animation.duration = animation_duration_tag; - c->animation.action = TAG; - } else if (c->is_pending_open_animation) { - c->animation.duration = animation_duration_open; - c->animation.action = OPEN; - } else { - c->animation.duration = animation_duration_move; - c->animation.action = MOVE; - } - - // 动画起始位置大小设置 - if (c->animation.tagouting) { - c->animainit_geom = c->animation.current; - } else if (c->animation.tagining) { - c->animainit_geom.height = c->animation.current.height; - c->animainit_geom.width = c->animation.current.width; - } else if (c->is_pending_open_animation) { - set_client_open_animaiton(c, c->geom); - } else { - c->animainit_geom = c->animation.current; - } - - if (c->isnoborder || c->iskilling) { - c->bw = 0; - } - - bool hit_no_border = check_hit_no_border(c); - if (hit_no_border && smartgaps) { - c->bw = 0; - c->fake_no_border = true; - } - - // c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局 - c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw, - c->geom.height - 2 * c->bw); - - if (c == server.grabc) { - c->animation.running = false; - c->need_output_flush = false; - - c->animainit_geom = c->current = c->pending = c->animation.current = - c->geom; - wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); - - client_draw_shadow(c); - apply_border(c); - client_get_clip(c, &clip); - wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); - return; - } - // 如果不是工作区切换时划出去的窗口,就让动画的结束位置,就是上面的真实位置和大小 - // c->pending 决定动画的终点,一般在其他调用resize的函数的附近设置了 - if (!c->animation.tagouting && !c->iskilling) { - c->pending = c->geom; - } - - if (c->swallowedby && c->animation.action == OPEN) { - c->animainit_geom = c->swallowedby->animation.current; - } - - if (c->swallowing) { - c->animainit_geom = c->geom; - } - - if ((c->isglobal || c->isunglobal) && c->isfloating && - c->animation.action == TAG) { - c->animainit_geom = c->geom; - } - - if (c->scratchpad_switching_mon && c->isfloating) { - c->animainit_geom = c->geom; - } - - // 开始应用动画设置 - client_set_pending_state(c); - - setborder_color(c); -} - -bool client_draw_fadeout_frame(Client *c) { - if (!c) - return false; - - fadeout_client_animation_next_tick(c); - return true; -} - -void client_set_focused_opacity_animation(Client *c) { - float *border_color = get_border_color(c); - - if (!animations) { - setborder_color(c); - return; - } - - c->opacity_animation.duration = animation_duration_focus; - memcpy(c->opacity_animation.target_border_color, border_color, - sizeof(c->opacity_animation.target_border_color)); - c->opacity_animation.target_opacity = c->focused_opacity; - c->opacity_animation.time_started = get_now_in_ms(); - memcpy(c->opacity_animation.initial_border_color, - c->opacity_animation.current_border_color, - sizeof(c->opacity_animation.initial_border_color)); - c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity; - - c->opacity_animation.running = true; -} - -void client_set_unfocused_opacity_animation(Client *c) { - // Start border color animation to unfocused - float *border_color = get_border_color(c); - - if (!animations) { - setborder_color(c); - return; - } - - c->opacity_animation.duration = animation_duration_focus; - memcpy(c->opacity_animation.target_border_color, border_color, - sizeof(c->opacity_animation.target_border_color)); - // Start opacity animation to unfocused - c->opacity_animation.target_opacity = c->unfocused_opacity; - c->opacity_animation.time_started = get_now_in_ms(); - - memcpy(c->opacity_animation.initial_border_color, - c->opacity_animation.current_border_color, - sizeof(c->opacity_animation.initial_border_color)); - c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity; - - c->opacity_animation.running = true; -} - -bool client_apply_focus_opacity(Client *c) { - // Animate focus transitions (opacity + border color) - float *border_color = get_border_color(c); - if (c->isfullscreen) { - c->opacity_animation.running = false; - client_set_opacity(c, 1); - } else if (c->animation.running && c->animation.action == OPEN) { - c->opacity_animation.running = false; - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; - double linear_progress = - c->animation.duration - ? (double)passed_time / (double)c->animation.duration - : 1.0; - - double opacity_eased_progress = - find_animation_curve_at(linear_progress, OPAFADEIN); - - float percent = - animation_fade_in && !c->nofadein ? opacity_eased_progress : 1.0; - float opacity = - c == server.selmon->sel ? c->focused_opacity : c->unfocused_opacity; - - float target_opacity = - percent * (1.0 - fadein_begin_opacity) + fadein_begin_opacity; - if (target_opacity > opacity) { - target_opacity = opacity; - } - memcpy(c->opacity_animation.current_border_color, - c->opacity_animation.target_border_color, - sizeof(c->opacity_animation.current_border_color)); - c->opacity_animation.current_opacity = target_opacity; - client_set_opacity(c, target_opacity); - client_set_border_color(c, c->opacity_animation.target_border_color); - } else if (animations && c->opacity_animation.running) { - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - int32_t passed_time = - timespec_to_ms(&now) - c->opacity_animation.time_started; - double linear_progress = - c->opacity_animation.duration - ? (double)passed_time / (double)c->opacity_animation.duration - : 1.0; - - float eased_progress = find_animation_curve_at(linear_progress, FOCUS); - - c->opacity_animation.current_opacity = - c->opacity_animation.initial_opacity + - (c->opacity_animation.target_opacity - - c->opacity_animation.initial_opacity) * - eased_progress; - client_set_opacity(c, c->opacity_animation.current_opacity); - - // Animate border color - for (int32_t i = 0; i < 4; i++) { - c->opacity_animation.current_border_color[i] = - c->opacity_animation.initial_border_color[i] + - (c->opacity_animation.target_border_color[i] - - c->opacity_animation.initial_border_color[i]) * - eased_progress; - } - client_set_border_color(c, c->opacity_animation.current_border_color); - if (linear_progress >= 1.0f) { - c->opacity_animation.running = false; - } else { - return true; - } - } else if (c == server.selmon->sel) { - c->opacity_animation.running = false; - c->opacity_animation.current_opacity = c->focused_opacity; - memcpy(c->opacity_animation.current_border_color, border_color, - sizeof(c->opacity_animation.current_border_color)); - client_set_opacity(c, c->focused_opacity); - } else { - c->opacity_animation.running = false; - c->opacity_animation.current_opacity = c->unfocused_opacity; - memcpy(c->opacity_animation.current_border_color, border_color, - sizeof(c->opacity_animation.current_border_color)); - client_set_opacity(c, c->unfocused_opacity); - } - - return false; -} - -bool client_draw_frame(Client *c) { - - if (!c || !client_surface(c)->mapped) - return false; - - if (!c->need_output_flush) { - return client_apply_focus_opacity(c); - } - - if (animations && c->animation.running) { - client_animation_next_tick(c); - } else { - wlr_scene_node_set_position(&c->scene->node, c->pending.x, - c->pending.y); - c->animation.current = c->animainit_geom = c->animation.initial = - c->pending = c->current = c->geom; - client_apply_clip(c, 1.0); - c->need_output_flush = false; - } - client_apply_focus_opacity(c); - return true; -} +#ifndef MANGO_ANIMATION_CLIENT_H +#define MANGO_ANIMATION_CLIENT_H + +// 函数声明 +void client_actual_size(Client *c, int32_t *width, int32_t *height); +void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height); +enum corner_location set_client_corner_location(Client *c); +bool is_horizontal_stack_layout(Monitor *m); +bool is_horizontal_right_stack_layout(Monitor *m); +int32_t is_special_animaiton_rule(Client *c); +void set_client_open_animaiton(Client *c, struct wlr_box geo); +void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, int32_t sy, void *data); +void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, int32_t sy, void *data); +void buffer_set_effect(Client *c, BufferData data); +void client_draw_shadow(Client *c); +void apply_border(Client *c); +struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box); +void client_apply_clip(Client *c, float factor); +void fadeout_client_animation_next_tick(Client *c); +void client_animation_next_tick(Client *c); +void init_fadeout_client(Client *c); +void client_commit(Client *c); +void client_set_pending_state(Client *c); +void resize(Client *c, struct wlr_box geo, int32_t interact); +bool client_draw_fadeout_frame(Client *c); +void client_set_focused_opacity_animation(Client *c); +void client_set_unfocused_opacity_animation(Client *c); +bool client_apply_focus_opacity(Client *c); +bool client_draw_frame(Client *c); + +#endif // CLIENT_DRAW_H \ No newline at end of file diff --git a/src/animation/common.c b/src/animation/common.c new file mode 100644 index 0000000..b9590b2 --- /dev/null +++ b/src/animation/common.c @@ -0,0 +1,270 @@ +#include "../type.h" + +extern Server server; + +struct dvec2 calculate_animation_curve_at(double t, int32_t type) { + struct dvec2 point; + double *animation_curve; + if (type == MOVE) { + animation_curve = animation_curve_move; + } else if (type == OPEN) { + animation_curve = animation_curve_open; + } else if (type == TAG) { + animation_curve = animation_curve_tag; + } else if (type == CLOSE) { + animation_curve = animation_curve_close; + } else if (type == FOCUS) { + animation_curve = animation_curve_focus; + } else if (type == OPAFADEIN) { + animation_curve = animation_curve_opafadein; + } else if (type == OPAFADEOUT) { + animation_curve = animation_curve_opafadeout; + } else { + animation_curve = animation_curve_move; + } + + point.x = 3 * t * (1 - t) * (1 - t) * animation_curve[0] + + 3 * t * t * (1 - t) * animation_curve[2] + t * t * t; + + point.y = 3 * t * (1 - t) * (1 - t) * animation_curve[1] + + 3 * t * t * (1 - t) * animation_curve[3] + t * t * t; + + return point; +} + +void init_baked_points(void) { + server.baked_points_move = + calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_move)); + server.baked_points_open = + calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_open)); + server.baked_points_tag = + calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_tag)); + server.baked_points_close = + calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_close)); + server.baked_points_focus = + calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_focus)); + server.baked_points_opafadein = + calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_opafadein)); + server.baked_points_opafadeout = + calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_opafadeout)); + + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + server.baked_points_move[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), MOVE); + } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + server.baked_points_open[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), OPEN); + } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + server.baked_points_tag[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), TAG); + } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + server.baked_points_close[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), CLOSE); + } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + server.baked_points_focus[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), FOCUS); + } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + server.baked_points_opafadein[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), OPAFADEIN); + } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + server.baked_points_opafadeout[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), OPAFADEOUT); + } +} + +double find_animation_curve_at(double t, int32_t type) { + int32_t down = 0; + int32_t up = BAKED_POINTS_COUNT - 1; + + int32_t middle = (up + down) / 2; + struct dvec2 *baked_points; + if (type == MOVE) { + baked_points = server.baked_points_move; + } else if (type == OPEN) { + baked_points = server.baked_points_open; + } else if (type == TAG) { + baked_points = server.baked_points_tag; + } else if (type == CLOSE) { + baked_points = server.baked_points_close; + } else if (type == FOCUS) { + baked_points = server.baked_points_focus; + } else if (type == OPAFADEIN) { + baked_points = server.baked_points_opafadein; + } else if (type == OPAFADEOUT) { + baked_points = server.baked_points_opafadeout; + } else { + baked_points = server.baked_points_move; + } + + while (up - down != 1) { + if (baked_points[middle].x <= t) { + down = middle; + } else { + up = middle; + } + middle = (up + down) / 2; + } + return baked_points[up].y; +} + +bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx, + int32_t ly, + struct wlr_scene_tree *snapshot_tree) { + if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) { + return true; + } + + lx += node->x; + ly += node->y; + + struct wlr_scene_node *snapshot_node = NULL; + switch (node->type) { + case WLR_SCENE_NODE_TREE: { + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + scene_node_snapshot(child, lx, ly, snapshot_tree); + } + break; + } + case WLR_SCENE_NODE_RECT: { + // struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); + + // struct wlr_scene_rect *snapshot_rect = + // wlr_scene_rect_create(snapshot_tree, scene_rect->width, + // scene_rect->height, scene_rect->color); + // snapshot_rect->node.data = scene_rect->node.data; + // if (snapshot_rect == NULL) { + // return false; + // } + + // wlr_scene_rect_set_clipped_region(scene_rect, + // snapshot_rect->clipped_region); + // wlr_scene_rect_set_backdrop_blur(scene_rect, false); + // wlr_scene_rect_set_backdrop_blur_optimized( + // scene_rect, snapshot_rect->backdrop_blur_optimized); + // wlr_scene_rect_set_corner_radius( + // scene_rect, snapshot_rect->corner_radius, snapshot_rect->corners); + // wlr_scene_rect_set_color(scene_rect, snapshot_rect->color); + + // snapshot_node = &snapshot_rect->node; + break; + } + case WLR_SCENE_NODE_BUFFER: { + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(node); + + struct wlr_scene_buffer *snapshot_buffer = + wlr_scene_buffer_create(snapshot_tree, NULL); + if (snapshot_buffer == NULL) { + return false; + } + snapshot_node = &snapshot_buffer->node; + snapshot_buffer->node.data = scene_buffer->node.data; + + wlr_scene_buffer_set_dest_size(snapshot_buffer, scene_buffer->dst_width, + scene_buffer->dst_height); + wlr_scene_buffer_set_opaque_region(snapshot_buffer, + &scene_buffer->opaque_region); + wlr_scene_buffer_set_source_box(snapshot_buffer, + &scene_buffer->src_box); + wlr_scene_buffer_set_transform(snapshot_buffer, + scene_buffer->transform); + wlr_scene_buffer_set_filter_mode(snapshot_buffer, + scene_buffer->filter_mode); + + // Effects + wlr_scene_buffer_set_opacity(snapshot_buffer, scene_buffer->opacity); + wlr_scene_buffer_set_corner_radius(snapshot_buffer, + scene_buffer->corner_radius, + scene_buffer->corners); + + // wlr_scene_buffer_set_backdrop_blur_optimized( + // snapshot_buffer, scene_buffer->backdrop_blur_optimized); + // wlr_scene_buffer_set_backdrop_blur_ignore_transparent( + // snapshot_buffer, scene_buffer->backdrop_blur_ignore_transparent); + wlr_scene_buffer_set_backdrop_blur(snapshot_buffer, false); + + snapshot_buffer->node.data = scene_buffer->node.data; + + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + if (scene_surface != NULL && scene_surface->surface->buffer != NULL) { + wlr_scene_buffer_set_buffer(snapshot_buffer, + &scene_surface->surface->buffer->base); + } else { + wlr_scene_buffer_set_buffer(snapshot_buffer, scene_buffer->buffer); + } + break; + } + case WLR_SCENE_NODE_SHADOW: { + struct wlr_scene_shadow *scene_shadow = + wlr_scene_shadow_from_node(node); + + struct wlr_scene_shadow *snapshot_shadow = wlr_scene_shadow_create( + snapshot_tree, scene_shadow->width, scene_shadow->height, + scene_shadow->corner_radius, scene_shadow->blur_sigma, + scene_shadow->color); + if (snapshot_shadow == NULL) { + return false; + } + snapshot_node = &snapshot_shadow->node; + + wlr_scene_shadow_set_clipped_region(snapshot_shadow, + scene_shadow->clipped_region); + + snapshot_shadow->node.data = scene_shadow->node.data; + + wlr_scene_node_set_enabled(&snapshot_shadow->node, false); + + break; + } + case WLR_SCENE_NODE_OPTIMIZED_BLUR: + return true; + } + + if (snapshot_node != NULL) { + wlr_scene_node_set_position(snapshot_node, lx, ly); + } + + return true; +} + +struct wlr_scene_tree *wlr_scene_tree_snapshot(struct wlr_scene_node *node, + struct wlr_scene_tree *parent) { + struct wlr_scene_tree *snapshot = wlr_scene_tree_create(parent); + if (snapshot == NULL) { + return NULL; + } + + // Disable and enable the snapshot tree like so to atomically update + // the server.scene -graph. This will prevent over-damaging or other + // weirdness. + wlr_scene_node_set_enabled(&snapshot->node, false); + + if (!scene_node_snapshot(node, 0, 0, snapshot)) { + wlr_scene_node_destroy(&snapshot->node); + return NULL; + } + + wlr_scene_node_set_enabled(&snapshot->node, true); + + return snapshot; +} + +void request_fresh_all_monitors(void) { + Monitor *m = NULL; + wl_list_for_each(m, &server.mons, link) { + if (!m->wlr_output->enabled) { + continue; + } + wlr_output_schedule_frame(m->wlr_output); + } +} \ No newline at end of file diff --git a/src/animation/common.h b/src/animation/common.h index e38b25f..4bd38f8 100644 --- a/src/animation/common.h +++ b/src/animation/common.h @@ -1,266 +1,16 @@ -struct dvec2 calculate_animation_curve_at(double t, int32_t type) { - struct dvec2 point; - double *animation_curve; - if (type == MOVE) { - animation_curve = animation_curve_move; - } else if (type == OPEN) { - animation_curve = animation_curve_open; - } else if (type == TAG) { - animation_curve = animation_curve_tag; - } else if (type == CLOSE) { - animation_curve = animation_curve_close; - } else if (type == FOCUS) { - animation_curve = animation_curve_focus; - } else if (type == OPAFADEIN) { - animation_curve = animation_curve_opafadein; - } else if (type == OPAFADEOUT) { - animation_curve = animation_curve_opafadeout; - } else { - animation_curve = animation_curve_move; - } +#ifndef MANGO_ANIMATION_COMMON_H +#define MANGO_ANIMATION_COMMON_H - point.x = 3 * t * (1 - t) * (1 - t) * animation_curve[0] + - 3 * t * t * (1 - t) * animation_curve[2] + t * t * t; +// 动画曲线相关函数 +struct dvec2 calculate_animation_curve_at(double t, int32_t type); +void init_baked_points(void); +double find_animation_curve_at(double t, int32_t type); - point.y = 3 * t * (1 - t) * (1 - t) * animation_curve[1] + - 3 * t * t * (1 - t) * animation_curve[3] + t * t * t; +// 场景快照函数 +bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx, int32_t ly, struct wlr_scene_tree *snapshot_tree); +struct wlr_scene_tree *wlr_scene_tree_snapshot(struct wlr_scene_node *node, struct wlr_scene_tree *parent); - return point; -} +// 屏幕刷新函数 +void request_fresh_all_monitors(void); -void init_baked_points(void) { - server.baked_points_move = - calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_move)); - server.baked_points_open = - calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_open)); - server.baked_points_tag = - calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_tag)); - server.baked_points_close = - calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_close)); - server.baked_points_focus = - calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_focus)); - server.baked_points_opafadein = - calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_opafadein)); - server.baked_points_opafadeout = - calloc(BAKED_POINTS_COUNT, sizeof(*server.baked_points_opafadeout)); - - for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { - server.baked_points_move[i] = calculate_animation_curve_at( - (double)i / (BAKED_POINTS_COUNT - 1), MOVE); - } - for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { - server.baked_points_open[i] = calculate_animation_curve_at( - (double)i / (BAKED_POINTS_COUNT - 1), OPEN); - } - for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { - server.baked_points_tag[i] = calculate_animation_curve_at( - (double)i / (BAKED_POINTS_COUNT - 1), TAG); - } - for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { - server.baked_points_close[i] = calculate_animation_curve_at( - (double)i / (BAKED_POINTS_COUNT - 1), CLOSE); - } - for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { - server.baked_points_focus[i] = calculate_animation_curve_at( - (double)i / (BAKED_POINTS_COUNT - 1), FOCUS); - } - for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { - server.baked_points_opafadein[i] = calculate_animation_curve_at( - (double)i / (BAKED_POINTS_COUNT - 1), OPAFADEIN); - } - for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { - server.baked_points_opafadeout[i] = calculate_animation_curve_at( - (double)i / (BAKED_POINTS_COUNT - 1), OPAFADEOUT); - } -} - -double find_animation_curve_at(double t, int32_t type) { - int32_t down = 0; - int32_t up = BAKED_POINTS_COUNT - 1; - - int32_t middle = (up + down) / 2; - struct dvec2 *baked_points; - if (type == MOVE) { - baked_points = server.baked_points_move; - } else if (type == OPEN) { - baked_points = server.baked_points_open; - } else if (type == TAG) { - baked_points = server.baked_points_tag; - } else if (type == CLOSE) { - baked_points = server.baked_points_close; - } else if (type == FOCUS) { - baked_points = server.baked_points_focus; - } else if (type == OPAFADEIN) { - baked_points = server.baked_points_opafadein; - } else if (type == OPAFADEOUT) { - baked_points = server.baked_points_opafadeout; - } else { - baked_points = server.baked_points_move; - } - - while (up - down != 1) { - if (baked_points[middle].x <= t) { - down = middle; - } else { - up = middle; - } - middle = (up + down) / 2; - } - return baked_points[up].y; -} - -static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx, - int32_t ly, - struct wlr_scene_tree *snapshot_tree) { - if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) { - return true; - } - - lx += node->x; - ly += node->y; - - struct wlr_scene_node *snapshot_node = NULL; - switch (node->type) { - case WLR_SCENE_NODE_TREE: { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_snapshot(child, lx, ly, snapshot_tree); - } - break; - } - case WLR_SCENE_NODE_RECT: { - // struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); - - // struct wlr_scene_rect *snapshot_rect = - // wlr_scene_rect_create(snapshot_tree, scene_rect->width, - // scene_rect->height, scene_rect->color); - // snapshot_rect->node.data = scene_rect->node.data; - // if (snapshot_rect == NULL) { - // return false; - // } - - // wlr_scene_rect_set_clipped_region(scene_rect, - // snapshot_rect->clipped_region); - // wlr_scene_rect_set_backdrop_blur(scene_rect, false); - // wlr_scene_rect_set_backdrop_blur_optimized( - // scene_rect, snapshot_rect->backdrop_blur_optimized); - // wlr_scene_rect_set_corner_radius( - // scene_rect, snapshot_rect->corner_radius, snapshot_rect->corners); - // wlr_scene_rect_set_color(scene_rect, snapshot_rect->color); - - // snapshot_node = &snapshot_rect->node; - break; - } - case WLR_SCENE_NODE_BUFFER: { - struct wlr_scene_buffer *scene_buffer = - wlr_scene_buffer_from_node(node); - - struct wlr_scene_buffer *snapshot_buffer = - wlr_scene_buffer_create(snapshot_tree, NULL); - if (snapshot_buffer == NULL) { - return false; - } - snapshot_node = &snapshot_buffer->node; - snapshot_buffer->node.data = scene_buffer->node.data; - - wlr_scene_buffer_set_dest_size(snapshot_buffer, scene_buffer->dst_width, - scene_buffer->dst_height); - wlr_scene_buffer_set_opaque_region(snapshot_buffer, - &scene_buffer->opaque_region); - wlr_scene_buffer_set_source_box(snapshot_buffer, - &scene_buffer->src_box); - wlr_scene_buffer_set_transform(snapshot_buffer, - scene_buffer->transform); - wlr_scene_buffer_set_filter_mode(snapshot_buffer, - scene_buffer->filter_mode); - - // Effects - wlr_scene_buffer_set_opacity(snapshot_buffer, scene_buffer->opacity); - wlr_scene_buffer_set_corner_radius(snapshot_buffer, - scene_buffer->corner_radius, - scene_buffer->corners); - - // wlr_scene_buffer_set_backdrop_blur_optimized( - // snapshot_buffer, scene_buffer->backdrop_blur_optimized); - // wlr_scene_buffer_set_backdrop_blur_ignore_transparent( - // snapshot_buffer, scene_buffer->backdrop_blur_ignore_transparent); - wlr_scene_buffer_set_backdrop_blur(snapshot_buffer, false); - - snapshot_buffer->node.data = scene_buffer->node.data; - - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_try_from_buffer(scene_buffer); - if (scene_surface != NULL && scene_surface->surface->buffer != NULL) { - wlr_scene_buffer_set_buffer(snapshot_buffer, - &scene_surface->surface->buffer->base); - } else { - wlr_scene_buffer_set_buffer(snapshot_buffer, scene_buffer->buffer); - } - break; - } - case WLR_SCENE_NODE_SHADOW: { - struct wlr_scene_shadow *scene_shadow = - wlr_scene_shadow_from_node(node); - - struct wlr_scene_shadow *snapshot_shadow = wlr_scene_shadow_create( - snapshot_tree, scene_shadow->width, scene_shadow->height, - scene_shadow->corner_radius, scene_shadow->blur_sigma, - scene_shadow->color); - if (snapshot_shadow == NULL) { - return false; - } - snapshot_node = &snapshot_shadow->node; - - wlr_scene_shadow_set_clipped_region(snapshot_shadow, - scene_shadow->clipped_region); - - snapshot_shadow->node.data = scene_shadow->node.data; - - wlr_scene_node_set_enabled(&snapshot_shadow->node, false); - - break; - } - case WLR_SCENE_NODE_OPTIMIZED_BLUR: - return true; - } - - if (snapshot_node != NULL) { - wlr_scene_node_set_position(snapshot_node, lx, ly); - } - - return true; -} - -struct wlr_scene_tree *wlr_scene_tree_snapshot(struct wlr_scene_node *node, - struct wlr_scene_tree *parent) { - struct wlr_scene_tree *snapshot = wlr_scene_tree_create(parent); - if (snapshot == NULL) { - return NULL; - } - - // Disable and enable the snapshot tree like so to atomically update - // the server.scene -graph. This will prevent over-damaging or other - // weirdness. - wlr_scene_node_set_enabled(&snapshot->node, false); - - if (!scene_node_snapshot(node, 0, 0, snapshot)) { - wlr_scene_node_destroy(&snapshot->node); - return NULL; - } - - wlr_scene_node_set_enabled(&snapshot->node, true); - - return snapshot; -} - -void request_fresh_all_monitors(void) { - Monitor *m = NULL; - wl_list_for_each(m, &server.mons, link) { - if (!m->wlr_output->enabled) { - continue; - } - wlr_output_schedule_frame(m->wlr_output); - } -} \ No newline at end of file +#endif // MANGO_ANIMATION_COMMON_H \ No newline at end of file diff --git a/src/animation/layer.c b/src/animation/layer.c new file mode 100644 index 0000000..8736cc9 --- /dev/null +++ b/src/animation/layer.c @@ -0,0 +1,590 @@ +#include "../type.h" + +extern Server server; + +void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height) { + struct wlr_box box; + + if (l->animation.running) { + *width = l->animation.current.width; + *height = l->animation.current.height; + } else { + get_layer_target_geometry(l, &box); + *width = box.width; + *height = box.height; + } +} + +void get_layer_area_bound(LayerSurface *l, struct wlr_box *bound) { + const struct wlr_layer_surface_v1_state *state = &l->layer_surface->current; + + if (state->exclusive_zone > 0 || state->exclusive_zone == -1) + *bound = l->mon->m; + else + *bound = l->mon->w; +} + +void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { + + if (!l || !l->mapped) + return; + + const struct wlr_layer_surface_v1_state *state = &l->layer_surface->current; + + // 限制区域 + // waybar一般都是大于0,表示要占用多少区域,所以计算位置也要用全部区域作为基准 + // 如果是-1可能表示独占所有可用空间 + // 如果是0,应该是表示使用exclusive_zone外的可用区域 + struct wlr_box bounds; + if (state->exclusive_zone > 0 || state->exclusive_zone == -1) + bounds = l->mon->m; + else + bounds = l->mon->w; + + // 初始化几何位置 + struct wlr_box box = {.width = state->desired_width, + .height = state->desired_height}; + + // 水平方向定位 + const int32_t both_horiz = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + if (box.width == 0) { + box.x = bounds.x; + } else if ((state->anchor & both_horiz) == both_horiz) { + box.x = bounds.x + ((bounds.width - box.width) / 2); + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { + box.x = bounds.x; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x = bounds.x + (bounds.width - box.width); + } else { + box.x = bounds.x + ((bounds.width - box.width) / 2); + } + + // 垂直方向定位 + const int32_t both_vert = + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + if (box.height == 0) { + box.y = bounds.y; + } else if ((state->anchor & both_vert) == both_vert) { + box.y = bounds.y + ((bounds.height - box.height) / 2); + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { + box.y = bounds.y; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y = bounds.y + (bounds.height - box.height); + } else { + box.y = bounds.y + ((bounds.height - box.height) / 2); + } + + // 应用边距 + if (box.width == 0) { + box.x += state->margin.left; + box.width = bounds.width - (state->margin.left + state->margin.right); + } else { + if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { + box.x += state->margin.left; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x -= state->margin.right; + } + } + + if (box.height == 0) { + box.y += state->margin.top; + box.height = bounds.height - (state->margin.top + state->margin.bottom); + } else { + if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { + box.y += state->margin.top; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y -= state->margin.bottom; + } + } + + target_box->x = box.x; + target_box->y = box.y; + target_box->width = box.width; + target_box->height = box.height; +} + +void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) { + int32_t slide_direction; + int32_t horizontal, horizontal_value; + int32_t vertical, vertical_value; + int32_t center_x, center_y; + + if (!l) + return; + + struct wlr_box usable_area; + get_layer_area_bound(l, &usable_area); + + geo->width = l->geom.width; + geo->height = l->geom.height; + + center_x = l->geom.x + l->geom.width / 2; + center_y = l->geom.y + l->geom.height / 2; + horizontal = + center_x > usable_area.x + usable_area.width / 2 ? RIGHT : LEFT; + horizontal_value = horizontal == LEFT + ? center_x - usable_area.x + : usable_area.x + usable_area.width - center_x; + vertical = center_y > usable_area.y + usable_area.height / 2 ? DOWN : UP; + vertical_value = vertical == UP + ? center_y - l->mon->w.y + : usable_area.y + usable_area.height - center_y; + slide_direction = horizontal_value < vertical_value ? horizontal : vertical; + + switch (slide_direction) { + case UP: + geo->x = l->geom.x; + geo->y = usable_area.y - l->geom.height; + break; + case DOWN: + geo->x = l->geom.x; + geo->y = usable_area.y + usable_area.height; + break; + case LEFT: + geo->x = usable_area.x - l->geom.width; + geo->y = l->geom.y; + break; + case RIGHT: + geo->x = usable_area.x + usable_area.width; + geo->y = l->geom.y; + break; + default: + geo->x = l->geom.x; + geo->y = 0 - l->geom.height; + } +} + +void layer_draw_shadow(LayerSurface *l) { + + if (!l->mapped || !l->shadow) + return; + + if (!shadows || !layer_shadows || l->noshadow) { + wlr_scene_shadow_set_size(l->shadow, 0, 0); + return; + } + + int32_t width, height; + layer_actual_size(l, &width, &height); + + int32_t delta = shadows_size; + + /* we calculate where to clip the shadow */ + struct wlr_box layer_box = { + .x = 0, + .y = 0, + .width = width, + .height = height, + }; + + struct wlr_box shadow_box = { + .x = shadows_position_x, + .y = shadows_position_y, + .width = width + 2 * delta, + .height = height + 2 * delta, + }; + + struct wlr_box intersection_box; + wlr_box_intersection(&intersection_box, &layer_box, &shadow_box); + /* clipped region takes shadow relative coords, so we translate everything + * by its position */ + intersection_box.x -= shadows_position_x; + intersection_box.y -= shadows_position_y; + + struct clipped_region clipped_region = { + .area = intersection_box, + .corner_radius = border_radius, + .corners = border_radius_location_default, + }; + + wlr_scene_node_set_position(&l->shadow->node, shadow_box.x, shadow_box.y); + + wlr_scene_shadow_set_size(l->shadow, shadow_box.width, shadow_box.height); + wlr_scene_shadow_set_clipped_region(l->shadow, clipped_region); +} + +void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, void *data) { + BufferData *buffer_data = (BufferData *)data; + + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(buffer); + + if (scene_surface == NULL) + return; + + struct wlr_surface *surface = scene_surface->surface; + + int32_t surface_width = surface->current.width * buffer_data->width_scale; + int32_t surface_height = + surface->current.height * buffer_data->height_scale; + + if (surface_height > 0 && surface_width > 0) { + wlr_scene_buffer_set_dest_size(buffer, surface_width, surface_height); + } +} + +void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, + void *data) { + BufferData *buffer_data = (BufferData *)data; + wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, + buffer_data->height); +} + +void fadeout_layer_animation_next_tick(LayerSurface *l) { + if (!l) + return; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started; + double animation_passed = + l->animation.duration + ? (double)passed_time / (double)l->animation.duration + : 1.0; + + int32_t type = l->animation.action = l->animation.action; + double factor = find_animation_curve_at(animation_passed, type); + int32_t width = l->animation.initial.width + + (l->current.width - l->animation.initial.width) * factor; + int32_t height = l->animation.initial.height + + (l->current.height - l->animation.initial.height) * factor; + + int32_t x = l->animation.initial.x + + (l->current.x - l->animation.initial.x) * factor; + int32_t y = l->animation.initial.y + + (l->current.y - l->animation.initial.y) * factor; + + wlr_scene_node_set_position(&l->scene->node, x, y); + + BufferData buffer_data; + buffer_data.width = width; + buffer_data.height = height; + + if ((!l->animation_type_close && + strcmp(layer_animation_type_close, "zoom") == 0) || + (l->animation_type_close && + strcmp(l->animation_type_close, "zoom") == 0)) { + wlr_scene_node_for_each_buffer(&l->scene->node, + layer_fadeout_scene_buffer_apply_effect, + &buffer_data); + } + + l->animation.current = (struct wlr_box){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + + double opacity_eased_progress = + find_animation_curve_at(animation_passed, OPAFADEOUT); + + double percent = fadeout_begin_opacity - + (opacity_eased_progress * fadeout_begin_opacity); + + double opacity = MAX(percent, 0.0f); + + if (animation_fade_out) + wlr_scene_node_for_each_buffer(&l->scene->node, + scene_buffer_apply_opacity, &opacity); + + if (animation_passed >= 1.0) { + wl_list_remove(&l->fadeout_link); + wlr_scene_node_destroy(&l->scene->node); + free(l); + l = NULL; + } +} + +void layer_animation_next_tick(LayerSurface *l) { + + if (!l || !l->mapped) + return; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started; + double animation_passed = + l->animation.duration + ? (double)passed_time / (double)l->animation.duration + : 1.0; + + int32_t type = l->animation.action == NONE ? MOVE : l->animation.action; + double factor = find_animation_curve_at(animation_passed, type); + + int32_t width = l->animation.initial.width + + (l->current.width - l->animation.initial.width) * factor; + int32_t height = l->animation.initial.height + + (l->current.height - l->animation.initial.height) * factor; + + int32_t x = l->animation.initial.x + + (l->current.x - l->animation.initial.x) * factor; + int32_t y = l->animation.initial.y + + (l->current.y - l->animation.initial.y) * factor; + + double opacity_eased_progress = + find_animation_curve_at(animation_passed, OPAFADEIN); + + double opacity = + MIN(fadein_begin_opacity + + opacity_eased_progress * (1.0 - fadein_begin_opacity), + 1.0f); + + if (animation_fade_in) + wlr_scene_node_for_each_buffer(&l->scene->node, + scene_buffer_apply_opacity, &opacity); + + wlr_scene_node_set_position(&l->scene->node, x, y); + + BufferData buffer_data; + if (factor == 1.0) { + buffer_data.width_scale = 1.0f; + buffer_data.height_scale = 1.0f; + } else { + buffer_data.width_scale = (float)width / (float)l->current.width; + buffer_data.height_scale = (float)height / (float)l->current.height; + } + + if ((!l->animation_type_open && + strcmp(layer_animation_type_open, "zoom") == 0) || + (l->animation_type_open && + strcmp(l->animation_type_open, "zoom") == 0)) { + wlr_scene_node_for_each_buffer( + &l->scene->node, layer_scene_buffer_apply_effect, &buffer_data); + } + + l->animation.current = (struct wlr_box){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + + if (animation_passed >= 1.0) { + l->animation.running = false; + l->need_output_flush = false; + l->animation.action = MOVE; + } +} + +void init_fadeout_layers(LayerSurface *l) { + + if (!animations || !layer_animations || l->noanim) { + return; + } + + if (!l->mon || !l->scene) + return; + + if ((l->animation_type_close && + strcmp(l->animation_type_close, "none") == 0) || + (!l->animation_type_close && + strcmp(layer_animation_type_close, "none") == 0)) { + return; + } + + if (l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM || + l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) + return; + + LayerSurface *fadeout_layer = ecalloc(1, sizeof(*fadeout_layer)); + + struct wlr_box usable_area; + get_layer_area_bound(l, &usable_area); + + wlr_scene_node_set_enabled(&l->scene->node, true); + fadeout_layer->scene = + wlr_scene_tree_snapshot(&l->scene->node, server.layers[LyrFadeOut]); + wlr_scene_node_set_enabled(&l->scene->node, false); + + if (!fadeout_layer->scene) { + free(fadeout_layer); + return; + } + + fadeout_layer->animation.duration = animation_duration_close; + fadeout_layer->geom = fadeout_layer->current = + fadeout_layer->animainit_geom = fadeout_layer->animation.initial = + l->animation.current; + fadeout_layer->mon = l->mon; + fadeout_layer->animation.action = CLOSE; + fadeout_layer->animation_type_close = l->animation_type_close; + fadeout_layer->animation_type_open = l->animation_type_open; + + // 这里snap节点的坐标设置是使用的相对坐标,不能用绝对坐标 + // 这跟普通node有区别 + + fadeout_layer->animation.initial.x = 0; + fadeout_layer->animation.initial.y = 0; + + if ((!l->animation_type_close && + strcmp(layer_animation_type_close, "zoom") == 0) || + (l->animation_type_close && + strcmp(l->animation_type_close, "zoom") == 0)) { + // 算出要设置的绝对坐标和大小 + fadeout_layer->current.width = + (float)l->animation.current.width * zoom_end_ratio; + fadeout_layer->current.height = + (float)l->animation.current.height * zoom_end_ratio; + fadeout_layer->current.x = usable_area.x + usable_area.width / 2 - + fadeout_layer->current.width / 2; + fadeout_layer->current.y = usable_area.y + usable_area.height / 2 - + fadeout_layer->current.height / 2; + // 算出偏差坐标,大小不用因为后续不使用他的大小偏差去设置,而是直接缩放buffer + fadeout_layer->current.x = + fadeout_layer->current.x - l->animation.current.x; + fadeout_layer->current.y = + fadeout_layer->current.y - l->animation.current.y; + + } else if ((!l->animation_type_close && + strcmp(layer_animation_type_close, "slide") == 0) || + (l->animation_type_close && + strcmp(l->animation_type_close, "slide") == 0)) { + // 获取slide动画的结束绝对坐标和大小 + set_layer_dir_animaiton(l, &fadeout_layer->current); + // 算出也能够有设置的偏差坐标和大小 + fadeout_layer->current.x = fadeout_layer->current.x - l->geom.x; + fadeout_layer->current.y = fadeout_layer->current.y - l->geom.y; + fadeout_layer->current.width = + fadeout_layer->current.width - l->geom.width; + fadeout_layer->current.height = + fadeout_layer->current.height - l->geom.height; + } else { + // fade动画坐标大小不用变 + fadeout_layer->current.x = 0; + fadeout_layer->current.y = 0; + fadeout_layer->current.width = 0; + fadeout_layer->current.height = 0; + } + + // 动画开始时间 + fadeout_layer->animation.time_started = get_now_in_ms(); + + // 将节点插入到关闭动画链表中,屏幕刷新哪里会检查链表中是否有节点可以应用于动画 + wlr_scene_node_set_enabled(&fadeout_layer->scene->node, true); + wl_list_insert(&server.fadeout_layers, &fadeout_layer->fadeout_link); + + // 请求刷新屏幕 + if (l->mon) + wlr_output_schedule_frame(l->mon->wlr_output); +} + +void layer_set_pending_state(LayerSurface *l) { + + if (!l || !l->mapped) + return; + + struct wlr_box usable_area; + get_layer_area_bound(l, &usable_area); + + l->pending = l->geom; + + if (l->animation.action == OPEN && !l->animation.running) { + + if ((!l->animation_type_open && + strcmp(layer_animation_type_open, "zoom") == 0) || + (l->animation_type_open && + strcmp(l->animation_type_open, "zoom") == 0)) { + l->animainit_geom.width = l->geom.width * zoom_initial_ratio; + l->animainit_geom.height = l->geom.height * zoom_initial_ratio; + l->animainit_geom.x = usable_area.x + usable_area.width / 2 - + l->animainit_geom.width / 2; + l->animainit_geom.y = usable_area.y + usable_area.height / 2 - + l->animainit_geom.height / 2; + } else if ((!l->animation_type_open && + strcmp(layer_animation_type_open, "slide") == 0) || + (l->animation_type_open && + strcmp(l->animation_type_open, "slide") == 0)) { + + set_layer_dir_animaiton(l, &l->animainit_geom); + } else { + l->animainit_geom.x = l->geom.x; + l->animainit_geom.y = l->geom.y; + l->animainit_geom.width = l->geom.width; + l->animainit_geom.height = l->geom.height; + } + } else { + l->animainit_geom = l->animation.current; + } + // 判断是否需要动画 + if (!animations || !layer_animations || l->noanim || + l->layer_surface->current.layer == + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || + l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { + l->animation.should_animate = false; + } else { + l->animation.should_animate = true; + } + + if (((l->animation_type_open && + strcmp(l->animation_type_open, "none") == 0) || + (!l->animation_type_open && + strcmp(layer_animation_type_open, "none") == 0)) && + l->animation.action == OPEN) { + l->animation.should_animate = false; + } + + // 开始动画 + layer_commit(l); + l->dirty = true; +} + +void layer_commit(LayerSurface *l) { + + if (!l || !l->mapped) + return; + + l->current = l->pending; // 设置动画的结束位置 + + if (l->animation.should_animate) { + if (!l->animation.running) { + l->animation.current = l->animainit_geom; + } + + l->animation.initial = l->animainit_geom; + l->animation.time_started = get_now_in_ms(); + + // 标记动画开始 + l->animation.running = true; + l->animation.should_animate = false; + } + // 请求刷新屏幕 + if (l->mon) + wlr_output_schedule_frame(l->mon->wlr_output); +} + +bool layer_draw_frame(LayerSurface *l) { + + if (!l || !l->mapped) + return false; + + if (!l->need_output_flush) + return false; + + if (l->layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_TOP && + l->layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { + return false; + } + + if (animations && layer_animations && l->animation.running && !l->noanim) { + layer_animation_next_tick(l); + layer_draw_shadow(l); + } else { + layer_draw_shadow(l); + l->need_output_flush = false; + } + return true; +} + +bool layer_draw_fadeout_frame(LayerSurface *l) { + if (!l) + return false; + + fadeout_layer_animation_next_tick(l); + return true; +} diff --git a/src/animation/layer.h b/src/animation/layer.h index c894aa3..50a5bc3 100644 --- a/src/animation/layer.h +++ b/src/animation/layer.h @@ -1,586 +1,26 @@ -void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height) { - struct wlr_box box; - - if (l->animation.running) { - *width = l->animation.current.width; - *height = l->animation.current.height; - } else { - get_layer_target_geometry(l, &box); - *width = box.width; - *height = box.height; - } -} - -void get_layer_area_bound(LayerSurface *l, struct wlr_box *bound) { - const struct wlr_layer_surface_v1_state *state = &l->layer_surface->current; - - if (state->exclusive_zone > 0 || state->exclusive_zone == -1) - *bound = l->mon->m; - else - *bound = l->mon->w; -} - -void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { - - if (!l || !l->mapped) - return; - - const struct wlr_layer_surface_v1_state *state = &l->layer_surface->current; - - // 限制区域 - // waybar一般都是大于0,表示要占用多少区域,所以计算位置也要用全部区域作为基准 - // 如果是-1可能表示独占所有可用空间 - // 如果是0,应该是表示使用exclusive_zone外的可用区域 - struct wlr_box bounds; - if (state->exclusive_zone > 0 || state->exclusive_zone == -1) - bounds = l->mon->m; - else - bounds = l->mon->w; - - // 初始化几何位置 - struct wlr_box box = {.width = state->desired_width, - .height = state->desired_height}; - - // 水平方向定位 - const int32_t both_horiz = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - if (box.width == 0) { - box.x = bounds.x; - } else if ((state->anchor & both_horiz) == both_horiz) { - box.x = bounds.x + ((bounds.width - box.width) / 2); - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { - box.x = bounds.x; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { - box.x = bounds.x + (bounds.width - box.width); - } else { - box.x = bounds.x + ((bounds.width - box.width) / 2); - } - - // 垂直方向定位 - const int32_t both_vert = - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - if (box.height == 0) { - box.y = bounds.y; - } else if ((state->anchor & both_vert) == both_vert) { - box.y = bounds.y + ((bounds.height - box.height) / 2); - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { - box.y = bounds.y; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { - box.y = bounds.y + (bounds.height - box.height); - } else { - box.y = bounds.y + ((bounds.height - box.height) / 2); - } - - // 应用边距 - if (box.width == 0) { - box.x += state->margin.left; - box.width = bounds.width - (state->margin.left + state->margin.right); - } else { - if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { - box.x += state->margin.left; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { - box.x -= state->margin.right; - } - } - - if (box.height == 0) { - box.y += state->margin.top; - box.height = bounds.height - (state->margin.top + state->margin.bottom); - } else { - if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { - box.y += state->margin.top; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { - box.y -= state->margin.bottom; - } - } - - target_box->x = box.x; - target_box->y = box.y; - target_box->width = box.width; - target_box->height = box.height; -} - -void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) { - int32_t slide_direction; - int32_t horizontal, horizontal_value; - int32_t vertical, vertical_value; - int32_t center_x, center_y; - - if (!l) - return; - - struct wlr_box usable_area; - get_layer_area_bound(l, &usable_area); - - geo->width = l->geom.width; - geo->height = l->geom.height; - - center_x = l->geom.x + l->geom.width / 2; - center_y = l->geom.y + l->geom.height / 2; - horizontal = - center_x > usable_area.x + usable_area.width / 2 ? RIGHT : LEFT; - horizontal_value = horizontal == LEFT - ? center_x - usable_area.x - : usable_area.x + usable_area.width - center_x; - vertical = center_y > usable_area.y + usable_area.height / 2 ? DOWN : UP; - vertical_value = vertical == UP - ? center_y - l->mon->w.y - : usable_area.y + usable_area.height - center_y; - slide_direction = horizontal_value < vertical_value ? horizontal : vertical; - - switch (slide_direction) { - case UP: - geo->x = l->geom.x; - geo->y = usable_area.y - l->geom.height; - break; - case DOWN: - geo->x = l->geom.x; - geo->y = usable_area.y + usable_area.height; - break; - case LEFT: - geo->x = usable_area.x - l->geom.width; - geo->y = l->geom.y; - break; - case RIGHT: - geo->x = usable_area.x + usable_area.width; - geo->y = l->geom.y; - break; - default: - geo->x = l->geom.x; - geo->y = 0 - l->geom.height; - } -} - -void layer_draw_shadow(LayerSurface *l) { - - if (!l->mapped || !l->shadow) - return; - - if (!shadows || !layer_shadows || l->noshadow) { - wlr_scene_shadow_set_size(l->shadow, 0, 0); - return; - } - - int32_t width, height; - layer_actual_size(l, &width, &height); - - int32_t delta = shadows_size; - - /* we calculate where to clip the shadow */ - struct wlr_box layer_box = { - .x = 0, - .y = 0, - .width = width, - .height = height, - }; - - struct wlr_box shadow_box = { - .x = shadows_position_x, - .y = shadows_position_y, - .width = width + 2 * delta, - .height = height + 2 * delta, - }; - - struct wlr_box intersection_box; - wlr_box_intersection(&intersection_box, &layer_box, &shadow_box); - /* clipped region takes shadow relative coords, so we translate everything - * by its position */ - intersection_box.x -= shadows_position_x; - intersection_box.y -= shadows_position_y; - - struct clipped_region clipped_region = { - .area = intersection_box, - .corner_radius = border_radius, - .corners = border_radius_location_default, - }; - - wlr_scene_node_set_position(&l->shadow->node, shadow_box.x, shadow_box.y); - - wlr_scene_shadow_set_size(l->shadow, shadow_box.width, shadow_box.height); - wlr_scene_shadow_set_clipped_region(l->shadow, clipped_region); -} - -void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, - int32_t sx, int32_t sy, void *data) { - BufferData *buffer_data = (BufferData *)data; - - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_try_from_buffer(buffer); - - if (scene_surface == NULL) - return; - - struct wlr_surface *surface = scene_surface->surface; - - int32_t surface_width = surface->current.width * buffer_data->width_scale; - int32_t surface_height = - surface->current.height * buffer_data->height_scale; - - if (surface_height > 0 && surface_width > 0) { - wlr_scene_buffer_set_dest_size(buffer, surface_width, surface_height); - } -} - -void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, - int32_t sx, int32_t sy, - void *data) { - BufferData *buffer_data = (BufferData *)data; - wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, - buffer_data->height); -} - -void fadeout_layer_animation_next_tick(LayerSurface *l) { - if (!l) - return; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started; - double animation_passed = - l->animation.duration - ? (double)passed_time / (double)l->animation.duration - : 1.0; - - int32_t type = l->animation.action = l->animation.action; - double factor = find_animation_curve_at(animation_passed, type); - int32_t width = l->animation.initial.width + - (l->current.width - l->animation.initial.width) * factor; - int32_t height = l->animation.initial.height + - (l->current.height - l->animation.initial.height) * factor; - - int32_t x = l->animation.initial.x + - (l->current.x - l->animation.initial.x) * factor; - int32_t y = l->animation.initial.y + - (l->current.y - l->animation.initial.y) * factor; - - wlr_scene_node_set_position(&l->scene->node, x, y); - - BufferData buffer_data; - buffer_data.width = width; - buffer_data.height = height; - - if ((!l->animation_type_close && - strcmp(layer_animation_type_close, "zoom") == 0) || - (l->animation_type_close && - strcmp(l->animation_type_close, "zoom") == 0)) { - wlr_scene_node_for_each_buffer(&l->scene->node, - layer_fadeout_scene_buffer_apply_effect, - &buffer_data); - } - - l->animation.current = (struct wlr_box){ - .x = x, - .y = y, - .width = width, - .height = height, - }; - - double opacity_eased_progress = - find_animation_curve_at(animation_passed, OPAFADEOUT); - - double percent = fadeout_begin_opacity - - (opacity_eased_progress * fadeout_begin_opacity); - - double opacity = MAX(percent, 0.0f); - - if (animation_fade_out) - wlr_scene_node_for_each_buffer(&l->scene->node, - scene_buffer_apply_opacity, &opacity); - - if (animation_passed >= 1.0) { - wl_list_remove(&l->fadeout_link); - wlr_scene_node_destroy(&l->scene->node); - free(l); - l = NULL; - } -} - -void layer_animation_next_tick(LayerSurface *l) { - - if (!l || !l->mapped) - return; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started; - double animation_passed = - l->animation.duration - ? (double)passed_time / (double)l->animation.duration - : 1.0; - - int32_t type = l->animation.action == NONE ? MOVE : l->animation.action; - double factor = find_animation_curve_at(animation_passed, type); - - int32_t width = l->animation.initial.width + - (l->current.width - l->animation.initial.width) * factor; - int32_t height = l->animation.initial.height + - (l->current.height - l->animation.initial.height) * factor; - - int32_t x = l->animation.initial.x + - (l->current.x - l->animation.initial.x) * factor; - int32_t y = l->animation.initial.y + - (l->current.y - l->animation.initial.y) * factor; - - double opacity_eased_progress = - find_animation_curve_at(animation_passed, OPAFADEIN); - - double opacity = - MIN(fadein_begin_opacity + - opacity_eased_progress * (1.0 - fadein_begin_opacity), - 1.0f); - - if (animation_fade_in) - wlr_scene_node_for_each_buffer(&l->scene->node, - scene_buffer_apply_opacity, &opacity); - - wlr_scene_node_set_position(&l->scene->node, x, y); - - BufferData buffer_data; - if (factor == 1.0) { - buffer_data.width_scale = 1.0f; - buffer_data.height_scale = 1.0f; - } else { - buffer_data.width_scale = (float)width / (float)l->current.width; - buffer_data.height_scale = (float)height / (float)l->current.height; - } - - if ((!l->animation_type_open && - strcmp(layer_animation_type_open, "zoom") == 0) || - (l->animation_type_open && - strcmp(l->animation_type_open, "zoom") == 0)) { - wlr_scene_node_for_each_buffer( - &l->scene->node, layer_scene_buffer_apply_effect, &buffer_data); - } - - l->animation.current = (struct wlr_box){ - .x = x, - .y = y, - .width = width, - .height = height, - }; - - if (animation_passed >= 1.0) { - l->animation.running = false; - l->need_output_flush = false; - l->animation.action = MOVE; - } -} - -void init_fadeout_layers(LayerSurface *l) { - - if (!animations || !layer_animations || l->noanim) { - return; - } - - if (!l->mon || !l->scene) - return; - - if ((l->animation_type_close && - strcmp(l->animation_type_close, "none") == 0) || - (!l->animation_type_close && - strcmp(layer_animation_type_close, "none") == 0)) { - return; - } - - if (l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM || - l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) - return; - - LayerSurface *fadeout_layer = ecalloc(1, sizeof(*fadeout_layer)); - - struct wlr_box usable_area; - get_layer_area_bound(l, &usable_area); - - wlr_scene_node_set_enabled(&l->scene->node, true); - fadeout_layer->scene = - wlr_scene_tree_snapshot(&l->scene->node, server.layers[LyrFadeOut]); - wlr_scene_node_set_enabled(&l->scene->node, false); - - if (!fadeout_layer->scene) { - free(fadeout_layer); - return; - } - - fadeout_layer->animation.duration = animation_duration_close; - fadeout_layer->geom = fadeout_layer->current = - fadeout_layer->animainit_geom = fadeout_layer->animation.initial = - l->animation.current; - fadeout_layer->mon = l->mon; - fadeout_layer->animation.action = CLOSE; - fadeout_layer->animation_type_close = l->animation_type_close; - fadeout_layer->animation_type_open = l->animation_type_open; - - // 这里snap节点的坐标设置是使用的相对坐标,不能用绝对坐标 - // 这跟普通node有区别 - - fadeout_layer->animation.initial.x = 0; - fadeout_layer->animation.initial.y = 0; - - if ((!l->animation_type_close && - strcmp(layer_animation_type_close, "zoom") == 0) || - (l->animation_type_close && - strcmp(l->animation_type_close, "zoom") == 0)) { - // 算出要设置的绝对坐标和大小 - fadeout_layer->current.width = - (float)l->animation.current.width * zoom_end_ratio; - fadeout_layer->current.height = - (float)l->animation.current.height * zoom_end_ratio; - fadeout_layer->current.x = usable_area.x + usable_area.width / 2 - - fadeout_layer->current.width / 2; - fadeout_layer->current.y = usable_area.y + usable_area.height / 2 - - fadeout_layer->current.height / 2; - // 算出偏差坐标,大小不用因为后续不使用他的大小偏差去设置,而是直接缩放buffer - fadeout_layer->current.x = - fadeout_layer->current.x - l->animation.current.x; - fadeout_layer->current.y = - fadeout_layer->current.y - l->animation.current.y; - - } else if ((!l->animation_type_close && - strcmp(layer_animation_type_close, "slide") == 0) || - (l->animation_type_close && - strcmp(l->animation_type_close, "slide") == 0)) { - // 获取slide动画的结束绝对坐标和大小 - set_layer_dir_animaiton(l, &fadeout_layer->current); - // 算出也能够有设置的偏差坐标和大小 - fadeout_layer->current.x = fadeout_layer->current.x - l->geom.x; - fadeout_layer->current.y = fadeout_layer->current.y - l->geom.y; - fadeout_layer->current.width = - fadeout_layer->current.width - l->geom.width; - fadeout_layer->current.height = - fadeout_layer->current.height - l->geom.height; - } else { - // fade动画坐标大小不用变 - fadeout_layer->current.x = 0; - fadeout_layer->current.y = 0; - fadeout_layer->current.width = 0; - fadeout_layer->current.height = 0; - } - - // 动画开始时间 - fadeout_layer->animation.time_started = get_now_in_ms(); - - // 将节点插入到关闭动画链表中,屏幕刷新哪里会检查链表中是否有节点可以应用于动画 - wlr_scene_node_set_enabled(&fadeout_layer->scene->node, true); - wl_list_insert(&server.fadeout_layers, &fadeout_layer->fadeout_link); - - // 请求刷新屏幕 - if (l->mon) - wlr_output_schedule_frame(l->mon->wlr_output); -} - -void layer_set_pending_state(LayerSurface *l) { - - if (!l || !l->mapped) - return; - - struct wlr_box usable_area; - get_layer_area_bound(l, &usable_area); - - l->pending = l->geom; - - if (l->animation.action == OPEN && !l->animation.running) { - - if ((!l->animation_type_open && - strcmp(layer_animation_type_open, "zoom") == 0) || - (l->animation_type_open && - strcmp(l->animation_type_open, "zoom") == 0)) { - l->animainit_geom.width = l->geom.width * zoom_initial_ratio; - l->animainit_geom.height = l->geom.height * zoom_initial_ratio; - l->animainit_geom.x = usable_area.x + usable_area.width / 2 - - l->animainit_geom.width / 2; - l->animainit_geom.y = usable_area.y + usable_area.height / 2 - - l->animainit_geom.height / 2; - } else if ((!l->animation_type_open && - strcmp(layer_animation_type_open, "slide") == 0) || - (l->animation_type_open && - strcmp(l->animation_type_open, "slide") == 0)) { - - set_layer_dir_animaiton(l, &l->animainit_geom); - } else { - l->animainit_geom.x = l->geom.x; - l->animainit_geom.y = l->geom.y; - l->animainit_geom.width = l->geom.width; - l->animainit_geom.height = l->geom.height; - } - } else { - l->animainit_geom = l->animation.current; - } - // 判断是否需要动画 - if (!animations || !layer_animations || l->noanim || - l->layer_surface->current.layer == - ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || - l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { - l->animation.should_animate = false; - } else { - l->animation.should_animate = true; - } - - if (((l->animation_type_open && - strcmp(l->animation_type_open, "none") == 0) || - (!l->animation_type_open && - strcmp(layer_animation_type_open, "none") == 0)) && - l->animation.action == OPEN) { - l->animation.should_animate = false; - } - - // 开始动画 - layer_commit(l); - l->dirty = true; -} - -void layer_commit(LayerSurface *l) { - - if (!l || !l->mapped) - return; - - l->current = l->pending; // 设置动画的结束位置 - - if (l->animation.should_animate) { - if (!l->animation.running) { - l->animation.current = l->animainit_geom; - } - - l->animation.initial = l->animainit_geom; - l->animation.time_started = get_now_in_ms(); - - // 标记动画开始 - l->animation.running = true; - l->animation.should_animate = false; - } - // 请求刷新屏幕 - if (l->mon) - wlr_output_schedule_frame(l->mon->wlr_output); -} - -bool layer_draw_frame(LayerSurface *l) { - - if (!l || !l->mapped) - return false; - - if (!l->need_output_flush) - return false; - - if (l->layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_TOP && - l->layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { - return false; - } - - if (animations && layer_animations && l->animation.running && !l->noanim) { - layer_animation_next_tick(l); - layer_draw_shadow(l); - } else { - layer_draw_shadow(l); - l->need_output_flush = false; - } - return true; -} - -bool layer_draw_fadeout_frame(LayerSurface *l) { - if (!l) - return false; - - fadeout_layer_animation_next_tick(l); - return true; -} +#ifndef MANGO_ANIMATION_LAYER_H +#define MANGO_ANIMATION_LAYER_H + +// 图层大小和几何计算函数 +void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height); +void get_layer_area_bound(LayerSurface *l, struct wlr_box *bound); +void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box); +void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo); + +// 图层阴影和视觉效果函数 +void layer_draw_shadow(LayerSurface *l); +void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, int32_t sy, void *data); +void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, int32_t sy, void *data); + +// 图层动画相关函数 +void fadeout_layer_animation_next_tick(LayerSurface *l); +void layer_animation_next_tick(LayerSurface *l); +void init_fadeout_layers(LayerSurface *l); +void layer_set_pending_state(LayerSurface *l); +void layer_commit(LayerSurface *l); + +// 图层绘制函数 +bool layer_draw_frame(LayerSurface *l); +bool layer_draw_fadeout_frame(LayerSurface *l); + +#endif // MANGO_ANIMATION_LAYER_H \ No newline at end of file diff --git a/src/animation/tag.c b/src/animation/tag.c new file mode 100644 index 0000000..e922d99 --- /dev/null +++ b/src/animation/tag.c @@ -0,0 +1,98 @@ +#include "../type.h" +#include "../client/client.h" + +extern Server server; + +void set_tagin_animation(Monitor *m, Client *c) { + if (c->animation.running) { + c->animainit_geom.x = c->animation.current.x; + c->animainit_geom.y = c->animation.current.y; + return; + } + + if (m->pertag->curtag > m->pertag->prevtag) { + + c->animainit_geom.x = tag_animation_direction == VERTICAL + ? c->animation.current.x + : MAX(c->mon->m.x + c->mon->m.width, + c->geom.x + c->mon->m.width); + c->animainit_geom.y = tag_animation_direction == VERTICAL + ? MAX(c->mon->m.y + c->mon->m.height, + c->geom.y + c->mon->m.height) + : c->animation.current.y; + + } else { + + c->animainit_geom.x = + tag_animation_direction == VERTICAL + ? c->animation.current.x + : MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width); + c->animainit_geom.y = + tag_animation_direction == VERTICAL + ? MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height) + : c->animation.current.y; + } +} + +void set_arrange_visible(Monitor *m, Client *c, bool want_animation) { + + if (!c->is_clip_to_hide || !ISTILED(c) || !is_scroller_layout(c->mon)) { + c->is_clip_to_hide = false; + wlr_scene_node_set_enabled(&c->scene->node, true); + wlr_scene_node_set_enabled(&c->scene_surface->node, true); + } + client_set_suspended(c, false); + + if (!c->animation.tag_from_rule && want_animation && + m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) { + c->animation.tagining = true; + set_tagin_animation(m, c); + } else { + c->animainit_geom.x = c->animation.current.x; + c->animainit_geom.y = c->animation.current.y; + } + + c->animation.tag_from_rule = false; + c->animation.tagouting = false; + c->animation.tagouted = false; + resize(c, c->geom, 0); +} + +void set_tagout_animation(Monitor *m, Client *c) { + if (m->pertag->curtag > m->pertag->prevtag) { + c->pending = c->geom; + c->pending.x = + tag_animation_direction == VERTICAL + ? c->animation.current.x + : MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width); + c->pending.y = tag_animation_direction == VERTICAL + ? MIN(c->mon->m.y - c->geom.height, + c->geom.y - c->mon->m.height) + : c->animation.current.y; + + resize(c, c->geom, 0); + } else { + c->pending = c->geom; + c->pending.x = tag_animation_direction == VERTICAL + ? c->animation.current.x + : MAX(c->mon->m.x + c->mon->m.width, + c->geom.x + c->mon->m.width); + c->pending.y = tag_animation_direction == VERTICAL + ? MAX(c->mon->m.y + c->mon->m.height, + c->geom.y + c->mon->m.height) + : c->animation.current.y; + resize(c, c->geom, 0); + } +} + +void set_arrange_hidden(Monitor *m, Client *c, bool want_animation) { + if ((c->tags & (1 << (m->pertag->prevtag - 1))) && + m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) { + c->animation.tagouting = true; + c->animation.tagining = false; + set_tagout_animation(m, c); + } else { + wlr_scene_node_set_enabled(&c->scene->node, false); + client_set_suspended(c, true); + } +} diff --git a/src/animation/tag.h b/src/animation/tag.h index 8e65a93..077431b 100644 --- a/src/animation/tag.h +++ b/src/animation/tag.h @@ -1,93 +1,10 @@ -void set_tagin_animation(Monitor *m, Client *c) { - if (c->animation.running) { - c->animainit_geom.x = c->animation.current.x; - c->animainit_geom.y = c->animation.current.y; - return; - } +#ifndef MANGO_ANIMATION_TAG_H +#define MANGO_ANIMATION_TAG_H - if (m->pertag->curtag > m->pertag->prevtag) { +// 标签切换动画函数 +void set_tagin_animation(Monitor *m, Client *c); +void set_arrange_visible(Monitor *m, Client *c, bool want_animation); +void set_tagout_animation(Monitor *m, Client *c); +void set_arrange_hidden(Monitor *m, Client *c, bool want_animation); - c->animainit_geom.x = tag_animation_direction == VERTICAL - ? c->animation.current.x - : MAX(c->mon->m.x + c->mon->m.width, - c->geom.x + c->mon->m.width); - c->animainit_geom.y = tag_animation_direction == VERTICAL - ? MAX(c->mon->m.y + c->mon->m.height, - c->geom.y + c->mon->m.height) - : c->animation.current.y; - - } else { - - c->animainit_geom.x = - tag_animation_direction == VERTICAL - ? c->animation.current.x - : MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width); - c->animainit_geom.y = - tag_animation_direction == VERTICAL - ? MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height) - : c->animation.current.y; - } -} - -void set_arrange_visible(Monitor *m, Client *c, bool want_animation) { - - if (!c->is_clip_to_hide || !ISTILED(c) || !is_scroller_layout(c->mon)) { - c->is_clip_to_hide = false; - wlr_scene_node_set_enabled(&c->scene->node, true); - wlr_scene_node_set_enabled(&c->scene_surface->node, true); - } - client_set_suspended(c, false); - - if (!c->animation.tag_from_rule && want_animation && - m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) { - c->animation.tagining = true; - set_tagin_animation(m, c); - } else { - c->animainit_geom.x = c->animation.current.x; - c->animainit_geom.y = c->animation.current.y; - } - - c->animation.tag_from_rule = false; - c->animation.tagouting = false; - c->animation.tagouted = false; - resize(c, c->geom, 0); -} - -void set_tagout_animation(Monitor *m, Client *c) { - if (m->pertag->curtag > m->pertag->prevtag) { - c->pending = c->geom; - c->pending.x = - tag_animation_direction == VERTICAL - ? c->animation.current.x - : MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width); - c->pending.y = tag_animation_direction == VERTICAL - ? MIN(c->mon->m.y - c->geom.height, - c->geom.y - c->mon->m.height) - : c->animation.current.y; - - resize(c, c->geom, 0); - } else { - c->pending = c->geom; - c->pending.x = tag_animation_direction == VERTICAL - ? c->animation.current.x - : MAX(c->mon->m.x + c->mon->m.width, - c->geom.x + c->mon->m.width); - c->pending.y = tag_animation_direction == VERTICAL - ? MAX(c->mon->m.y + c->mon->m.height, - c->geom.y + c->mon->m.height) - : c->animation.current.y; - resize(c, c->geom, 0); - } -} - -void set_arrange_hidden(Monitor *m, Client *c, bool want_animation) { - if ((c->tags & (1 << (m->pertag->prevtag - 1))) && - m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) { - c->animation.tagouting = true; - c->animation.tagining = false; - set_tagout_animation(m, c); - } else { - wlr_scene_node_set_enabled(&c->scene->node, false); - client_set_suspended(c, true); - } -} +#endif // MANGO_ANIMATION_TAG_H \ No newline at end of file diff --git a/src/client/client.c b/src/client/client.c new file mode 100644 index 0000000..30bb00f --- /dev/null +++ b/src/client/client.c @@ -0,0 +1,546 @@ +#include "../type.h" + +extern Server server; + +int32_t client_is_x11(Client *c) { +#ifdef XWAYLAND + return c->type == X11; +#endif + return 0; +} + +struct wlr_surface *client_surface(Client *c) { +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->surface; +#endif + return c->surface.xdg->surface; +} + +int32_t toplevel_from_wlr_surface(struct wlr_surface *s, + Client **pc, + LayerSurface **pl) { + struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; + struct wlr_surface *root_surface; + struct wlr_layer_surface_v1 *layer_surface; + Client *c = NULL; + LayerSurface *l = NULL; + int32_t type = -1; +#ifdef XWAYLAND + struct wlr_xwayland_surface *xsurface; +#endif + + if (!s) + return -1; + root_surface = wlr_surface_get_root_surface(s); + +#ifdef XWAYLAND + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) { + c = xsurface->data; + type = c->type; + goto end; + } +#endif + + if ((layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) { + l = layer_surface->data; + type = LayerShell; + goto end; + } + + xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface); + while (xdg_surface) { + tmp_xdg_surface = NULL; + switch (xdg_surface->role) { + case WLR_XDG_SURFACE_ROLE_POPUP: + if (!xdg_surface->popup || !xdg_surface->popup->parent) + return -1; + + tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface( + xdg_surface->popup->parent); + + if (!tmp_xdg_surface) + return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, + pl); + + xdg_surface = tmp_xdg_surface; + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + c = xdg_surface->data; + type = c->type; + goto end; + case WLR_XDG_SURFACE_ROLE_NONE: + return -1; + } + } + +end: + if (pl) + *pl = l; + if (pc) + *pc = c; + return type; +} + +/* The others */ +void client_activate_surface(struct wlr_surface *s, + int32_t activated) { + struct wlr_xdg_toplevel *toplevel; +#ifdef XWAYLAND + struct wlr_xwayland_surface *xsurface; + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) { + if (activated && xsurface->minimized) + wlr_xwayland_surface_set_minimized(xsurface, false); + wlr_xwayland_surface_activate(xsurface, activated); + return; + } +#endif + if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s))) + wlr_xdg_toplevel_set_activated(toplevel, activated); +} + +const char *client_get_appid(Client *c) { +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->class ? c->surface.xwayland->class + : "broken"; +#endif + return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id + : "broken"; +} + +int32_t client_get_pid(Client *c) { + pid_t pid; +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->pid; +#endif + wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); + return pid; +} + +void client_get_clip(Client *c, struct wlr_box *clip) { + *clip = (struct wlr_box){ + .x = 0, + .y = 0, + .width = c->geom.width - 2 * c->bw, + .height = c->geom.height - 2 * c->bw, + }; + +#ifdef XWAYLAND + if (client_is_x11(c)) + return; +#endif + + clip->x = c->surface.xdg->geometry.x; + clip->y = c->surface.xdg->geometry.y; +} + +void client_get_geometry(Client *c, struct wlr_box *geom) { +#ifdef XWAYLAND + if (client_is_x11(c)) { + geom->x = c->surface.xwayland->x; + geom->y = c->surface.xwayland->y; + geom->width = c->surface.xwayland->width; + geom->height = c->surface.xwayland->height; + return; + } +#endif + *geom = c->surface.xdg->geometry; +} + +Client *client_get_parent(Client *c) { + Client *p = NULL; +#ifdef XWAYLAND + if (client_is_x11(c)) { + if (c->surface.xwayland->parent) + toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, + NULL); + return p; + } +#endif + if (c->surface.xdg->toplevel->parent) + toplevel_from_wlr_surface( + c->surface.xdg->toplevel->parent->base->surface, &p, NULL); + return p; +} + +int32_t client_has_children(Client *c) { +#ifdef XWAYLAND + if (client_is_x11(c)) + return !wl_list_empty(&c->surface.xwayland->children); +#endif + /* surface.xdg->link is never empty because it always contains at least the + * surface itself. */ + return wl_list_length(&c->surface.xdg->link) > 1; +} + +const char *client_get_title(Client *c) { +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->title ? c->surface.xwayland->title + : "broken"; +#endif + return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title + : "broken"; +} + +int32_t client_is_float_type(Client *c) { + struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_toplevel_state state; + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + xcb_size_hints_t *size_hints = surface->size_hints; + + if (!size_hints) + return 0; + + if (surface->modal) + return 1; + + if (wlr_xwayland_surface_has_window_type( + surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG) || + wlr_xwayland_surface_has_window_type( + surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH) || + wlr_xwayland_surface_has_window_type( + surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR) || + wlr_xwayland_surface_has_window_type( + surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) { + return 1; + } + + return size_hints && size_hints->min_width > 0 && + size_hints->min_height > 0 && + (size_hints->max_width == size_hints->min_width || + size_hints->max_height == size_hints->min_height); + } +#endif + + toplevel = c->surface.xdg->toplevel; + state = toplevel->current; + return toplevel->parent || (state.min_width != 0 && state.min_height != 0 && + (state.min_width == state.max_width || + state.min_height == state.max_height)); +} + +int32_t client_is_rendered_on_mon(Client *c, Monitor *m) { + /* This is needed for when you don't want to check formal assignment, + * but rather actual displaying of the pixels. + * Usually VISIBLEON suffices and is also faster. */ + struct wlr_surface_output *s; + int32_t unused_lx, unused_ly; + if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) + return 0; + wl_list_for_each(s, &client_surface(c)->current_outputs, + link) if (s->output == m->wlr_output) return 1; + return 0; +} + +int32_t client_is_stopped(Client *c) { + int32_t pid; + siginfo_t in = {0}; +#ifdef XWAYLAND + if (client_is_x11(c)) + return 0; +#endif + + wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); + if (waitid(P_PID, pid, &in, WNOHANG | WCONTINUED | WSTOPPED | WNOWAIT) < + 0) { + /* This process is not our child process, while is very unluckely that + * it is stopped, in order to do not skip frames assume that it is. */ + if (errno == ECHILD) + return 1; + } else if (in.si_pid) { + if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED) + return 1; + if (in.si_code == CLD_CONTINUED) + return 0; + } + + return 0; +} + +int32_t client_is_unmanaged(Client *c) { +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->override_redirect; +#endif + return 0; +} + +void client_notify_enter(struct wlr_surface *s, + struct wlr_keyboard *kb) { + if (kb) + wlr_seat_keyboard_notify_enter(server.seat, s, kb->keycodes, + kb->num_keycodes, &kb->modifiers); + else + wlr_seat_keyboard_notify_enter(server.seat, s, NULL, 0, NULL); +} + +void client_send_close(Client *c) { +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_close(c->surface.xwayland); + return; + } +#endif + wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel); +} + +void client_set_border_color(Client *c, + const float color[4]) { + wlr_scene_rect_set_color(c->border, color); +} + +void client_set_fullscreen(Client *c, int32_t fullscreen) { +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); + return; + } +#endif + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen); +} + +void client_set_scale(struct wlr_surface *s, float scale) { + wlr_fractional_scale_v1_notify_scale(s, scale); + wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale)); +} + +uint32_t client_set_size(Client *c, uint32_t width, + uint32_t height) { +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw, + c->geom.y + c->bw, width, height); + return 0; + } +#endif + if ((int32_t)width == c->surface.xdg->toplevel->current.width && + (int32_t)height == c->surface.xdg->toplevel->current.height) + return 0; + return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, + (int32_t)height); +} + +void client_set_minimized(Client *c, bool minimized) { +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_set_minimized(c->surface.xwayland, minimized); + return; + } +#endif + + return; +} + +void client_set_maximized(Client *c, bool maximized) { + struct wlr_xdg_toplevel *toplevel; + +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_set_maximized(c->surface.xwayland, maximized, + maximized); + return; + } +#endif + toplevel = c->surface.xdg->toplevel; + wlr_xdg_toplevel_set_maximized(toplevel, maximized); + return; +} + +void client_set_tiled(Client *c, uint32_t edges) { + struct wlr_xdg_toplevel *toplevel; +#ifdef XWAYLAND + if (client_is_x11(c) && c->force_maximize) { + wlr_xwayland_surface_set_maximized(c->surface.xwayland, + edges != WLR_EDGE_NONE, + edges != WLR_EDGE_NONE); + return; + } +#endif + + toplevel = c->surface.xdg->toplevel; + + if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >= + XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { + wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); + } + + if (c->force_maximize) { + wlr_xdg_toplevel_set_maximized(toplevel, edges != WLR_EDGE_NONE); + } +} + +void client_set_suspended(Client *c, int32_t suspended) { +#ifdef XWAYLAND + if (client_is_x11(c)) + return; +#endif + + wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); +} + +int32_t client_should_ignore_focus(Client *c) { + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + + if (!surface->hints) + return 0; + + return !surface->hints->input; + } +#endif + return 0; +} + +int32_t client_is_x11_popup(Client *c) { + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + // 处理不需要焦点的窗口类型 + const uint32_t no_focus_types[] = { + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_COMBO, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DND, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_MENU, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NOTIFICATION, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_POPUP_MENU, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLTIP, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY}; + // 检查窗口类型是否需要禁止焦点 + for (size_t i = 0; + i < sizeof(no_focus_types) / sizeof(no_focus_types[0]); ++i) { + if (wlr_xwayland_surface_has_window_type(surface, + no_focus_types[i])) { + return 1; + } + } + } +#endif + return 0; +} + +int32_t client_should_global(Client *c) { + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + + if (surface->sticky) + return 1; + } +#endif + return 0; +} + +int32_t client_should_overtop(Client *c) { + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + if (surface->above) + return 1; + } +#endif + return 0; +} + +int32_t client_wants_focus(Client *c) { +#ifdef XWAYLAND + return client_is_unmanaged(c) && + wlr_xwayland_surface_override_redirect_wants_focus( + c->surface.xwayland) && + wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) != + WLR_ICCCM_INPUT_MODEL_NONE; +#endif + return 0; +} + +int32_t client_wants_fullscreen(Client *c) { +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->fullscreen; +#endif + return c->surface.xdg->toplevel->requested.fullscreen; +} + +bool client_request_minimize(Client *c, void *data) { + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_minimize_event *event = data; + return event->minimize; + } +#endif + + return c->surface.xdg->toplevel->requested.minimized; +} + +bool client_request_maximize(Client *c, void *data) { + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + return surface->maximized_vert || surface->maximized_horz; + } +#endif + + return c->surface.xdg->toplevel->requested.maximized; +} + +void client_set_size_bound(Client *c) { + struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_toplevel_state state; + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + xcb_size_hints_t *size_hints = surface->size_hints; + + if (!size_hints) + return; + + if ((uint32_t)c->geom.width - 2 * c->bw < size_hints->min_width && + size_hints->min_width > 0) + c->geom.width = size_hints->min_width + 2 * c->bw; + if ((uint32_t)c->geom.height - 2 * c->bw < size_hints->min_height && + size_hints->min_height > 0) + c->geom.height = size_hints->min_height + 2 * c->bw; + if ((uint32_t)c->geom.width - 2 * c->bw > size_hints->max_width && + size_hints->max_width > 0) + c->geom.width = size_hints->max_width + 2 * c->bw; + if ((uint32_t)c->geom.height - 2 * c->bw > size_hints->max_height && + size_hints->max_height > 0) + c->geom.height = size_hints->max_height + 2 * c->bw; + return; + } +#endif + + toplevel = c->surface.xdg->toplevel; + state = toplevel->current; + if ((uint32_t)c->geom.width - 2 * c->bw < state.min_width && + state.min_width > 0) { + c->geom.width = state.min_width + 2 * c->bw; + } + if ((uint32_t)c->geom.height - 2 * c->bw < state.min_height && + state.min_height > 0) { + c->geom.height = state.min_height + 2 * c->bw; + } + if ((uint32_t)c->geom.width - 2 * c->bw > state.max_width && + state.max_width > 0) { + c->geom.width = state.max_width + 2 * c->bw; + } + if ((uint32_t)c->geom.height - 2 * c->bw > state.max_height && + state.max_height > 0) { + c->geom.height = state.max_height + 2 * c->bw; + } +} \ No newline at end of file diff --git a/src/client/client.h b/src/client/client.h index f38c582..c50cb15 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -1,550 +1,50 @@ -/* - * Attempt to consolidate unavoidable suck into one file, away from dwl.c. This - * file is not meant to be pretty. We use a .h file with static inline - * functions instead of a separate .c module, or function pointers like sway, so - * that they will simply compile out if the chosen #defines leave them unused. - */ - -/* Leave these functions first; they're used in the others */ -static inline int32_t client_is_x11(Client *c) { -#ifdef XWAYLAND - return c->type == X11; -#endif - return 0; -} - -static inline struct wlr_surface *client_surface(Client *c) { -#ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->surface; -#endif - return c->surface.xdg->surface; -} - -static inline int32_t toplevel_from_wlr_surface(struct wlr_surface *s, - Client **pc, - LayerSurface **pl) { - struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; - struct wlr_surface *root_surface; - struct wlr_layer_surface_v1 *layer_surface; - Client *c = NULL; - LayerSurface *l = NULL; - int32_t type = -1; -#ifdef XWAYLAND - struct wlr_xwayland_surface *xsurface; -#endif - - if (!s) - return -1; - root_surface = wlr_surface_get_root_surface(s); - -#ifdef XWAYLAND - if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) { - c = xsurface->data; - type = c->type; - goto end; - } -#endif - - if ((layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) { - l = layer_surface->data; - type = LayerShell; - goto end; - } - - xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface); - while (xdg_surface) { - tmp_xdg_surface = NULL; - switch (xdg_surface->role) { - case WLR_XDG_SURFACE_ROLE_POPUP: - if (!xdg_surface->popup || !xdg_surface->popup->parent) - return -1; - - tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface( - xdg_surface->popup->parent); - - if (!tmp_xdg_surface) - return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, - pl); - - xdg_surface = tmp_xdg_surface; - break; - case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - c = xdg_surface->data; - type = c->type; - goto end; - case WLR_XDG_SURFACE_ROLE_NONE: - return -1; - } - } - -end: - if (pl) - *pl = l; - if (pc) - *pc = c; - return type; -} - -/* The others */ -static inline void client_activate_surface(struct wlr_surface *s, - int32_t activated) { - struct wlr_xdg_toplevel *toplevel; -#ifdef XWAYLAND - struct wlr_xwayland_surface *xsurface; - if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) { - if (activated && xsurface->minimized) - wlr_xwayland_surface_set_minimized(xsurface, false); - wlr_xwayland_surface_activate(xsurface, activated); - return; - } -#endif - if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s))) - wlr_xdg_toplevel_set_activated(toplevel, activated); -} - -static inline const char *client_get_appid(Client *c) { -#ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->class ? c->surface.xwayland->class - : "broken"; -#endif - return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id - : "broken"; -} - -static inline int32_t client_get_pid(Client *c) { - pid_t pid; -#ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->pid; -#endif - wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); - return pid; -} - -static inline void client_get_clip(Client *c, struct wlr_box *clip) { - *clip = (struct wlr_box){ - .x = 0, - .y = 0, - .width = c->geom.width - 2 * c->bw, - .height = c->geom.height - 2 * c->bw, - }; - -#ifdef XWAYLAND - if (client_is_x11(c)) - return; -#endif - - clip->x = c->surface.xdg->geometry.x; - clip->y = c->surface.xdg->geometry.y; -} - -static inline void client_get_geometry(Client *c, struct wlr_box *geom) { -#ifdef XWAYLAND - if (client_is_x11(c)) { - geom->x = c->surface.xwayland->x; - geom->y = c->surface.xwayland->y; - geom->width = c->surface.xwayland->width; - geom->height = c->surface.xwayland->height; - return; - } -#endif - *geom = c->surface.xdg->geometry; -} - -static inline Client *client_get_parent(Client *c) { - Client *p = NULL; -#ifdef XWAYLAND - if (client_is_x11(c)) { - if (c->surface.xwayland->parent) - toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, - NULL); - return p; - } -#endif - if (c->surface.xdg->toplevel->parent) - toplevel_from_wlr_surface( - c->surface.xdg->toplevel->parent->base->surface, &p, NULL); - return p; -} - -static inline int32_t client_has_children(Client *c) { -#ifdef XWAYLAND - if (client_is_x11(c)) - return !wl_list_empty(&c->surface.xwayland->children); -#endif - /* surface.xdg->link is never empty because it always contains at least the - * surface itself. */ - return wl_list_length(&c->surface.xdg->link) > 1; -} - -static inline const char *client_get_title(Client *c) { -#ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->title ? c->surface.xwayland->title - : "broken"; -#endif - return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title - : "broken"; -} - -static inline int32_t client_is_float_type(Client *c) { - struct wlr_xdg_toplevel *toplevel; - struct wlr_xdg_toplevel_state state; - -#ifdef XWAYLAND - if (client_is_x11(c)) { - struct wlr_xwayland_surface *surface = c->surface.xwayland; - xcb_size_hints_t *size_hints = surface->size_hints; - - if (!size_hints) - return 0; - - if (surface->modal) - return 1; - - if (wlr_xwayland_surface_has_window_type( - surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG) || - wlr_xwayland_surface_has_window_type( - surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH) || - wlr_xwayland_surface_has_window_type( - surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR) || - wlr_xwayland_surface_has_window_type( - surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) { - return 1; - } - - return size_hints && size_hints->min_width > 0 && - size_hints->min_height > 0 && - (size_hints->max_width == size_hints->min_width || - size_hints->max_height == size_hints->min_height); - } -#endif - - toplevel = c->surface.xdg->toplevel; - state = toplevel->current; - return toplevel->parent || (state.min_width != 0 && state.min_height != 0 && - (state.min_width == state.max_width || - state.min_height == state.max_height)); -} - -static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) { - /* This is needed for when you don't want to check formal assignment, - * but rather actual displaying of the pixels. - * Usually VISIBLEON suffices and is also faster. */ - struct wlr_surface_output *s; - int32_t unused_lx, unused_ly; - if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) - return 0; - wl_list_for_each(s, &client_surface(c)->current_outputs, - link) if (s->output == m->wlr_output) return 1; - return 0; -} - -static inline int32_t client_is_stopped(Client *c) { - int32_t pid; - siginfo_t in = {0}; -#ifdef XWAYLAND - if (client_is_x11(c)) - return 0; -#endif - - wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); - if (waitid(P_PID, pid, &in, WNOHANG | WCONTINUED | WSTOPPED | WNOWAIT) < - 0) { - /* This process is not our child process, while is very unluckely that - * it is stopped, in order to do not skip frames assume that it is. */ - if (errno == ECHILD) - return 1; - } else if (in.si_pid) { - if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED) - return 1; - if (in.si_code == CLD_CONTINUED) - return 0; - } - - return 0; -} - -static inline int32_t client_is_unmanaged(Client *c) { -#ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->override_redirect; -#endif - return 0; -} - -static inline void client_notify_enter(struct wlr_surface *s, - struct wlr_keyboard *kb) { - if (kb) - wlr_seat_keyboard_notify_enter(server.seat, s, kb->keycodes, - kb->num_keycodes, &kb->modifiers); - else - wlr_seat_keyboard_notify_enter(server.seat, s, NULL, 0, NULL); -} - -static inline void client_send_close(Client *c) { -#ifdef XWAYLAND - if (client_is_x11(c)) { - wlr_xwayland_surface_close(c->surface.xwayland); - return; - } -#endif - wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel); -} - -static inline void client_set_border_color(Client *c, - const float color[static 4]) { - wlr_scene_rect_set_color(c->border, color); -} - -static inline void client_set_fullscreen(Client *c, int32_t fullscreen) { -#ifdef XWAYLAND - if (client_is_x11(c)) { - wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); - return; - } -#endif - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen); -} - -static inline void client_set_scale(struct wlr_surface *s, float scale) { - wlr_fractional_scale_v1_notify_scale(s, scale); - wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale)); -} - -static inline uint32_t client_set_size(Client *c, uint32_t width, - uint32_t height) { -#ifdef XWAYLAND - if (client_is_x11(c)) { - wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw, - c->geom.y + c->bw, width, height); - return 0; - } -#endif - if ((int32_t)width == c->surface.xdg->toplevel->current.width && - (int32_t)height == c->surface.xdg->toplevel->current.height) - return 0; - return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, - (int32_t)height); -} - -static inline void client_set_minimized(Client *c, bool minimized) { -#ifdef XWAYLAND - if (client_is_x11(c)) { - wlr_xwayland_surface_set_minimized(c->surface.xwayland, minimized); - return; - } -#endif - - return; -} - -static inline void client_set_maximized(Client *c, bool maximized) { - struct wlr_xdg_toplevel *toplevel; - -#ifdef XWAYLAND - if (client_is_x11(c)) { - wlr_xwayland_surface_set_maximized(c->surface.xwayland, maximized, - maximized); - return; - } -#endif - toplevel = c->surface.xdg->toplevel; - wlr_xdg_toplevel_set_maximized(toplevel, maximized); - return; -} - -static inline void client_set_tiled(Client *c, uint32_t edges) { - struct wlr_xdg_toplevel *toplevel; -#ifdef XWAYLAND - if (client_is_x11(c) && c->force_maximize) { - wlr_xwayland_surface_set_maximized(c->surface.xwayland, - edges != WLR_EDGE_NONE, - edges != WLR_EDGE_NONE); - return; - } -#endif - - toplevel = c->surface.xdg->toplevel; - - if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >= - XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { - wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); - } - - if (c->force_maximize) { - wlr_xdg_toplevel_set_maximized(toplevel, edges != WLR_EDGE_NONE); - } -} - -static inline void client_set_suspended(Client *c, int32_t suspended) { -#ifdef XWAYLAND - if (client_is_x11(c)) - return; -#endif - - wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); -} - -static inline int32_t client_should_ignore_focus(Client *c) { - -#ifdef XWAYLAND - if (client_is_x11(c)) { - struct wlr_xwayland_surface *surface = c->surface.xwayland; - - if (!surface->hints) - return 0; - - return !surface->hints->input; - } -#endif - return 0; -} - -static inline int32_t client_is_x11_popup(Client *c) { - -#ifdef XWAYLAND - if (client_is_x11(c)) { - struct wlr_xwayland_surface *surface = c->surface.xwayland; - // 处理不需要焦点的窗口类型 - const uint32_t no_focus_types[] = { - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_COMBO, - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DND, - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_MENU, - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NOTIFICATION, - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_POPUP_MENU, - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH, - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLTIP, - WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY}; - // 检查窗口类型是否需要禁止焦点 - for (size_t i = 0; - i < sizeof(no_focus_types) / sizeof(no_focus_types[0]); ++i) { - if (wlr_xwayland_surface_has_window_type(surface, - no_focus_types[i])) { - return 1; - } - } - } -#endif - return 0; -} - -static inline int32_t client_should_global(Client *c) { - -#ifdef XWAYLAND - if (client_is_x11(c)) { - struct wlr_xwayland_surface *surface = c->surface.xwayland; - - if (surface->sticky) - return 1; - } -#endif - return 0; -} - -static inline int32_t client_should_overtop(Client *c) { - -#ifdef XWAYLAND - if (client_is_x11(c)) { - struct wlr_xwayland_surface *surface = c->surface.xwayland; - if (surface->above) - return 1; - } -#endif - return 0; -} - -static inline int32_t client_wants_focus(Client *c) { -#ifdef XWAYLAND - return client_is_unmanaged(c) && - wlr_xwayland_surface_override_redirect_wants_focus( - c->surface.xwayland) && - wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) != - WLR_ICCCM_INPUT_MODEL_NONE; -#endif - return 0; -} - -static inline int32_t client_wants_fullscreen(Client *c) { -#ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->fullscreen; -#endif - return c->surface.xdg->toplevel->requested.fullscreen; -} - -static inline bool client_request_minimize(Client *c, void *data) { - -#ifdef XWAYLAND - if (client_is_x11(c)) { - struct wlr_xwayland_minimize_event *event = data; - return event->minimize; - } -#endif - - return c->surface.xdg->toplevel->requested.minimized; -} - -static inline bool client_request_maximize(Client *c, void *data) { - -#ifdef XWAYLAND - if (client_is_x11(c)) { - struct wlr_xwayland_surface *surface = c->surface.xwayland; - return surface->maximized_vert || surface->maximized_horz; - } -#endif - - return c->surface.xdg->toplevel->requested.maximized; -} - -static inline void client_set_size_bound(Client *c) { - struct wlr_xdg_toplevel *toplevel; - struct wlr_xdg_toplevel_state state; - -#ifdef XWAYLAND - if (client_is_x11(c)) { - struct wlr_xwayland_surface *surface = c->surface.xwayland; - xcb_size_hints_t *size_hints = surface->size_hints; - - if (!size_hints) - return; - - if ((uint32_t)c->geom.width - 2 * c->bw < size_hints->min_width && - size_hints->min_width > 0) - c->geom.width = size_hints->min_width + 2 * c->bw; - if ((uint32_t)c->geom.height - 2 * c->bw < size_hints->min_height && - size_hints->min_height > 0) - c->geom.height = size_hints->min_height + 2 * c->bw; - if ((uint32_t)c->geom.width - 2 * c->bw > size_hints->max_width && - size_hints->max_width > 0) - c->geom.width = size_hints->max_width + 2 * c->bw; - if ((uint32_t)c->geom.height - 2 * c->bw > size_hints->max_height && - size_hints->max_height > 0) - c->geom.height = size_hints->max_height + 2 * c->bw; - return; - } -#endif - - toplevel = c->surface.xdg->toplevel; - state = toplevel->current; - if ((uint32_t)c->geom.width - 2 * c->bw < state.min_width && - state.min_width > 0) { - c->geom.width = state.min_width + 2 * c->bw; - } - if ((uint32_t)c->geom.height - 2 * c->bw < state.min_height && - state.min_height > 0) { - c->geom.height = state.min_height + 2 * c->bw; - } - if ((uint32_t)c->geom.width - 2 * c->bw > state.max_width && - state.max_width > 0) { - c->geom.width = state.max_width + 2 * c->bw; - } - if ((uint32_t)c->geom.height - 2 * c->bw > state.max_height && - state.max_height > 0) { - c->geom.height = state.max_height + 2 * c->bw; - } -} \ No newline at end of file +#ifndef MANGO_CLIENT_CLIENT_H +#define MANGO_CLIENT_CLIENT_H + +/* 基础客户端类型检测和表面处理 */ +int32_t client_is_x11(Client *c); +struct wlr_surface *client_surface(Client *c); +int32_t toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl); + +/* 客户端激活和焦点控制 */ +void client_activate_surface(struct wlr_surface *s, int32_t activated); +void client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb); +int32_t client_should_ignore_focus(Client *c); +int32_t client_is_x11_popup(Client *c); +int32_t client_wants_focus(Client *c); + +/* 客户端信息获取 */ +const char *client_get_appid(Client *c); +int32_t client_get_pid(Client *c); +const char *client_get_title(Client *c); +Client *client_get_parent(Client *c); +int32_t client_has_children(Client *c); +int32_t client_is_float_type(Client *c); +int32_t client_is_rendered_on_mon(Client *c, Monitor *m); +int32_t client_is_stopped(Client *c); +int32_t client_is_unmanaged(Client *c); +int32_t client_wants_fullscreen(Client *c); +bool client_request_minimize(Client *c, void *data); +bool client_request_maximize(Client *c, void *data); + +/* 客户端几何和布局 */ +void client_get_clip(Client *c, struct wlr_box *clip); +void client_get_geometry(Client *c, struct wlr_box *geom); +void client_set_size_bound(Client *c); + +/* 客户端状态设置 */ +void client_send_close(Client *c); +void client_set_border_color(Client *c, const float color[4]); +void client_set_fullscreen(Client *c, int32_t fullscreen); +void client_set_scale(struct wlr_surface *s, float scale); +uint32_t client_set_size(Client *c, uint32_t width, uint32_t height); +void client_set_minimized(Client *c, bool minimized); +void client_set_maximized(Client *c, bool maximized); +void client_set_tiled(Client *c, uint32_t edges); +void client_set_suspended(Client *c, int32_t suspended); + +/* 特殊客户端行为 */ +int32_t client_should_global(Client *c); +int32_t client_should_overtop(Client *c); + +#endif // MANGO_CLIENT_CLIENT_H \ No newline at end of file diff --git a/src/config/parse_config.c b/src/config/parse_config.c new file mode 100644 index 0000000..51afc0a --- /dev/null +++ b/src/config/parse_config.c @@ -0,0 +1,3403 @@ +#include +#include +#include +#include +#include + +#include "../type.h" +#include "parse_config.h" +#include "../dispatch/bind_define.h" + +extern Server server; +extern Layout layouts[]; +extern const uint32_t layouts_len; +extern Layout overviewlayout; + + +// 默认按键绑定数组 +KeyBinding default_key_bindings[] = {CHVT(1), CHVT(2), CHVT(3), CHVT(4), + CHVT(5), CHVT(6), CHVT(7), CHVT(8), + CHVT(9), CHVT(10), CHVT(11), CHVT(12)}; + + +Config config; + +// 查找或添加keymode,返回索引 +int32_t find_or_add_keymode(Config *config, const char *name) { + // 检查是否已存在 + for (int32_t i = 0; i < config->keymodes_count; i++) { + if (strcmp(config->keymodes[i].name, name) == 0) { + return i; + } + } + + // 添加新的keymode + config->keymodes = realloc(config->keymodes, + (config->keymodes_count + 1) * sizeof(KeyMode)); + if (!config->keymodes) { + fprintf(stderr, "Failed to allocate memory for keymodes\n"); + return -1; + } + + KeyMode *new_mode = &config->keymodes[config->keymodes_count]; + new_mode->name = strdup(name); + new_mode->is_default = (strcmp(name, "default") == 0); + new_mode->is_common = (strcmp(name, "common") == 0); + + return config->keymodes_count++; // 返回索引,然后增加计数 +} + +// 设置当前keymode +void set_current_keymode(Config *config, const char *name) { + int32_t idx = find_or_add_keymode(config, name); + if (idx >= 0) { + config->current_keymode_idx = idx; + } +} + +// Helper function to trim whitespace from start and end of a string +void trim_whitespace(char *str) { + if (str == NULL || *str == '\0') + return; + + // Trim leading space + char *start = str; + while (isspace((unsigned char)*start)) { + start++; + } + + // Trim trailing space + char *end = str + strlen(str) - 1; + while (end > start && isspace((unsigned char)*end)) { + end--; + } + + // Null-terminate the trimmed string + *(end + 1) = '\0'; + + // Move the trimmed part to the beginning if needed + if (start != str) { + memmove(str, start, end - start + 2); // +2 to include null terminator + } +} + +int32_t parse_double_array(const char *input, double *output, + int32_t max_count) { + char *dup = strdup(input); + char *token; + int32_t count = 0; + + // 先清空整个数组 + memset(output, 0, max_count * sizeof(double)); + + token = strtok(dup, ","); + while (token != NULL && count < max_count) { + trim_whitespace(token); + char *endptr; + double val = strtod(token, &endptr); + if (endptr == token || *endptr != '\0') { + fprintf( + stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid number in array: %s\n", + token); + free(dup); + return -1; + } + output[count] = val; // 赋值到当前count位置 + count++; // 然后才自增 + token = strtok(NULL, ","); + } + + free(dup); + return count; +} + +// 清理字符串中的不可见字符(包括 \r, \n, 空格等) +char *sanitize_string(char *str) { + // 去除首部不可见字符 + while (*str != '\0' && !isprint((unsigned char)*str)) + str++; + // 去除尾部不可见字符 + char *end = str + strlen(str) - 1; + while (end > str && !isprint((unsigned char)*end)) + end--; + *(end + 1) = '\0'; + return str; +} + +// 解析bind组合字符串 +void parse_bind_flags(const char *str, KeyBinding *kb) { + + // 检查是否以"bind"开头 + if (strncmp(str, "bind", 4) != 0) { + return; + } + + const char *suffix = str + 4; // 跳过"bind" + + // 遍历后缀字符 + for (int32_t i = 0; suffix[i] != '\0'; i++) { + switch (suffix[i]) { + case 's': + kb->keysymcode.type = KEY_TYPE_SYM; + break; + case 'l': + kb->islockapply = true; + break; + case 'r': + kb->isreleaseapply = true; + break; + case 'p': + kb->ispassapply = true; + break; + default: + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown bind flag: %c\n", + suffix[i]); + break; + } + } +} + +int32_t parse_circle_direction(const char *str) { + // 将输入字符串转换为小写 + char lowerStr[10]; + int32_t i = 0; + while (str[i] && i < 9) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; + + // 根据转换后的小写字符串返回对应的枚举值 + if (strcmp(lowerStr, "next") == 0) { + return NEXT; + } else { + return PREV; + } +} + +int32_t parse_direction(const char *str) { + // 将输入字符串转换为小写 + char lowerStr[10]; + int32_t i = 0; + while (str[i] && i < 9) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; + + // 根据转换后的小写字符串返回对应的枚举值 + if (strcmp(lowerStr, "up") == 0) { + return UP; + } else if (strcmp(lowerStr, "down") == 0) { + return DOWN; + } else if (strcmp(lowerStr, "left") == 0) { + return LEFT; + } else if (strcmp(lowerStr, "right") == 0) { + return RIGHT; + } else { + return UNDIR; + } +} + +int32_t parse_fold_state(const char *str) { + // 将输入字符串转换为小写 + char lowerStr[10]; + int32_t i = 0; + while (str[i] && i < 9) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; + + // 根据转换后的小写字符串返回对应的枚举值 + if (strcmp(lowerStr, "fold") == 0) { + return FOLD; + } else if (strcmp(lowerStr, "unfold") == 0) { + return UNFOLD; + } else { + return INVALIDFOLD; + } +} +int64_t parse_color(const char *hex_str) { + char *endptr; + int64_t hex_num = strtol(hex_str, &endptr, 16); + if (*endptr != '\0') { + return -1; + } + return hex_num; +} + +// 辅助函数:检查字符串是否以指定的前缀开头(忽略大小写) +bool starts_with_ignore_case(const char *str, const char *prefix) { + while (*prefix) { + if (tolower(*str) != tolower(*prefix)) { + return false; + } + str++; + prefix++; + } + return true; +} + +char *combine_args_until_empty(char *values[], int count) { + // find the first empty string + int first_empty = count; + for (int i = 0; i < count; i++) { + // check if it's empty: empty string or only contains "0" (initialized) + if (values[i][0] == '\0' || + (strlen(values[i]) == 1 && values[i][0] == '0')) { + first_empty = i; + break; + } + } + + // if there are no valid parameters, return an empty string + if (first_empty == 0) { + return strdup(""); + } + + // calculate the total length + size_t total_len = 0; + for (int i = 0; i < first_empty; i++) { + total_len += strlen(values[i]); + } + // plus the number of commas (first_empty-1 commas) + total_len += (first_empty - 1); + + // allocate memory and concatenate + char *combined = malloc(total_len + 1); + if (combined == NULL) { + return strdup(""); + } + + combined[0] = '\0'; + for (int i = 0; i < first_empty; i++) { + if (i > 0) { + strcat(combined, ","); + } + strcat(combined, values[i]); + } + + return combined; +} + +uint32_t parse_mod(const char *mod_str) { + if (!mod_str || !*mod_str) { + return UINT32_MAX; + } + + uint32_t mod = 0; + char input_copy[256]; + char *token; + char *saveptr = NULL; + bool match_success = false; + + // 复制并转换为小写 + strncpy(input_copy, mod_str, sizeof(input_copy) - 1); + input_copy[sizeof(input_copy) - 1] = '\0'; + for (char *p = input_copy; *p; p++) { + *p = tolower(*p); + } + + // 分割处理每个部分 + token = strtok_r(input_copy, "+", &saveptr); + while (token != NULL) { + // 去除空白 + while (*token == ' ' || *token == '\t') + token++; + + if (strncmp(token, "code:", 5) == 0) { + // 处理 code: 形式 + char *endptr; + long keycode = strtol(token + 5, &endptr, 10); + if (endptr != token + 5 && (*endptr == '\0' || *endptr == ' ')) { + switch (keycode) { + case 133: + case 134: + mod |= WLR_MODIFIER_LOGO; + break; + case 37: + case 105: + mod |= WLR_MODIFIER_CTRL; + break; + case 50: + case 62: + mod |= WLR_MODIFIER_SHIFT; + break; + case 64: + case 108: + mod |= WLR_MODIFIER_ALT; + break; + default: + fprintf(stderr, + "unknown modifier keycode: \033[1m\033[31m%s\n", + token); + break; + } + } + } else { + // 完整的 modifier 检查(保留原始所有检查项) + if (!strcmp(token, "super") || !strcmp(token, "super_l") || + !strcmp(token, "super_r")) { + mod |= WLR_MODIFIER_LOGO; + match_success = true; + } + if (!strcmp(token, "ctrl") || !strcmp(token, "ctrl_l") || + !strcmp(token, "ctrl_r")) { + mod |= WLR_MODIFIER_CTRL; + match_success = true; + } + if (!strcmp(token, "shift") || !strcmp(token, "shift_l") || + !strcmp(token, "shift_r")) { + mod |= WLR_MODIFIER_SHIFT; + match_success = true; + } + if (!strcmp(token, "alt") || !strcmp(token, "alt_l") || + !strcmp(token, "alt_r")) { + mod |= WLR_MODIFIER_ALT; + match_success = true; + } + if (!strcmp(token, "hyper") || !strcmp(token, "hyper_l") || + !strcmp(token, "hyper_r")) { + mod |= WLR_MODIFIER_MOD3; + match_success = true; + } + if (!strcmp(token, "none")) { + match_success = true; + } + } + + token = strtok_r(NULL, "+", &saveptr); + } + + if (!match_success) { + mod = UINT32_MAX; + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown modifier: " + "\033[1m\033[31m%s\n", + mod_str); + } + + return mod; +} + +// 定义辅助函数:在 keymap 中查找 keysym 对应的多个 keycode +int32_t find_keycodes_for_keysym(struct xkb_keymap *keymap, + xkb_keysym_t sym, + MultiKeycode *multi_kc) { + xkb_keycode_t min_keycode = xkb_keymap_min_keycode(keymap); + xkb_keycode_t max_keycode = xkb_keymap_max_keycode(keymap); + + multi_kc->keycode1 = 0; + multi_kc->keycode2 = 0; + multi_kc->keycode3 = 0; + + int32_t found_count = 0; + + for (xkb_keycode_t keycode = min_keycode; + keycode <= max_keycode && found_count < 3; keycode++) { + // 使用布局0和层级0 + const xkb_keysym_t *syms; + int32_t num_syms = + xkb_keymap_key_get_syms_by_level(keymap, keycode, 0, 0, &syms); + + for (int32_t i = 0; i < num_syms; i++) { + if (syms[i] == sym) { + switch (found_count) { + case 0: + multi_kc->keycode1 = keycode; + break; + case 1: + multi_kc->keycode2 = keycode; + break; + case 2: + multi_kc->keycode3 = keycode; + break; + } + found_count++; + break; + } + } + } + + return found_count; +} + +void cleanup_config_keymap(void) { + if (config.keymap != NULL) { + xkb_keymap_unref(config.keymap); + config.keymap = NULL; + } + if (config.ctx != NULL) { + xkb_context_unref(config.ctx); + config.ctx = NULL; + } +} + +void create_config_keymap(void) { + // 初始化 xkb 上下文和 keymap + + if (config.ctx == NULL) { + config.ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + } + + if (config.keymap == NULL) { + config.keymap = xkb_keymap_new_from_names( + config.ctx, &xkb_fallback_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + } +} + +KeySymCode parse_key(const char *key_str, bool isbindsym) { + KeySymCode kc = {0}; // 初始化为0 + + if (config.keymap == NULL || config.ctx == NULL) { + // 处理错误 + kc.type = KEY_TYPE_SYM; + kc.keysym = XKB_KEY_NoSymbol; + return kc; + } + + // 处理 code: 前缀的情况 + if (strncmp(key_str, "code:", 5) == 0) { + char *endptr; + xkb_keycode_t keycode = (xkb_keycode_t)strtol(key_str + 5, &endptr, 10); + kc.type = KEY_TYPE_CODE; + kc.keycode.keycode1 = keycode; // 只设置第一个 + kc.keycode.keycode2 = 0; + kc.keycode.keycode3 = 0; + return kc; + } + + // 普通键名直接转换 + xkb_keysym_t sym = xkb_keysym_from_name(key_str, XKB_KEYSYM_NO_FLAGS); + + if (isbindsym) { + kc.type = KEY_TYPE_SYM; + kc.keysym = sym; + return kc; + } + + if (sym != XKB_KEY_NoSymbol) { + // 尝试找到对应的多个 keycode + int32_t found_count = + find_keycodes_for_keysym(config.keymap, sym, &kc.keycode); + if (found_count > 0) { + kc.type = KEY_TYPE_CODE; + kc.keysym = sym; // 仍然保存 keysym 供参考 + } else { + kc.type = KEY_TYPE_SYM; + kc.keysym = sym; + // keycode 字段保持为0 + } + } else { + // 无法解析的键名 + kc.type = KEY_TYPE_SYM; + kc.keysym = XKB_KEY_NoSymbol; + fprintf( + stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown key: \033[1m\033[31m%s\n", + key_str); + // keycode 字段保持为0 + } + + return kc; +} + +uint32_t parse_button(const char *str) { + // 将输入字符串转换为小写 + char lowerStr[20]; + int32_t i = 0; + while (str[i] && i < 19) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; // 确保字符串正确终止 + + // 根据转换后的小写字符串返回对应的按钮编号 + if (strcmp(lowerStr, "btn_left") == 0) { + return BTN_LEFT; + } else if (strcmp(lowerStr, "btn_right") == 0) { + return BTN_RIGHT; + } else if (strcmp(lowerStr, "btn_middle") == 0) { + return BTN_MIDDLE; + } else if (strcmp(lowerStr, "btn_side") == 0) { + return BTN_SIDE; + } else if (strcmp(lowerStr, "btn_extra") == 0) { + return BTN_EXTRA; + } else if (strcmp(lowerStr, "btn_forward") == 0) { + return BTN_FORWARD; + } else if (strcmp(lowerStr, "btn_back") == 0) { + return BTN_BACK; + } else if (strcmp(lowerStr, "btn_task") == 0) { + return BTN_TASK; + } else { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown button: " + "\033[1m\033[31m%s\n", + str); + return UINT32_MAX; + } +} + +int32_t parse_mouse_action(const char *str) { + // 将输入字符串转换为小写 + char lowerStr[20]; + int32_t i = 0; + while (str[i] && i < 19) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; // 确保字符串正确终止 + + // 根据转换后的小写字符串返回对应的按钮编号 + if (strcmp(lowerStr, "curmove") == 0) { + return CurMove; + } else if (strcmp(lowerStr, "curresize") == 0) { + return CurResize; + } else if (strcmp(lowerStr, "curnormal") == 0) { + return CurNormal; + } else if (strcmp(lowerStr, "curpressed") == 0) { + return CurPressed; + } else { + return 0; + } +} + +void convert_hex_to_rgba(float *color, uint32_t hex) { + color[0] = ((hex >> 24) & 0xFF) / 255.0f; + color[1] = ((hex >> 16) & 0xFF) / 255.0f; + color[2] = ((hex >> 8) & 0xFF) / 255.0f; + color[3] = (hex & 0xFF) / 255.0f; +} + +uint32_t parse_num_type(char *str) { + switch (str[0]) { + case '-': + return NUM_TYPE_MINUS; + case '+': + return NUM_TYPE_PLUS; + default: + return NUM_TYPE_DEFAULT; + } +} + +FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, + char *arg_value2, char *arg_value3, char *arg_value4, + char *arg_value5) { + + FuncType func = NULL; + (*arg).i = 0; + (*arg).i2 = 0; + (*arg).f = 0.0f; + (*arg).f2 = 0.0f; + (*arg).ui = 0; + (*arg).ui2 = 0; + (*arg).v = NULL; + (*arg).v2 = NULL; + (*arg).v3 = NULL; + + if (strcmp(func_name, "focusstack") == 0) { + func = focusstack; + (*arg).i = parse_circle_direction(arg_value); + } else if (strcmp(func_name, "focusdir") == 0) { + func = focusdir; + (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "incnmaster") == 0) { + func = incnmaster; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "setmfact") == 0) { + func = setmfact; + (*arg).f = atof(arg_value); + } else if (strcmp(func_name, "zoom") == 0) { + func = zoom; + } else if (strcmp(func_name, "exchange_client") == 0) { + func = exchange_client; + (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "exchange_stack_client") == 0) { + func = exchange_stack_client; + (*arg).i = parse_circle_direction(arg_value); + } else if (strcmp(func_name, "toggleglobal") == 0) { + func = toggleglobal; + } else if (strcmp(func_name, "toggleoverview") == 0) { + func = toggleoverview; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "set_proportion") == 0) { + func = set_proportion; + (*arg).f = atof(arg_value); + } else if (strcmp(func_name, "switch_proportion_preset") == 0) { + func = switch_proportion_preset; + } else if (strcmp(func_name, "viewtoleft") == 0) { + func = viewtoleft; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "viewtoright") == 0) { + func = viewtoright; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "tagsilent") == 0) { + func = tagsilent; + (*arg).ui = 1 << (atoi(arg_value) - 1); + } else if (strcmp(func_name, "tagtoleft") == 0) { + func = tagtoleft; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "tagtoright") == 0) { + func = tagtoright; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "killclient") == 0) { + func = killclient; + } else if (strcmp(func_name, "centerwin") == 0) { + func = centerwin; + } else if (strcmp(func_name, "focuslast") == 0) { + func = focuslast; + } else if (strcmp(func_name, "toggle_trackpad_enable") == 0) { + func = toggle_trackpad_enable; + } else if (strcmp(func_name, "setoption") == 0) { + func = setoption; + + (*arg).v = strdup(arg_value); + + // 收集需要拼接的参数 + const char *non_empty_params[4] = {NULL}; + int32_t param_index = 0; + + if (arg_value2 && arg_value2[0] != '\0') + non_empty_params[param_index++] = arg_value2; + if (arg_value3 && arg_value3[0] != '\0') + non_empty_params[param_index++] = arg_value3; + if (arg_value4 && arg_value4[0] != '\0') + non_empty_params[param_index++] = arg_value4; + if (arg_value5 && arg_value5[0] != '\0') + non_empty_params[param_index++] = arg_value5; + + // 处理拼接 + if (param_index == 0) { + (*arg).v2 = strdup(""); + } else { + // 计算总长度 + size_t len = 0; + for (int32_t i = 0; i < param_index; i++) { + len += strlen(non_empty_params[i]); + } + len += (param_index - 1) + 1; // 逗号数 + null终止符 + + char *temp = malloc(len); + if (temp) { + char *cursor = temp; + for (int32_t i = 0; i < param_index; i++) { + if (i > 0) { + *cursor++ = ','; + } + size_t param_len = strlen(non_empty_params[i]); + memcpy(cursor, non_empty_params[i], param_len); + cursor += param_len; + } + *cursor = '\0'; + (*arg).v2 = temp; + } + } + } else if (strcmp(func_name, "setkeymode") == 0) { + func = setkeymode; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "switch_keyboard_layout") == 0) { + func = switch_keyboard_layout; + } else if (strcmp(func_name, "setlayout") == 0) { + func = setlayout; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "switch_layout") == 0) { + func = switch_layout; + } else if (strcmp(func_name, "togglefloating") == 0) { + func = togglefloating; + } else if (strcmp(func_name, "togglefullscreen") == 0) { + func = togglefullscreen; + } else if (strcmp(func_name, "togglefakefullscreen") == 0) { + func = togglefakefullscreen; + } else if (strcmp(func_name, "toggleoverlay") == 0) { + func = toggleoverlay; + } else if (strcmp(func_name, "minimized") == 0) { + func = minimized; + } else if (strcmp(func_name, "restore_minimized") == 0) { + func = restore_minimized; + } else if (strcmp(func_name, "toggle_scratchpad") == 0) { + func = toggle_scratchpad; + } else if (strcmp(func_name, "toggle_render_border") == 0) { + func = toggle_render_border; + } else if (strcmp(func_name, "focusmon") == 0) { + func = focusmon; + (*arg).i = parse_direction(arg_value); + if ((*arg).i == UNDIR) { + (*arg).v = strdup(arg_value); + } + } else if (strcmp(func_name, "tagmon") == 0) { + func = tagmon; + (*arg).i = parse_direction(arg_value); + (*arg).i2 = atoi(arg_value2); + if ((*arg).i == UNDIR) { + (*arg).v = strdup(arg_value); + }; + } else if (strcmp(func_name, "incgaps") == 0) { + func = incgaps; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "togglegaps") == 0) { + func = togglegaps; + } else if (strcmp(func_name, "chvt") == 0) { + func = chvt; + (*arg).ui = atoi(arg_value); + } else if (strcmp(func_name, "spawn") == 0) { + func = spawn; + char *values[] = {arg_value, arg_value2, arg_value3, arg_value4, + arg_value5}; + (*arg).v = combine_args_until_empty(values, 5); + } else if (strcmp(func_name, "spawn_shell") == 0) { + func = spawn_shell; + char *values[] = {arg_value, arg_value2, arg_value3, arg_value4, + arg_value5}; + (*arg).v = combine_args_until_empty(values, 5); + } else if (strcmp(func_name, "spawn_on_empty") == 0) { + func = spawn_on_empty; + (*arg).v = strdup(arg_value); // 注意:之后需要释放这个内存 + (*arg).ui = 1 << (atoi(arg_value2) - 1); + } else if (strcmp(func_name, "quit") == 0) { + func = quit; + } else if (strcmp(func_name, "create_virtual_output") == 0) { + func = create_virtual_output; + } else if (strcmp(func_name, "destroy_all_virtual_output") == 0) { + func = destroy_all_virtual_output; + } else if (strcmp(func_name, "moveresize") == 0) { + func = moveresize; + (*arg).ui = parse_mouse_action(arg_value); + } else if (strcmp(func_name, "togglemaximizescreen") == 0) { + func = togglemaximizescreen; + } else if (strcmp(func_name, "viewtoleft_have_client") == 0) { + func = viewtoleft_have_client; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "viewtoright_have_client") == 0) { + func = viewtoright_have_client; + (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "reload_config") == 0) { + func = reload_config; + } else if (strcmp(func_name, "tag") == 0) { + func = tag; + (*arg).ui = 1 << (atoi(arg_value) - 1); + (*arg).i = atoi(arg_value2); + } else if (strcmp(func_name, "view") == 0) { + func = bind_to_view; + + uint32_t mask = 0; + char *token; + char *arg_copy = strdup(arg_value); + + if (arg_copy != NULL) { + char *saveptr = NULL; + token = strtok_r(arg_copy, "|", &saveptr); + + while (token != NULL) { + int32_t num = atoi(token); + if (num > 0 && num <= LENGTH(tags)) { + mask |= (1 << (num - 1)); + } + token = strtok_r(NULL, "|", &saveptr); + } + + free(arg_copy); + } + + if (mask) { + (*arg).ui = mask; + } else { + (*arg).ui = atoi(arg_value); + } + (*arg).i = atoi(arg_value2); + } else if (strcmp(func_name, "viewcrossmon") == 0) { + func = viewcrossmon; + (*arg).ui = 1 << (atoi(arg_value) - 1); + (*arg).v = strdup(arg_value2); + } else if (strcmp(func_name, "tagcrossmon") == 0) { + func = tagcrossmon; + (*arg).ui = 1 << (atoi(arg_value) - 1); + (*arg).v = strdup(arg_value2); + } else if (strcmp(func_name, "toggletag") == 0) { + func = toggletag; + (*arg).ui = 1 << (atoi(arg_value) - 1); + } else if (strcmp(func_name, "toggleview") == 0) { + func = toggleview; + (*arg).ui = 1 << (atoi(arg_value) - 1); + } else if (strcmp(func_name, "comboview") == 0) { + func = comboview; + (*arg).ui = 1 << (atoi(arg_value) - 1); + } else if (strcmp(func_name, "smartmovewin") == 0) { + func = smartmovewin; + (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "smartresizewin") == 0) { + func = smartresizewin; + (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "resizewin") == 0) { + func = resizewin; + (*arg).ui = parse_num_type(arg_value); + (*arg).ui2 = parse_num_type(arg_value2); + (*arg).i = (*arg).ui == NUM_TYPE_DEFAULT ? atoi(arg_value) + : atoi(arg_value + 1); + (*arg).i2 = (*arg).ui2 == NUM_TYPE_DEFAULT ? atoi(arg_value2) + : atoi(arg_value2 + 1); + } else if (strcmp(func_name, "movewin") == 0) { + func = movewin; + (*arg).ui = parse_num_type(arg_value); + (*arg).ui2 = parse_num_type(arg_value2); + (*arg).i = (*arg).ui == NUM_TYPE_DEFAULT ? atoi(arg_value) + : atoi(arg_value + 1); + (*arg).i2 = (*arg).ui2 == NUM_TYPE_DEFAULT ? atoi(arg_value2) + : atoi(arg_value2 + 1); + } else if (strcmp(func_name, "toggle_named_scratchpad") == 0) { + func = toggle_named_scratchpad; + (*arg).v = strdup(arg_value); + (*arg).v2 = strdup(arg_value2); + (*arg).v3 = strdup(arg_value3); + } else if (strcmp(func_name, "disable_monitor") == 0) { + func = disable_monitor; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "enable_monitor") == 0) { + func = enable_monitor; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "toggle_monitor") == 0) { + func = toggle_monitor; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "scroller_stack") == 0) { + func = scroller_stack; + (*arg).i = parse_direction(arg_value); + } else { + return NULL; + } + return func; +} + +void set_env() { + for (int32_t i = 0; i < config.env_count; i++) { + setenv(config.env[i]->type, config.env[i]->value, 1); + } +} + +void run_exec() { + Arg arg; + + for (int32_t i = 0; i < config.exec_count; i++) { + arg.v = config.exec[i]; + spawn_shell(&arg); + } +} + +void run_exec_once() { + Arg arg; + + for (int32_t i = 0; i < config.exec_once_count; i++) { + arg.v = config.exec_once[i]; + spawn_shell(&arg); + } +} + +bool parse_option(Config *config, char *key, char *value) { + if (strcmp(key, "keymode") == 0) { + set_current_keymode(config, value); + } else if (strcmp(key, "animations") == 0) { + config->animations = atoi(value); + } else if (strcmp(key, "layer_animations") == 0) { + config->layer_animations = atoi(value); + } else if (strcmp(key, "animation_type_open") == 0) { + snprintf(config->animation_type_open, + sizeof(config->animation_type_open), "%.9s", + value); // string limit to 9 char + } else if (strcmp(key, "animation_type_close") == 0) { + snprintf(config->animation_type_close, + sizeof(config->animation_type_close), "%.9s", + value); // string limit to 9 char + } else if (strcmp(key, "layer_animation_type_open") == 0) { + snprintf(config->layer_animation_type_open, + sizeof(config->layer_animation_type_open), "%.9s", + value); // string limit to 9 char + } else if (strcmp(key, "layer_animation_type_close") == 0) { + snprintf(config->layer_animation_type_close, + sizeof(config->layer_animation_type_close), "%.9s", + value); // string limit to 9 char + } else if (strcmp(key, "animation_fade_in") == 0) { + config->animation_fade_in = atoi(value); + } else if (strcmp(key, "animation_fade_out") == 0) { + config->animation_fade_out = atoi(value); + } else if (strcmp(key, "tag_animation_direction") == 0) { + config->tag_animation_direction = atoi(value); + } else if (strcmp(key, "zoom_initial_ratio") == 0) { + config->zoom_initial_ratio = atof(value); + } else if (strcmp(key, "zoom_end_ratio") == 0) { + config->zoom_end_ratio = atof(value); + } else if (strcmp(key, "fadein_begin_opacity") == 0) { + config->fadein_begin_opacity = atof(value); + } else if (strcmp(key, "fadeout_begin_opacity") == 0) { + config->fadeout_begin_opacity = atof(value); + } else if (strcmp(key, "animation_duration_move") == 0) { + config->animation_duration_move = atoi(value); + } else if (strcmp(key, "animation_duration_open") == 0) { + config->animation_duration_open = atoi(value); + } else if (strcmp(key, "animation_duration_tag") == 0) { + config->animation_duration_tag = atoi(value); + } else if (strcmp(key, "animation_duration_close") == 0) { + config->animation_duration_close = atoi(value); + } else if (strcmp(key, "animation_duration_focus") == 0) { + config->animation_duration_focus = atoi(value); + } else if (strcmp(key, "animation_curve_move") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_move, 4); + if (num != 4) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " + "animation_curve_move: %s\n", + value); + return false; + } + } else if (strcmp(key, "animation_curve_open") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_open, 4); + if (num != 4) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " + "animation_curve_open: %s\n", + value); + return false; + } + } else if (strcmp(key, "animation_curve_tag") == 0) { + int32_t num = parse_double_array(value, config->animation_curve_tag, 4); + if (num != 4) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " + "animation_curve_tag: %s\n", + value); + return false; + } + } else if (strcmp(key, "animation_curve_close") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_close, 4); + if (num != 4) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " + "animation_curve_close: %s\n", + value); + return false; + } + } else if (strcmp(key, "animation_curve_focus") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_focus, 4); + if (num != 4) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " + "animation_curve_focus: %s\n", + value); + return false; + } + } else if (strcmp(key, "animation_curve_opafadein") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_opafadein, 4); + if (num != 4) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " + "animation_curve_opafadein: %s\n", + value); + return false; + } + } else if (strcmp(key, "animation_curve_opafadeout") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_opafadeout, 4); + if (num != 4) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " + "animation_curve_opafadeout: %s\n", + value); + return false; + } + } else if (strcmp(key, "scroller_structs") == 0) { + config->scroller_structs = atoi(value); + } else if (strcmp(key, "scroller_default_proportion") == 0) { + config->scroller_default_proportion = atof(value); + } else if (strcmp(key, "scroller_default_proportion_single") == 0) { + config->scroller_default_proportion_single = atof(value); + } else if (strcmp(key, "scroller_ignore_proportion_single") == 0) { + config->scroller_ignore_proportion_single = atoi(value); + } else if (strcmp(key, "scroller_focus_center") == 0) { + config->scroller_focus_center = atoi(value); + } else if (strcmp(key, "scroller_prefer_center") == 0) { + config->scroller_prefer_center = atoi(value); + } else if (strcmp(key, "edge_scroller_pointer_focus") == 0) { + config->edge_scroller_pointer_focus = atoi(value); + } else if (strcmp(key, "focus_cross_monitor") == 0) { + config->focus_cross_monitor = atoi(value); + } else if (strcmp(key, "exchange_cross_monitor") == 0) { + config->exchange_cross_monitor = atoi(value); + } else if (strcmp(key, "scratchpad_cross_monitor") == 0) { + config->scratchpad_cross_monitor = atoi(value); + } else if (strcmp(key, "focus_cross_tag") == 0) { + config->focus_cross_tag = atoi(value); + } else if (strcmp(key, "view_current_to_back") == 0) { + config->view_current_to_back = atoi(value); + } else if (strcmp(key, "blur") == 0) { + config->blur = atoi(value); + } else if (strcmp(key, "blur_layer") == 0) { + config->blur_layer = atoi(value); + } else if (strcmp(key, "blur_optimized") == 0) { + config->blur_optimized = atoi(value); + } else if (strcmp(key, "border_radius") == 0) { + config->border_radius = atoi(value); + } else if (strcmp(key, "blur_params_num_passes") == 0) { + config->blur_params.num_passes = atoi(value); + } else if (strcmp(key, "blur_params_radius") == 0) { + config->blur_params.radius = atoi(value); + } else if (strcmp(key, "blur_params_noise") == 0) { + config->blur_params.noise = atof(value); + } else if (strcmp(key, "blur_params_brightness") == 0) { + config->blur_params.brightness = atof(value); + } else if (strcmp(key, "blur_params_contrast") == 0) { + config->blur_params.contrast = atof(value); + } else if (strcmp(key, "blur_params_saturation") == 0) { + config->blur_params.saturation = atof(value); + } else if (strcmp(key, "shadows") == 0) { + config->shadows = atoi(value); + } else if (strcmp(key, "shadow_only_floating") == 0) { + config->shadow_only_floating = atoi(value); + } else if (strcmp(key, "layer_shadows") == 0) { + config->layer_shadows = atoi(value); + } else if (strcmp(key, "shadows_size") == 0) { + config->shadows_size = atoi(value); + } else if (strcmp(key, "shadows_blur") == 0) { + config->shadows_blur = atof(value); + } else if (strcmp(key, "shadows_position_x") == 0) { + config->shadows_position_x = atoi(value); + } else if (strcmp(key, "shadows_position_y") == 0) { + config->shadows_position_y = atoi(value); + } else if (strcmp(key, "single_scratchpad") == 0) { + config->single_scratchpad = atoi(value); + } else if (strcmp(key, "xwayland_persistence") == 0) { + config->xwayland_persistence = atoi(value); + } else if (strcmp(key, "syncobj_enable") == 0) { + config->syncobj_enable = atoi(value); + } else if (strcmp(key, "allow_tearing") == 0) { + config->allow_tearing = atoi(value); + } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { + config->allow_shortcuts_inhibit = atoi(value); + } else if (strcmp(key, "allow_lock_transparent") == 0) { + config->allow_lock_transparent = atoi(value); + } else if (strcmp(key, "no_border_when_single") == 0) { + config->no_border_when_single = atoi(value); + } else if (strcmp(key, "no_radius_when_single") == 0) { + config->no_radius_when_single = atoi(value); + } else if (strcmp(key, "snap_distance") == 0) { + config->snap_distance = atoi(value); + } else if (strcmp(key, "enable_floating_snap") == 0) { + config->enable_floating_snap = atoi(value); + } else if (strcmp(key, "drag_tile_to_tile") == 0) { + config->drag_tile_to_tile = atoi(value); + } else if (strcmp(key, "swipe_min_threshold") == 0) { + config->swipe_min_threshold = atoi(value); + } else if (strcmp(key, "focused_opacity") == 0) { + config->focused_opacity = atof(value); + } else if (strcmp(key, "unfocused_opacity") == 0) { + config->unfocused_opacity = atof(value); + } else if (strcmp(key, "xkb_rules_rules") == 0) { + strncpy(xkb_rules_rules, value, sizeof(xkb_rules_rules) - 1); + xkb_rules_rules[sizeof(xkb_rules_rules) - 1] = + '\0'; // 确保字符串以 null 结尾 + } else if (strcmp(key, "xkb_rules_model") == 0) { + strncpy(xkb_rules_model, value, sizeof(xkb_rules_model) - 1); + xkb_rules_model[sizeof(xkb_rules_model) - 1] = + '\0'; // 确保字符串以 null 结尾 + } else if (strcmp(key, "xkb_rules_layout") == 0) { + strncpy(xkb_rules_layout, value, sizeof(xkb_rules_layout) - 1); + xkb_rules_layout[sizeof(xkb_rules_layout) - 1] = + '\0'; // 确保字符串以 null 结尾 + } else if (strcmp(key, "xkb_rules_variant") == 0) { + strncpy(xkb_rules_variant, value, sizeof(xkb_rules_variant) - 1); + xkb_rules_variant[sizeof(xkb_rules_variant) - 1] = + '\0'; // 确保字符串以 null 结尾 + } else if (strcmp(key, "xkb_rules_options") == 0) { + strncpy(xkb_rules_options, value, sizeof(xkb_rules_options) - 1); + xkb_rules_options[sizeof(xkb_rules_options) - 1] = + '\0'; // 确保字符串以 null 结尾 + } else if (strcmp(key, "scroller_proportion_preset") == 0) { + // 1. 统计 value 中有多少个逗号,确定需要解析的浮点数个数 + int32_t count = 0; // 初始化为 0 + for (const char *p = value; *p; p++) { + if (*p == ',') + count++; + } + int32_t float_count = count + 1; // 浮点数的数量是逗号数量加 1 + + // 2. 动态分配内存,存储浮点数 + config->scroller_proportion_preset = + (float *)malloc(float_count * sizeof(float)); + if (!config->scroller_proportion_preset) { + fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Memory " + "allocation failed\n"); + return false; + } + + // 3. 解析 value 中的浮点数 + char *value_copy = + strdup(value); // 复制 value,因为 strtok 会修改原字符串 + char *token = strtok(value_copy, ","); + int32_t i = 0; + float value_set; + + while (token != NULL && i < float_count) { + if (sscanf(token, "%f", &value_set) != 1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid float " + "value in " + "scroller_proportion_preset: %s\n", + token); + free(value_copy); + free(config->scroller_proportion_preset); + config->scroller_proportion_preset = NULL; + return false; + } + + // Clamp the value between 0.0 and 1.0 (or your desired + // range) + config->scroller_proportion_preset[i] = + CLAMP_FLOAT(value_set, 0.1f, 1.0f); + + token = strtok(NULL, ","); + i++; + } + + // 4. 检查解析的浮点数数量是否匹配 + if (i != float_count) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "scroller_proportion_preset format: %s\n", + value); + free(value_copy); + free(config->scroller_proportion_preset); // 释放已分配的内存 + config->scroller_proportion_preset = NULL; // 防止野指针 + config->scroller_proportion_preset_count = 0; + return false; + } + config->scroller_proportion_preset_count = float_count; + + // 5. 释放临时复制的字符串 + free(value_copy); + } else if (strcmp(key, "circle_layout") == 0) { + // 1. 统计 value 中有多少个逗号,确定需要解析的字符串个数 + int32_t count = 0; // 初始化为 0 + for (const char *p = value; *p; p++) { + if (*p == ',') + count++; + } + int32_t string_count = count + 1; // 字符串的数量是逗号数量加 1 + + // 2. 动态分配内存,存储字符串指针 + config->circle_layout = (char **)malloc(string_count * sizeof(char *)); + memset(config->circle_layout, 0, string_count * sizeof(char *)); + if (!config->circle_layout) { + fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Memory " + "allocation failed\n"); + return false; + } + + // 3. 解析 value 中的字符串 + char *value_copy = + strdup(value); // 复制 value,因为 strtok 会修改原字符串 + char *token = strtok(value_copy, ","); + int32_t i = 0; + char *cleaned_token; + while (token != NULL && i < string_count) { + // 为每个字符串分配内存并复制内容 + cleaned_token = sanitize_string(token); + config->circle_layout[i] = strdup(cleaned_token); + if (!config->circle_layout[i]) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Memory allocation " + "failed for " + "string: %s\n", + token); + // 释放之前分配的内存 + for (int32_t j = 0; j < i; j++) { + free(config->circle_layout[j]); + } + free(config->circle_layout); + free(value_copy); + config->circle_layout = NULL; // 防止野指针 + config->circle_layout_count = 0; + return false; + } + token = strtok(NULL, ","); + i++; + } + + // 4. 检查解析的字符串数量是否匹配 + if (i != string_count) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid circle_layout " + "format: %s\n", + value); + // 释放之前分配的内存 + for (int32_t j = 0; j < i; j++) { + free(config->circle_layout[j]); + } + free(config->circle_layout); + free(value_copy); + config->circle_layout = NULL; // 防止野指针 + config->circle_layout_count = 0; + return false; + } + config->circle_layout_count = string_count; + + // 5. 释放临时复制的字符串 + free(value_copy); + } else if (strcmp(key, "new_is_master") == 0) { + config->new_is_master = atoi(value); + } else if (strcmp(key, "default_mfact") == 0) { + config->default_mfact = atof(value); + } else if (strcmp(key, "default_nmaster") == 0) { + config->default_nmaster = atoi(value); + } else if (strcmp(key, "center_master_overspread") == 0) { + config->center_master_overspread = atoi(value); + } else if (strcmp(key, "center_when_single_stack") == 0) { + config->center_when_single_stack = atoi(value); + } else if (strcmp(key, "hotarea_size") == 0) { + config->hotarea_size = atoi(value); + } else if (strcmp(key, "hotarea_corner") == 0) { + config->hotarea_corner = atoi(value); + } else if (strcmp(key, "enable_hotarea") == 0) { + config->enable_hotarea = atoi(value); + } else if (strcmp(key, "ov_tab_mode") == 0) { + config->ov_tab_mode = atoi(value); + } else if (strcmp(key, "overviewgappi") == 0) { + config->overviewgappi = atoi(value); + } else if (strcmp(key, "overviewgappo") == 0) { + config->overviewgappo = atoi(value); + } else if (strcmp(key, "cursor_hide_timeout") == 0) { + config->cursor_hide_timeout = atoi(value); + } else if (strcmp(key, "axis_bind_apply_timeout") == 0) { + config->axis_bind_apply_timeout = atoi(value); + } else if (strcmp(key, "focus_on_activate") == 0) { + config->focus_on_activate = atoi(value); + } else if (strcmp(key, "numlockon") == 0) { + config->numlockon = atoi(value); + } else if (strcmp(key, "idleinhibit_ignore_visible") == 0) { + config->idleinhibit_ignore_visible = atoi(value); + } else if (strcmp(key, "sloppyfocus") == 0) { + config->sloppyfocus = atoi(value); + } else if (strcmp(key, "warpcursor") == 0) { + config->warpcursor = atoi(value); + } else if (strcmp(key, "drag_corner") == 0) { + config->drag_corner = atoi(value); + } else if (strcmp(key, "drag_warp_cursor") == 0) { + config->drag_warp_cursor = atoi(value); + } else if (strcmp(key, "smartgaps") == 0) { + config->smartgaps = atoi(value); + } else if (strcmp(key, "repeat_rate") == 0) { + config->repeat_rate = atoi(value); + } else if (strcmp(key, "repeat_delay") == 0) { + config->repeat_delay = atoi(value); + } else if (strcmp(key, "disable_trackpad") == 0) { + config->disable_trackpad = atoi(value); + } else if (strcmp(key, "tap_to_click") == 0) { + config->tap_to_click = atoi(value); + } else if (strcmp(key, "tap_and_drag") == 0) { + config->tap_and_drag = atoi(value); + } else if (strcmp(key, "drag_lock") == 0) { + config->drag_lock = atoi(value); + } else if (strcmp(key, "mouse_natural_scrolling") == 0) { + config->mouse_natural_scrolling = atoi(value); + } else if (strcmp(key, "trackpad_natural_scrolling") == 0) { + config->trackpad_natural_scrolling = atoi(value); + } else if (strcmp(key, "cursor_size") == 0) { + config->cursor_size = atoi(value); + } else if (strcmp(key, "cursor_theme") == 0) { + config->cursor_theme = strdup(value); + } else if (strcmp(key, "disable_while_typing") == 0) { + config->disable_while_typing = atoi(value); + } else if (strcmp(key, "left_handed") == 0) { + config->left_handed = atoi(value); + } else if (strcmp(key, "middle_button_emulation") == 0) { + config->middle_button_emulation = atoi(value); + } else if (strcmp(key, "accel_profile") == 0) { + config->accel_profile = atoi(value); + } else if (strcmp(key, "accel_speed") == 0) { + config->accel_speed = atof(value); + } else if (strcmp(key, "scroll_method") == 0) { + config->scroll_method = atoi(value); + } else if (strcmp(key, "scroll_button") == 0) { + config->scroll_button = atoi(value); + } else if (strcmp(key, "click_method") == 0) { + config->click_method = atoi(value); + } else if (strcmp(key, "send_events_mode") == 0) { + config->send_events_mode = atoi(value); + } else if (strcmp(key, "button_map") == 0) { + config->button_map = atoi(value); + } else if (strcmp(key, "axis_scroll_factor") == 0) { + config->axis_scroll_factor = atof(value); + } else if (strcmp(key, "gappih") == 0) { + config->gappih = atoi(value); + } else if (strcmp(key, "gappiv") == 0) { + config->gappiv = atoi(value); + } else if (strcmp(key, "gappoh") == 0) { + config->gappoh = atoi(value); + } else if (strcmp(key, "gappov") == 0) { + config->gappov = atoi(value); + } else if (strcmp(key, "scratchpad_width_ratio") == 0) { + config->scratchpad_width_ratio = atof(value); + } else if (strcmp(key, "scratchpad_height_ratio") == 0) { + config->scratchpad_height_ratio = atof(value); + } else if (strcmp(key, "borderpx") == 0) { + config->borderpx = atoi(value); + } else if (strcmp(key, "rootcolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid rootcolor " + "format: " + "%s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->rootcolor, color); + } + + } else if (strcmp(key, "shadowscolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid shadowscolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->shadowscolor, color); + } + } else if (strcmp(key, "bordercolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid bordercolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->bordercolor, color); + } + } else if (strcmp(key, "focuscolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid focuscolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->focuscolor, color); + } + } else if (strcmp(key, "maximizescreencolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "maximizescreencolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->maximizescreencolor, color); + } + } else if (strcmp(key, "urgentcolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid urgentcolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->urgentcolor, color); + } + } else if (strcmp(key, "scratchpadcolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "scratchpadcolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->scratchpadcolor, color); + } + } else if (strcmp(key, "globalcolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid globalcolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->globalcolor, color); + } + } else if (strcmp(key, "overlaycolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid overlaycolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->overlaycolor, color); + } + } else if (strcmp(key, "monitorrule") == 0) { + config->monitor_rules = + realloc(config->monitor_rules, (config->monitor_rules_count + 1) * + sizeof(ConfigMonitorRule)); + if (!config->monitor_rules) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for monitor rules\n"); + return false; + } + + ConfigMonitorRule *rule = + &config->monitor_rules[config->monitor_rules_count]; + memset(rule, 0, sizeof(ConfigMonitorRule)); + + // 设置默认值 + rule->name = NULL; + rule->rr = 0; + rule->scale = 1.0f; + rule->x = INT32_MAX; + rule->y = INT32_MAX; + rule->width = -1; + rule->height = -1; + rule->refresh = 0.0f; + rule->vrr = 0; + + bool parse_error = false; + char *token = strtok(value, ","); + while (token != NULL) { + char *colon = strchr(token, ':'); + if (colon != NULL) { + *colon = '\0'; + char *key = token; + char *val = colon + 1; + + trim_whitespace(key); + trim_whitespace(val); + + if (strcmp(key, "name") == 0) { + rule->name = strdup(val); + } else if (strcmp(key, "rr") == 0) { + rule->rr = CLAMP_INT(atoi(val), 0, 7); + } else if (strcmp(key, "scale") == 0) { + rule->scale = CLAMP_FLOAT(atof(val), 0.001f, 1000.0f); + } else if (strcmp(key, "x") == 0) { + rule->x = atoi(val); + } else if (strcmp(key, "y") == 0) { + rule->y = atoi(val); + } else if (strcmp(key, "width") == 0) { + rule->width = CLAMP_INT(atoi(val), 1, INT32_MAX); + } else if (strcmp(key, "height") == 0) { + rule->height = CLAMP_INT(atoi(val), 1, INT32_MAX); + } else if (strcmp(key, "refresh") == 0) { + rule->refresh = CLAMP_FLOAT(atof(val), 0.001f, 1000.0f); + } else if (strcmp(key, "vrr") == 0) { + rule->vrr = CLAMP_INT(atoi(val), 0, 1); + } else { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "monitor rule " + "option:\033[1m\033[31m %s\n", + key); + parse_error = true; + } + } + token = strtok(NULL, ","); + } + + config->monitor_rules_count++; + return !parse_error; + } else if (strcmp(key, "tagrule") == 0) { + config->tag_rules = + realloc(config->tag_rules, + (config->tag_rules_count + 1) * sizeof(ConfigTagRule)); + if (!config->tag_rules) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for tag rules\n"); + return false; + } + + ConfigTagRule *rule = &config->tag_rules[config->tag_rules_count]; + memset(rule, 0, sizeof(ConfigTagRule)); + + // 设置默认值 + rule->id = 0; + rule->layout_name = NULL; + rule->monitor_name = NULL; + rule->nmaster = 0; + rule->mfact = 0.0f; + rule->no_render_border = 0; + rule->no_hide = 0; + + bool parse_error = false; + char *token = strtok(value, ","); + while (token != NULL) { + char *colon = strchr(token, ':'); + if (colon != NULL) { + *colon = '\0'; + char *key = token; + char *val = colon + 1; + + trim_whitespace(key); + trim_whitespace(val); + + if (strcmp(key, "id") == 0) { + rule->id = CLAMP_INT(atoi(val), 0, LENGTH(tags)); + } else if (strcmp(key, "layout_name") == 0) { + rule->layout_name = strdup(val); + } else if (strcmp(key, "monitor_name") == 0) { + rule->monitor_name = strdup(val); + } else if (strcmp(key, "no_render_border") == 0) { + rule->no_render_border = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "no_hide") == 0) { + rule->no_hide = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "nmaster") == 0) { + rule->nmaster = CLAMP_INT(atoi(val), 1, 99); + } else if (strcmp(key, "mfact") == 0) { + rule->mfact = CLAMP_FLOAT(atof(val), 0.1f, 0.9f); + } else { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "tag rule " + "option:\033[1m\033[31m %s\n", + key); + parse_error = true; + } + } + token = strtok(NULL, ","); + } + + config->tag_rules_count++; + return !parse_error; + } else if (strcmp(key, "layerrule") == 0) { + config->layer_rules = + realloc(config->layer_rules, + (config->layer_rules_count + 1) * sizeof(ConfigLayerRule)); + if (!config->layer_rules) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for layer rules\n"); + return false; + } + + ConfigLayerRule *rule = &config->layer_rules[config->layer_rules_count]; + memset(rule, 0, sizeof(ConfigLayerRule)); + + // 设置默认值 + rule->layer_name = NULL; + rule->animation_type_open = NULL; + rule->animation_type_close = NULL; + rule->noblur = 0; + rule->noanim = 0; + rule->noshadow = 0; + + bool parse_error = false; + char *token = strtok(value, ","); + while (token != NULL) { + char *colon = strchr(token, ':'); + if (colon != NULL) { + *colon = '\0'; + char *key = token; + char *val = colon + 1; + + trim_whitespace(key); + trim_whitespace(val); + + if (strcmp(key, "layer_name") == 0) { + rule->layer_name = strdup(val); + } else if (strcmp(key, "animation_type_open") == 0) { + rule->animation_type_open = strdup(val); + } else if (strcmp(key, "animation_type_close") == 0) { + rule->animation_type_close = strdup(val); + } else if (strcmp(key, "noblur") == 0) { + rule->noblur = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "noanim") == 0) { + rule->noanim = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "noshadow") == 0) { + rule->noshadow = CLAMP_INT(atoi(val), 0, 1); + } else { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "layer rule " + "option:\033[1m\033[31m %s\n", + key); + parse_error = true; + } + } + token = strtok(NULL, ","); + } + + // 如果没有指定布局名称,则使用默认值 + if (rule->layer_name == NULL) { + rule->layer_name = strdup("default"); + } + + config->layer_rules_count++; + return !parse_error; + } else if (strcmp(key, "windowrule") == 0) { + config->window_rules = + realloc(config->window_rules, + (config->window_rules_count + 1) * sizeof(ConfigWinRule)); + if (!config->window_rules) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for window rules\n"); + return false; + } + + ConfigWinRule *rule = &config->window_rules[config->window_rules_count]; + memset(rule, 0, sizeof(ConfigWinRule)); + + // int32_t rule value, relay to a client property + rule->isfloating = -1; + rule->isfullscreen = -1; + rule->isnoborder = -1; + rule->isnoshadow = -1; + rule->isnoradius = -1; + rule->isnoanimation = -1; + rule->isopensilent = -1; + rule->istagsilent = -1; + rule->isnamedscratchpad = -1; + rule->isunglobal = -1; + rule->isglobal = -1; + rule->isoverlay = -1; + rule->allow_shortcuts_inhibit = -1; + rule->ignore_maximize = -1; + rule->ignore_minimize = -1; + rule->isnosizehint = -1; + rule->isterm = -1; + rule->allow_csd = -1; + rule->force_maximize = -1; + rule->force_tearing = -1; + rule->noswallow = -1; + rule->noblur = -1; + rule->nofocus = -1; + rule->nofadein = -1; + rule->nofadeout = -1; + rule->no_force_center = -1; + + // string rule value, relay to a client property + rule->animation_type_open = NULL; + rule->animation_type_close = NULL; + + // float rule value, relay to a client property + rule->focused_opacity = 0; + rule->unfocused_opacity = 0; + rule->scroller_proportion_single = 0.0f; + rule->scroller_proportion = 0; + + // special rule value,not directly set to client property + rule->tags = 0; + rule->offsetx = 0; + rule->offsety = 0; + rule->width = 0; + rule->height = 0; + rule->monitor = NULL; + rule->id = NULL; + rule->title = NULL; + + rule->globalkeybinding = (KeyBinding){0}; + + bool parse_error = false; + char *token = strtok(value, ","); + while (token != NULL) { + char *colon = strchr(token, ':'); + if (colon != NULL) { + *colon = '\0'; + char *key = token; + char *val = colon + 1; + + trim_whitespace(key); + trim_whitespace(val); + + if (strcmp(key, "isfloating") == 0) { + rule->isfloating = atoi(val); + } else if (strcmp(key, "title") == 0) { + rule->title = strdup(val); + } else if (strcmp(key, "appid") == 0) { + rule->id = strdup(val); + } else if (strcmp(key, "animation_type_open") == 0) { + rule->animation_type_open = strdup(val); + } else if (strcmp(key, "animation_type_close") == 0) { + rule->animation_type_close = strdup(val); + } else if (strcmp(key, "tags") == 0) { + rule->tags = 1 << (atoi(val) - 1); + } else if (strcmp(key, "monitor") == 0) { + rule->monitor = strdup(val); + } else if (strcmp(key, "offsetx") == 0) { + rule->offsetx = atoi(val); + } else if (strcmp(key, "offsety") == 0) { + rule->offsety = atoi(val); + } else if (strcmp(key, "nofocus") == 0) { + rule->nofocus = atoi(val); + } else if (strcmp(key, "nofadein") == 0) { + rule->nofadein = atoi(val); + } else if (strcmp(key, "nofadeout") == 0) { + rule->nofadeout = atoi(val); + } else if (strcmp(key, "no_force_center") == 0) { + rule->no_force_center = atoi(val); + } else if (strcmp(key, "width") == 0) { + rule->width = atoi(val); + } else if (strcmp(key, "height") == 0) { + rule->height = atoi(val); + } else if (strcmp(key, "isnoborder") == 0) { + rule->isnoborder = atoi(val); + } else if (strcmp(key, "isnoshadow") == 0) { + rule->isnoshadow = atoi(val); + } else if (strcmp(key, "isnoradius") == 0) { + rule->isnoradius = atoi(val); + } else if (strcmp(key, "isnoanimation") == 0) { + rule->isnoanimation = atoi(val); + } else if (strcmp(key, "isopensilent") == 0) { + rule->isopensilent = atoi(val); + } else if (strcmp(key, "istagsilent") == 0) { + rule->istagsilent = atoi(val); + } else if (strcmp(key, "isnamedscratchpad") == 0) { + rule->isnamedscratchpad = atoi(val); + } else if (strcmp(key, "isunglobal") == 0) { + rule->isunglobal = atoi(val); + } else if (strcmp(key, "isglobal") == 0) { + rule->isglobal = atoi(val); + } else if (strcmp(key, "scroller_proportion_single") == 0) { + rule->scroller_proportion_single = atof(val); + } else if (strcmp(key, "unfocused_opacity") == 0) { + rule->unfocused_opacity = atof(val); + } else if (strcmp(key, "focused_opacity") == 0) { + rule->focused_opacity = atof(val); + } else if (strcmp(key, "isoverlay") == 0) { + rule->isoverlay = atoi(val); + } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { + rule->allow_shortcuts_inhibit = atoi(val); + } else if (strcmp(key, "ignore_maximize") == 0) { + rule->ignore_maximize = atoi(val); + } else if (strcmp(key, "ignore_minimize") == 0) { + rule->ignore_minimize = atoi(val); + } else if (strcmp(key, "isnosizehint") == 0) { + rule->isnosizehint = atoi(val); + } else if (strcmp(key, "isterm") == 0) { + rule->isterm = atoi(val); + } else if (strcmp(key, "allow_csd") == 0) { + rule->allow_csd = atoi(val); + } else if (strcmp(key, "force_maximize") == 0) { + rule->force_maximize = atoi(val); + } else if (strcmp(key, "force_tearing") == 0) { + rule->force_tearing = atoi(val); + } else if (strcmp(key, "noswallow") == 0) { + rule->noswallow = atoi(val); + } else if (strcmp(key, "noblur") == 0) { + rule->noblur = atoi(val); + } else if (strcmp(key, "scroller_proportion") == 0) { + rule->scroller_proportion = atof(val); + } else if (strcmp(key, "isfullscreen") == 0) { + rule->isfullscreen = atoi(val); + } else if (strcmp(key, "globalkeybinding") == 0) { + char mod_str[256], keysym_str[256]; + sscanf(val, "%255[^-]-%255[a-zA-Z]", mod_str, keysym_str); + trim_whitespace(mod_str); + trim_whitespace(keysym_str); + rule->globalkeybinding.mod = parse_mod(mod_str); + rule->globalkeybinding.keysymcode = + parse_key(keysym_str, false); + if (rule->globalkeybinding.mod == UINT32_MAX) { + return false; + } + if (rule->globalkeybinding.keysymcode.type == + KEY_TYPE_SYM && + rule->globalkeybinding.keysymcode.keysym == + XKB_KEY_NoSymbol) { + return false; + } + } else { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "window rule " + "option:\033[1m\033[31m %s\n", + key); + parse_error = true; + } + } + token = strtok(NULL, ","); + } + config->window_rules_count++; + return !parse_error; + } else if (strncmp(key, "env", 3) == 0) { + + char env_type[256], env_value[256]; + if (sscanf(value, "%255[^,],%255[^\n]", env_type, env_value) < 2) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid bind format: " + "\033[1m\033[31m%s\n", + value); + return false; + } + trim_whitespace(env_type); + trim_whitespace(env_value); + + ConfigEnv *env = calloc(1, sizeof(ConfigEnv)); + env->type = strdup(env_type); + env->value = strdup(env_value); + + config->env = + realloc(config->env, (config->env_count + 1) * sizeof(ConfigEnv)); + if (!config->env) { + free(env->type); + free(env->value); + free(env); + fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Failed to " + "allocate memory for env\n"); + return false; + } + + config->env[config->env_count] = env; + config->env_count++; + + } else if (strncmp(key, "exec", 9) == 0) { + char **new_exec = + realloc(config->exec, (config->exec_count + 1) * sizeof(char *)); + if (!new_exec) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for exec\n"); + return false; + } + config->exec = new_exec; + + config->exec[config->exec_count] = strdup(value); + if (!config->exec[config->exec_count]) { + fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Failed to " + "duplicate exec string\n"); + return false; + } + + config->exec_count++; + + } else if (strncmp(key, "exec-once", 9) == 0) { + + char **new_exec_once = realloc( + config->exec_once, (config->exec_once_count + 1) * sizeof(char *)); + if (!new_exec_once) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for exec_once\n"); + return false; + } + config->exec_once = new_exec_once; + + config->exec_once[config->exec_once_count] = strdup(value); + if (!config->exec_once[config->exec_once_count]) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to duplicate " + "exec_once string\n"); + return false; + } + + config->exec_once_count++; + + } else if (regex_match("^bind[s|l|r|p]*$", key)) { + config->key_bindings = + realloc(config->key_bindings, + (config->key_bindings_count + 1) * sizeof(KeyBinding)); + if (!config->key_bindings) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for key bindings\n"); + return false; + } + + KeyBinding *binding = &config->key_bindings[config->key_bindings_count]; + memset(binding, 0, sizeof(KeyBinding)); + + char mod_str[256], keysym_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[^\n]", + mod_str, keysym_str, func_name, arg_value, arg_value2, + arg_value3, arg_value4, arg_value5) < 3) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid bind format: " + "\033[1m\033[31m%s\n", + value); + return false; + } + trim_whitespace(mod_str); + trim_whitespace(keysym_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->keymode_idx = config->current_keymode_idx; + + parse_bind_flags(key, binding); + binding->keysymcode = + parse_key(keysym_str, binding->keysymcode.type == KEY_TYPE_SYM); + binding->mod = parse_mod(mod_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 || binding->mod == UINT32_MAX || + (binding->keysymcode.type == KEY_TYPE_SYM && + binding->keysymcode.keysym == XKB_KEY_NoSymbol)) { + 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; + } + if (!binding->func) + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "dispatch in bind: " + "\033[1m\033[31m%s\n", + func_name); + return false; + } else { + config->key_bindings_count++; + } + + } else if (strncmp(key, "mousebind", 9) == 0) { + config->mouse_bindings = + realloc(config->mouse_bindings, + (config->mouse_bindings_count + 1) * sizeof(MouseBinding)); + if (!config->mouse_bindings) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for mouse bindings\n"); + return false; + } + + MouseBinding *binding = + &config->mouse_bindings[config->mouse_bindings_count]; + memset(binding, 0, sizeof(MouseBinding)); + + char mod_str[256], button_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[^\n]", + mod_str, button_str, func_name, arg_value, arg_value2, + arg_value3, arg_value4, arg_value5) < 3) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid mousebind " + "format: " + "%s\n", + value); + return false; + } + trim_whitespace(mod_str); + trim_whitespace(button_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->mod = parse_mod(mod_str); + binding->button = parse_button(button_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 || binding->mod == UINT32_MAX || + binding->button == UINT32_MAX) { + 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; + } + if (!binding->func) + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "dispatch in " + "mousebind: \033[1m\033[31m%s\n", + func_name); + return false; + } else { + config->mouse_bindings_count++; + } + } else if (strncmp(key, "axisbind", 8) == 0) { + config->axis_bindings = + realloc(config->axis_bindings, + (config->axis_bindings_count + 1) * sizeof(AxisBinding)); + if (!config->axis_bindings) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for axis bindings\n"); + return false; + } + + AxisBinding *binding = + &config->axis_bindings[config->axis_bindings_count]; + memset(binding, 0, sizeof(AxisBinding)); + + char mod_str[256], dir_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[^\n]", + mod_str, dir_str, func_name, arg_value, arg_value2, + arg_value3, arg_value4, arg_value5) < 3) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid axisbind " + "format: %s\n", + value); + return false; + } + + trim_whitespace(mod_str); + trim_whitespace(dir_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->mod = parse_mod(mod_str); + binding->dir = parse_direction(dir_str); + 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 || binding->mod == UINT32_MAX) { + 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; + } + if (!binding->func) + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "dispatch in " + "axisbind: \033[1m\033[31m%s\n", + func_name); + return false; + } else { + config->axis_bindings_count++; + } + + } else if (strncmp(key, "switchbind", 10) == 0) { + config->switch_bindings = realloc(config->switch_bindings, + (config->switch_bindings_count + 1) * + sizeof(SwitchBinding)); + if (!config->switch_bindings) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for switch bindings\n"); + return false; + } + + SwitchBinding *binding = + &config->switch_bindings[config->switch_bindings_count]; + memset(binding, 0, sizeof(SwitchBinding)); + + char fold_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[" + "^\n]", + fold_str, func_name, arg_value, arg_value2, arg_value3, + arg_value4, arg_value5) < 3) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid switchbind " + "format: %s\n", + value); + return false; + } + trim_whitespace(fold_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->fold = parse_fold_state(fold_str); + 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 " + "switchbind: " + "\033[1m\033[31m%s\n", + func_name); + return false; + } else { + config->switch_bindings_count++; + } + + } else if (strncmp(key, "gesturebind", 11) == 0) { + config->gesture_bindings = realloc( + config->gesture_bindings, + (config->gesture_bindings_count + 1) * sizeof(GestureBinding)); + if (!config->gesture_bindings) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " + "memory for axis gesturebind\n"); + return false; + } + + 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] = "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[^\n]", + mod_str, motion_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 gesturebind " + "format: %s\n", + value); + return false; + } + + trim_whitespace(mod_str); + trim_whitespace(motion_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->mod = parse_mod(mod_str); + binding->motion = parse_direction(motion_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 || binding->mod == UINT32_MAX) { + 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; + } + if (!binding->func) + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown " + "dispatch in " + "axisbind: \033[1m\033[31m%s\n", + func_name); + return false; + } else { + config->gesture_bindings_count++; + } + + } else if (strncmp(key, "source", 6) == 0) { + parse_config_file(config, value); + } else { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Unknown keyword: " + "\033[1m\033[31m%s\n", + key); + return false; + } + + return true; +} + +bool parse_config_line(Config *config, const char *line) { + char key[256], value[256]; + if (sscanf(line, "%255[^=]=%255[^\n]", key, value) != 2) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid line format: %s", + line); + return false; + } + + // Then trim each part separately + trim_whitespace(key); + trim_whitespace(value); + + return parse_option(config, key, value); +} + +void parse_config_file(Config *config, const char *file_path) { + FILE *file; + char full_path[1024]; + + if (file_path[0] == '.' && file_path[1] == '/') { + // Relative path + + if (server.cli_config_path) { + char *config_path = strdup(server.cli_config_path); + char *config_dir = dirname(config_path); + snprintf(full_path, sizeof(full_path), "%s/%s", config_dir, + file_path + 1); + free(config_path); + } else { + const char *home = getenv("HOME"); + if (!home) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m HOME environment " + "variable not set.\n"); + return; + } + snprintf(full_path, sizeof(full_path), "%s/.config/mango/%s", home, + file_path + 1); + } + file = fopen(full_path, "r"); + + } else if (file_path[0] == '~' && + (file_path[1] == '/' || file_path[1] == '\0')) { + // Home directory + + const char *home = getenv("HOME"); + if (!home) { + fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m HOME environment " + "variable not set.\n"); + return; + } + snprintf(full_path, sizeof(full_path), "%s%s", home, file_path + 1); + file = fopen(full_path, "r"); + + } else { + // Absolute path + file = fopen(file_path, "r"); + } + + if (!file) { + fprintf(stderr, + "\033[1;31m\033[1;33m[ERROR]:\033[0m Failed to open " + "config file: %s\n", + file_path); + return; + } + + char line[512]; + bool parse_correct = true; + uint32_t line_count = 0; + while (fgets(line, sizeof(line), file)) { + line_count++; + if (line[0] == '#' || line[0] == '\n') { + continue; + } + parse_correct = parse_config_line(config, line); + if (!parse_correct) { + fprintf(stderr, + "\033[1;31m╰─\033[1;33m[Index]\033[0m " + "\033[1;36m%s\033[0m:\033[1;35m%d\033[0m\n" + " \033[1;36m╰─\033[0;33m%s\033[0m\n\n", + file_path, line_count, line); + } + } + + fclose(file); +} + +void free_circle_layout(Config *config) { + if (config->circle_layout) { + // 释放每个字符串 + for (int32_t i = 0; i < config->circle_layout_count; i++) { + if (config->circle_layout[i]) { + free(config->circle_layout[i]); // 释放单个字符串 + config->circle_layout[i] = NULL; // 防止野指针 + } + } + // 释放 circle_layout 数组本身 + free(config->circle_layout); + config->circle_layout = NULL; // 防止野指针 + } + config->circle_layout_count = 0; // 重置计数 +} + +void free_baked_points(void) { + if (server.baked_points_move) { + free(server.baked_points_move); + server.baked_points_move = NULL; + } + if (server.baked_points_open) { + free(server.baked_points_open); + server.baked_points_open = NULL; + } + if (server.baked_points_close) { + free(server.baked_points_close); + server.baked_points_close = NULL; + } + if (server.baked_points_tag) { + free(server.baked_points_tag); + server.baked_points_tag = NULL; + } + if (server.baked_points_focus) { + free(server.baked_points_focus); + server.baked_points_focus = NULL; + } + if (server.baked_points_opafadein) { + free(server.baked_points_opafadein); + server.baked_points_opafadein = NULL; + } + if (server.baked_points_opafadeout) { + free(server.baked_points_opafadeout); + server.baked_points_opafadeout = NULL; + } +} + +void free_config(void) { + // 释放内存 + int32_t i; + + // 释放 window_rules + if (config.window_rules) { + for (int32_t i = 0; i < config.window_rules_count; i++) { + ConfigWinRule *rule = &config.window_rules[i]; + if (rule->id) + free((void *)rule->id); + if (rule->title) + free((void *)rule->title); + if (rule->animation_type_open) + free((void *)rule->animation_type_open); + if (rule->animation_type_close) + free((void *)rule->animation_type_close); + if (rule->monitor) + free((void *)rule->monitor); + rule->id = NULL; + rule->title = NULL; + rule->animation_type_open = NULL; + rule->animation_type_close = NULL; + rule->monitor = NULL; + // 释放 globalkeybinding 的 arg.v(如果动态分配) + if (rule->globalkeybinding.arg.v) { + free((void *)rule->globalkeybinding.arg.v); + } + } + free(config.window_rules); + config.window_rules = NULL; + config.window_rules_count = 0; + } + + // 释放 key_bindings + if (config.key_bindings) { + for (i = 0; i < config.key_bindings_count; i++) { + if (config.key_bindings[i].arg.v) { + free((void *)config.key_bindings[i].arg.v); + config.key_bindings[i].arg.v = NULL; + } + if (config.key_bindings[i].arg.v2) { + free((void *)config.key_bindings[i].arg.v2); + config.key_bindings[i].arg.v2 = NULL; + } + if (config.key_bindings[i].arg.v3) { + free((void *)config.key_bindings[i].arg.v3); + config.key_bindings[i].arg.v3 = NULL; + } + } + free(config.key_bindings); + config.key_bindings = NULL; + config.key_bindings_count = 0; + } + + // 释放 mouse_bindings + if (config.mouse_bindings) { + for (i = 0; i < config.mouse_bindings_count; i++) { + if (config.mouse_bindings[i].arg.v) { + free((void *)config.mouse_bindings[i].arg.v); + config.mouse_bindings[i].arg.v = NULL; + } + if (config.mouse_bindings[i].arg.v2) { + free((void *)config.mouse_bindings[i].arg.v2); + config.mouse_bindings[i].arg.v2 = NULL; + } + if (config.mouse_bindings[i].arg.v3) { + free((void *)config.mouse_bindings[i].arg.v3); + config.mouse_bindings[i].arg.v3 = NULL; + } + } + free(config.mouse_bindings); + config.mouse_bindings = NULL; + config.mouse_bindings_count = 0; + } + + // 释放 axis_bindings + if (config.axis_bindings) { + for (i = 0; i < config.axis_bindings_count; i++) { + if (config.axis_bindings[i].arg.v) { + free((void *)config.axis_bindings[i].arg.v); + config.axis_bindings[i].arg.v = NULL; + } + if (config.axis_bindings[i].arg.v2) { + free((void *)config.axis_bindings[i].arg.v2); + config.axis_bindings[i].arg.v2 = NULL; + } + if (config.axis_bindings[i].arg.v3) { + free((void *)config.axis_bindings[i].arg.v3); + config.axis_bindings[i].arg.v3 = NULL; + } + } + free(config.axis_bindings); + config.axis_bindings = NULL; + config.axis_bindings_count = 0; + } + + // 释放 switch_bindings + if (config.switch_bindings) { + for (i = 0; i < config.switch_bindings_count; i++) { + if (config.switch_bindings[i].arg.v) { + free((void *)config.switch_bindings[i].arg.v); + config.switch_bindings[i].arg.v = NULL; + } + if (config.switch_bindings[i].arg.v2) { + free((void *)config.switch_bindings[i].arg.v2); + config.switch_bindings[i].arg.v2 = NULL; + } + if (config.switch_bindings[i].arg.v3) { + free((void *)config.switch_bindings[i].arg.v3); + config.switch_bindings[i].arg.v3 = NULL; + } + } + free(config.switch_bindings); + config.switch_bindings = NULL; + config.switch_bindings_count = 0; + } + + // 释放 gesture_bindings + if (config.gesture_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; + } + if (config.gesture_bindings[i].arg.v2) { + free((void *)config.gesture_bindings[i].arg.v2); + config.gesture_bindings[i].arg.v2 = NULL; + } + if (config.gesture_bindings[i].arg.v3) { + free((void *)config.gesture_bindings[i].arg.v3); + config.gesture_bindings[i].arg.v3 = NULL; + } + } + free(config.gesture_bindings); + config.gesture_bindings = NULL; + config.gesture_bindings_count = 0; + } + + // 释放 tag_rules + if (config.tag_rules) { + for (int32_t i = 0; i < config.tag_rules_count; i++) { + if (config.tag_rules[i].layout_name) + free((void *)config.tag_rules[i].layout_name); + if (config.tag_rules[i].monitor_name) + free((void *)config.tag_rules[i].monitor_name); + } + free(config.tag_rules); + config.tag_rules = NULL; + config.tag_rules_count = 0; + } + + // 释放 monitor_rules + if (config.monitor_rules) { + for (int32_t i = 0; i < config.monitor_rules_count; i++) { + if (config.monitor_rules[i].name) + free((void *)config.monitor_rules[i].name); + } + free(config.monitor_rules); + config.monitor_rules = NULL; + config.monitor_rules_count = 0; + } + + // 释放 layer_rules + if (config.layer_rules) { + for (int32_t i = 0; i < config.layer_rules_count; i++) { + if (config.layer_rules[i].layer_name) + free((void *)config.layer_rules[i].layer_name); + if (config.layer_rules[i].animation_type_open) + free((void *)config.layer_rules[i].animation_type_open); + if (config.layer_rules[i].animation_type_close) + free((void *)config.layer_rules[i].animation_type_close); + } + free(config.layer_rules); + config.layer_rules = NULL; + config.layer_rules_count = 0; + } + + // 释放 env + if (config.env) { + for (int32_t i = 0; i < config.env_count; i++) { + if (config.env[i]->type) { + free((void *)config.env[i]->type); + } + if (config.env[i]->value) { + free((void *)config.env[i]->value); + } + free(config.env[i]); + } + free(config.env); + config.env = NULL; + config.env_count = 0; + } + + // 释放 exec + if (config.exec) { + for (i = 0; i < config.exec_count; i++) { + free(config.exec[i]); + } + free(config.exec); + config.exec = NULL; + config.exec_count = 0; + } + + // 释放 exec_once + if (config.exec_once) { + for (i = 0; i < config.exec_once_count; i++) { + free(config.exec_once[i]); + } + free(config.exec_once); + config.exec_once = NULL; + config.exec_once_count = 0; + } + + // 释放 scroller_proportion_preset + if (config.scroller_proportion_preset) { + free(config.scroller_proportion_preset); + config.scroller_proportion_preset = NULL; + config.scroller_proportion_preset_count = 0; + } + + if (config.cursor_theme) { + free(config.cursor_theme); + config.cursor_theme = NULL; + } + + if (config.keymodes) { + for (int32_t i = 0; i < config.keymodes_count; i++) { + free(config.keymodes[i].name); + } + free(config.keymodes); + config.keymodes = NULL; + config.keymodes_count = 0; + config.current_keymode_idx = -1; + } + + // 释放 circle_layout + free_circle_layout(&config); + + // 释放动画资源 + free_baked_points(); + + // 清理解析按键用的keymap + cleanup_config_keymap(); +} + +void override_config(void) { + // 动画启用 + animations = CLAMP_INT(config.animations, 0, 1); + layer_animations = CLAMP_INT(config.layer_animations, 0, 1); + + // 标签动画方向 + tag_animation_direction = CLAMP_INT(config.tag_animation_direction, 0, 1); + + // 动画淡入淡出设置 + animation_fade_in = CLAMP_INT(config.animation_fade_in, 0, 1); + animation_fade_out = CLAMP_INT(config.animation_fade_out, 0, 1); + zoom_initial_ratio = CLAMP_FLOAT(config.zoom_initial_ratio, 0.1f, 1.0f); + zoom_end_ratio = CLAMP_FLOAT(config.zoom_end_ratio, 0.1f, 1.0f); + fadein_begin_opacity = CLAMP_FLOAT(config.fadein_begin_opacity, 0.0f, 1.0f); + fadeout_begin_opacity = + CLAMP_FLOAT(config.fadeout_begin_opacity, 0.0f, 1.0f); + + // 打开关闭动画类型 + animation_type_open = config.animation_type_open; + animation_type_close = config.animation_type_close; + + // layer打开关闭动画类型 + layer_animation_type_open = config.layer_animation_type_open; + layer_animation_type_close = config.layer_animation_type_close; + + // 动画时间限制在合理范围(1-50000ms) + animation_duration_move = + CLAMP_INT(config.animation_duration_move, 1, 50000); + animation_duration_open = + CLAMP_INT(config.animation_duration_open, 1, 50000); + animation_duration_tag = CLAMP_INT(config.animation_duration_tag, 1, 50000); + animation_duration_close = + CLAMP_INT(config.animation_duration_close, 1, 50000); + animation_duration_focus = + CLAMP_INT(config.animation_duration_focus, 1, 50000); + + // 滚动布局设置 + scroller_default_proportion = + CLAMP_FLOAT(config.scroller_default_proportion, 0.1f, 1.0f); + scroller_default_proportion_single = + CLAMP_FLOAT(config.scroller_default_proportion_single, 0.1f, 1.0f); + scroller_ignore_proportion_single = + CLAMP_INT(config.scroller_ignore_proportion_single, 0, 1); + scroller_focus_center = CLAMP_INT(config.scroller_focus_center, 0, 1); + scroller_prefer_center = CLAMP_INT(config.scroller_prefer_center, 0, 1); + edge_scroller_pointer_focus = + CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1); + scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000); + + // 主从布局设置 + default_mfact = CLAMP_FLOAT(config.default_mfact, 0.1f, 0.9f); + default_nmaster = CLAMP_INT(config.default_nmaster, 1, 1000); + center_master_overspread = CLAMP_INT(config.center_master_overspread, 0, 1); + center_when_single_stack = CLAMP_INT(config.center_when_single_stack, 0, 1); + new_is_master = CLAMP_INT(config.new_is_master, 0, 1); + + // 概述模式设置 + hotarea_size = CLAMP_INT(config.hotarea_size, 1, 1000); + hotarea_corner = CLAMP_INT(config.hotarea_corner, 0, 3); + enable_hotarea = CLAMP_INT(config.enable_hotarea, 0, 1); + ov_tab_mode = CLAMP_INT(config.ov_tab_mode, 0, 1); + overviewgappi = CLAMP_INT(config.overviewgappi, 0, 1000); + overviewgappo = CLAMP_INT(config.overviewgappo, 0, 1000); + + // 杂项设置 + xwayland_persistence = CLAMP_INT(config.xwayland_persistence, 0, 1); + syncobj_enable = CLAMP_INT(config.syncobj_enable, 0, 1); + allow_tearing = CLAMP_INT(config.allow_tearing, 0, 2); + allow_shortcuts_inhibit = CLAMP_INT(config.allow_shortcuts_inhibit, 0, 1); + allow_lock_transparent = CLAMP_INT(config.allow_lock_transparent, 0, 1); + axis_bind_apply_timeout = + CLAMP_INT(config.axis_bind_apply_timeout, 0, 1000); + focus_on_activate = CLAMP_INT(config.focus_on_activate, 0, 1); + idleinhibit_ignore_visible = + CLAMP_INT(config.idleinhibit_ignore_visible, 0, 1); + sloppyfocus = CLAMP_INT(config.sloppyfocus, 0, 1); + warpcursor = CLAMP_INT(config.warpcursor, 0, 1); + drag_corner = CLAMP_INT(config.drag_corner, 0, 4); + drag_warp_cursor = CLAMP_INT(config.drag_warp_cursor, 0, 1); + focus_cross_monitor = CLAMP_INT(config.focus_cross_monitor, 0, 1); + exchange_cross_monitor = CLAMP_INT(config.exchange_cross_monitor, 0, 1); + scratchpad_cross_monitor = CLAMP_INT(config.scratchpad_cross_monitor, 0, 1); + focus_cross_tag = CLAMP_INT(config.focus_cross_tag, 0, 1); + view_current_to_back = CLAMP_INT(config.view_current_to_back, 0, 1); + enable_floating_snap = CLAMP_INT(config.enable_floating_snap, 0, 1); + snap_distance = CLAMP_INT(config.snap_distance, 0, 99999); + cursor_size = CLAMP_INT(config.cursor_size, 4, 512); + no_border_when_single = CLAMP_INT(config.no_border_when_single, 0, 1); + no_radius_when_single = CLAMP_INT(config.no_radius_when_single, 0, 1); + cursor_hide_timeout = + CLAMP_INT(config.cursor_hide_timeout, 0, 36000); // 0-10小时 + drag_tile_to_tile = CLAMP_INT(config.drag_tile_to_tile, 0, 1); + single_scratchpad = CLAMP_INT(config.single_scratchpad, 0, 1); + + // 键盘设置 + repeat_rate = CLAMP_INT(config.repeat_rate, 1, 1000); + repeat_delay = CLAMP_INT(config.repeat_delay, 1, 20000); + numlockon = CLAMP_INT(config.numlockon, 0, 1); + + // 触控板设置 + disable_trackpad = CLAMP_INT(config.disable_trackpad, 0, 1); + tap_to_click = CLAMP_INT(config.tap_to_click, 0, 1); + tap_and_drag = CLAMP_INT(config.tap_and_drag, 0, 1); + drag_lock = CLAMP_INT(config.drag_lock, 0, 1); + trackpad_natural_scrolling = + CLAMP_INT(config.trackpad_natural_scrolling, 0, 1); + disable_while_typing = CLAMP_INT(config.disable_while_typing, 0, 1); + left_handed = CLAMP_INT(config.left_handed, 0, 1); + middle_button_emulation = CLAMP_INT(config.middle_button_emulation, 0, 1); + swipe_min_threshold = CLAMP_INT(config.swipe_min_threshold, 1, 1000); + + // 鼠标设置 + mouse_natural_scrolling = CLAMP_INT(config.mouse_natural_scrolling, 0, 1); + accel_profile = CLAMP_INT(config.accel_profile, 0, 2); + accel_speed = CLAMP_FLOAT(config.accel_speed, -1.0f, 1.0f); + scroll_method = CLAMP_INT(config.scroll_method, 0, 4); + scroll_button = CLAMP_INT(config.scroll_button, 272, 276); + click_method = CLAMP_INT(config.click_method, 0, 2); + send_events_mode = CLAMP_INT(config.send_events_mode, 0, 2); + button_map = CLAMP_INT(config.button_map, 0, 1); + axis_scroll_factor = CLAMP_FLOAT(config.axis_scroll_factor, 0.1f, 10.0f); + + // 外观设置 + gappih = CLAMP_INT(config.gappih, 0, 1000); + gappiv = CLAMP_INT(config.gappiv, 0, 1000); + gappoh = CLAMP_INT(config.gappoh, 0, 1000); + gappov = CLAMP_INT(config.gappov, 0, 1000); + scratchpad_width_ratio = + CLAMP_FLOAT(config.scratchpad_width_ratio, 0.1f, 1.0f); + scratchpad_height_ratio = + CLAMP_FLOAT(config.scratchpad_height_ratio, 0.1f, 1.0f); + borderpx = CLAMP_INT(config.borderpx, 0, 200); + smartgaps = CLAMP_INT(config.smartgaps, 0, 1); + + blur = CLAMP_INT(config.blur, 0, 1); + blur_layer = CLAMP_INT(config.blur_layer, 0, 1); + blur_optimized = CLAMP_INT(config.blur_optimized, 0, 1); + border_radius = CLAMP_INT(config.border_radius, 0, 100); + blur_params.num_passes = CLAMP_INT(config.blur_params.num_passes, 0, 10); + blur_params.radius = CLAMP_INT(config.blur_params.radius, 0, 100); + blur_params.noise = CLAMP_FLOAT(config.blur_params.noise, 0, 1); + blur_params.brightness = CLAMP_FLOAT(config.blur_params.brightness, 0, 1); + blur_params.contrast = CLAMP_FLOAT(config.blur_params.contrast, 0, 1); + blur_params.saturation = CLAMP_FLOAT(config.blur_params.saturation, 0, 1); + shadows = CLAMP_INT(config.shadows, 0, 1); + shadow_only_floating = CLAMP_INT(config.shadow_only_floating, 0, 1); + layer_shadows = CLAMP_INT(config.layer_shadows, 0, 1); + shadows_size = CLAMP_INT(config.shadows_size, 0, 100); + shadows_blur = CLAMP_INT(config.shadows_blur, 0, 100); + shadows_position_x = CLAMP_INT(config.shadows_position_x, -1000, 1000); + shadows_position_y = CLAMP_INT(config.shadows_position_y, -1000, 1000); + focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f); + unfocused_opacity = CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f); + memcpy(shadowscolor, config.shadowscolor, sizeof(shadowscolor)); + + // 复制颜色数组 + memcpy(rootcolor, config.rootcolor, sizeof(rootcolor)); + memcpy(bordercolor, config.bordercolor, sizeof(bordercolor)); + memcpy(focuscolor, config.focuscolor, sizeof(focuscolor)); + memcpy(maximizescreencolor, config.maximizescreencolor, + sizeof(maximizescreencolor)); + memcpy(urgentcolor, config.urgentcolor, sizeof(urgentcolor)); + memcpy(scratchpadcolor, config.scratchpadcolor, sizeof(scratchpadcolor)); + memcpy(globalcolor, config.globalcolor, sizeof(globalcolor)); + memcpy(overlaycolor, config.overlaycolor, sizeof(overlaycolor)); + + // 复制动画曲线 + memcpy(animation_curve_move, config.animation_curve_move, + sizeof(animation_curve_move)); + memcpy(animation_curve_open, config.animation_curve_open, + sizeof(animation_curve_open)); + memcpy(animation_curve_tag, config.animation_curve_tag, + sizeof(animation_curve_tag)); + memcpy(animation_curve_close, config.animation_curve_close, + sizeof(animation_curve_close)); + memcpy(animation_curve_focus, config.animation_curve_focus, + sizeof(animation_curve_focus)); + memcpy(animation_curve_opafadein, config.animation_curve_opafadein, + sizeof(animation_curve_opafadein)); + memcpy(animation_curve_opafadeout, config.animation_curve_opafadeout, + sizeof(animation_curve_opafadeout)); +} + +void set_value_default() { + /* animaion */ + config.animations = animations; // 是否启用动画 + config.layer_animations = layer_animations; // 是否启用layer动画 + config.animation_fade_in = animation_fade_in; // Enable animation fade in + config.animation_fade_out = animation_fade_out; // Enable animation fade out + config.tag_animation_direction = tag_animation_direction; // 标签动画方向 + config.zoom_initial_ratio = zoom_initial_ratio; // 动画起始窗口比例 + config.zoom_end_ratio = zoom_end_ratio; // 动画结束窗口比例 + config.fadein_begin_opacity = + fadein_begin_opacity; // Begin opac window ratio for animations + config.fadeout_begin_opacity = fadeout_begin_opacity; + config.animation_duration_move = + animation_duration_move; // Animation move speed + config.animation_duration_open = + animation_duration_open; // Animation open speed + config.animation_duration_tag = + animation_duration_tag; // Animation tag speed + config.animation_duration_close = + animation_duration_close; // Animation tag speed + config.animation_duration_focus = + animation_duration_focus; // Animation focus opacity speed + + /* appearance */ + config.axis_bind_apply_timeout = + axis_bind_apply_timeout; // 滚轮绑定动作的触发的时间间隔 + config.focus_on_activate = + focus_on_activate; // 收到窗口激活请求是否自动跳转聚焦 + config.new_is_master = new_is_master; // 新窗口是否插在头部 + config.default_mfact = default_mfact; // master 窗口比例 + config.default_nmaster = default_nmaster; // 默认master数量 + config.center_master_overspread = + center_master_overspread; // 中心master时是否铺满 + config.center_when_single_stack = + center_when_single_stack; // 单个stack时是否居中 + + config.numlockon = numlockon; // 是否打开右边小键盘 + + config.ov_tab_mode = ov_tab_mode; // alt tab切换模式 + config.hotarea_size = hotarea_size; // 热区大小,10x10 + config.hotarea_corner = hotarea_corner; + config.enable_hotarea = enable_hotarea; // 是否启用鼠标热区 + config.smartgaps = smartgaps; /* 1 means no outer gap when there is + only one window */ + config.sloppyfocus = sloppyfocus; /* focus follows mouse */ + config.gappih = gappih; /* horiz inner gap between windows */ + config.gappiv = gappiv; /* vert inner gap between windows */ + config.gappoh = + gappoh; /* horiz outer gap between windows and screen edge */ + config.gappov = gappov; /* vert outer gap between windows and screen edge */ + config.scratchpad_width_ratio = scratchpad_width_ratio; + config.scratchpad_height_ratio = scratchpad_height_ratio; + + config.scroller_structs = scroller_structs; + config.scroller_default_proportion = scroller_default_proportion; + config.scroller_default_proportion_single = + scroller_default_proportion_single; + config.scroller_ignore_proportion_single = + scroller_ignore_proportion_single; + config.scroller_focus_center = scroller_focus_center; + config.scroller_prefer_center = scroller_prefer_center; + config.edge_scroller_pointer_focus = edge_scroller_pointer_focus; + config.focus_cross_monitor = focus_cross_monitor; + config.exchange_cross_monitor = exchange_cross_monitor; + config.scratchpad_cross_monitor = scratchpad_cross_monitor; + config.focus_cross_tag = focus_cross_tag; + config.axis_scroll_factor = axis_scroll_factor; + config.view_current_to_back = view_current_to_back; + config.single_scratchpad = single_scratchpad; + config.xwayland_persistence = xwayland_persistence; + config.syncobj_enable = syncobj_enable; + config.allow_tearing = allow_tearing; + config.allow_shortcuts_inhibit = allow_shortcuts_inhibit; + config.allow_lock_transparent = allow_lock_transparent; + config.no_border_when_single = no_border_when_single; + config.no_radius_when_single = no_radius_when_single; + config.snap_distance = snap_distance; + config.drag_tile_to_tile = drag_tile_to_tile; + config.enable_floating_snap = enable_floating_snap; + config.swipe_min_threshold = swipe_min_threshold; + + config.idleinhibit_ignore_visible = + idleinhibit_ignore_visible; /* 1 means idle inhibitors will + disable idle tracking even if it's + surface isn't visible + */ + + config.borderpx = borderpx; + config.overviewgappi = overviewgappi; /* overview时 窗口与边缘 缝隙大小 */ + config.overviewgappo = overviewgappo; /* overview时 窗口与窗口 缝隙大小 */ + config.cursor_hide_timeout = cursor_hide_timeout; + + config.warpcursor = warpcursor; /* Warp server.cursor to focused client */ + config.drag_corner = drag_corner; + config.drag_warp_cursor = drag_warp_cursor; + + config.repeat_rate = repeat_rate; + config.repeat_delay = repeat_delay; + + /* Trackpad */ + config.disable_trackpad = disable_trackpad; + config.tap_to_click = tap_to_click; + config.tap_and_drag = tap_and_drag; + config.drag_lock = drag_lock; + config.mouse_natural_scrolling = mouse_natural_scrolling; + config.cursor_size = cursor_size; + config.trackpad_natural_scrolling = trackpad_natural_scrolling; + config.disable_while_typing = disable_while_typing; + config.left_handed = left_handed; + config.middle_button_emulation = middle_button_emulation; + config.accel_profile = accel_profile; + config.accel_speed = accel_speed; + config.scroll_method = scroll_method; + config.scroll_button = scroll_button; + config.click_method = click_method; + config.send_events_mode = send_events_mode; + config.button_map = button_map; + + config.blur = blur; + config.blur_layer = blur_layer; + config.blur_optimized = blur_optimized; + config.border_radius = border_radius; + config.blur_params.num_passes = blur_params_num_passes; + config.blur_params.radius = blur_params_radius; + config.blur_params.noise = blur_params_noise; + config.blur_params.brightness = blur_params_brightness; + config.blur_params.contrast = blur_params_contrast; + config.blur_params.saturation = blur_params_saturation; + config.shadows = shadows; + config.shadow_only_floating = shadow_only_floating; + config.layer_shadows = layer_shadows; + config.shadows_size = shadows_size; + config.shadows_blur = shadows_blur; + config.shadows_position_x = shadows_position_x; + config.shadows_position_y = shadows_position_y; + config.focused_opacity = focused_opacity; + config.unfocused_opacity = unfocused_opacity; + memcpy(config.shadowscolor, shadowscolor, sizeof(shadowscolor)); + + memcpy(config.animation_curve_move, animation_curve_move, + sizeof(animation_curve_move)); + memcpy(config.animation_curve_open, animation_curve_open, + sizeof(animation_curve_open)); + memcpy(config.animation_curve_tag, animation_curve_tag, + sizeof(animation_curve_tag)); + memcpy(config.animation_curve_close, animation_curve_close, + sizeof(animation_curve_close)); + memcpy(config.animation_curve_focus, animation_curve_focus, + sizeof(animation_curve_focus)); + memcpy(config.animation_curve_opafadein, animation_curve_opafadein, + sizeof(animation_curve_opafadein)); + memcpy(config.animation_curve_opafadeout, animation_curve_opafadeout, + sizeof(animation_curve_opafadeout)); + + memcpy(config.rootcolor, rootcolor, sizeof(rootcolor)); + memcpy(config.bordercolor, bordercolor, sizeof(bordercolor)); + memcpy(config.focuscolor, focuscolor, sizeof(focuscolor)); + memcpy(config.maximizescreencolor, maximizescreencolor, + sizeof(maximizescreencolor)); + memcpy(config.urgentcolor, urgentcolor, sizeof(urgentcolor)); + memcpy(config.scratchpadcolor, scratchpadcolor, sizeof(scratchpadcolor)); + memcpy(config.globalcolor, globalcolor, sizeof(globalcolor)); + memcpy(config.overlaycolor, overlaycolor, sizeof(overlaycolor)); +} + +void set_default_key_bindings(Config *config) { + // 确保common模式存在 + int32_t common_idx = find_or_add_keymode(config, "common"); + + // 计算默认按键绑定的数量 + size_t default_key_bindings_count = + sizeof(default_key_bindings) / sizeof(KeyBinding); + + // 重新分配内存以容纳新的默认按键绑定 + config->key_bindings = + realloc(config->key_bindings, + (config->key_bindings_count + default_key_bindings_count) * + sizeof(KeyBinding)); + if (!config->key_bindings) { + return; + } + + // 将默认按键绑定复制到配置的按键绑定数组中 + for (size_t i = 0; i < default_key_bindings_count; i++) { + config->key_bindings[config->key_bindings_count + i] = + default_key_bindings[i]; + // 设置keymode为common模式 + config->key_bindings[config->key_bindings_count + i].keymode_idx = + common_idx; + // 设置其他标志 + config->key_bindings[config->key_bindings_count + i].islockapply = true; + } + + // 更新按键绑定的总数 + config->key_bindings_count += default_key_bindings_count; +} + +void parse_config(void) { + + char filename[1024]; + + free_config(); + + // 重置config结构体,确保所有指针初始化为NULL + memset(&config, 0, sizeof(config)); + memset(&xkb_rules_rules, 0, sizeof(xkb_rules_rules)); + memset(&xkb_rules_model, 0, sizeof(xkb_rules_model)); + memset(&xkb_rules_layout, 0, sizeof(xkb_rules_layout)); + memset(&xkb_rules_variant, 0, sizeof(xkb_rules_variant)); + memset(&xkb_rules_options, 0, sizeof(xkb_rules_options)); + + // 初始化动态数组的指针为NULL,避免野指针 + config.keymodes = NULL; + config.keymodes_count = 0; + config.current_keymode_idx = -1; + config.window_rules = NULL; + config.window_rules_count = 0; + config.monitor_rules = NULL; + config.monitor_rules_count = 0; + config.key_bindings = NULL; + config.key_bindings_count = 0; + config.mouse_bindings = NULL; + config.mouse_bindings_count = 0; + config.axis_bindings = NULL; + config.axis_bindings_count = 0; + config.switch_bindings = NULL; + config.switch_bindings_count = 0; + config.gesture_bindings = NULL; + config.gesture_bindings_count = 0; + config.env = NULL; + config.env_count = 0; + config.exec = NULL; + config.exec_count = 0; + config.exec_once = NULL; + config.exec_once_count = 0; + config.scroller_proportion_preset = NULL; + config.scroller_proportion_preset_count = 0; + config.circle_layout = NULL; + config.circle_layout_count = 0; + config.tag_rules = NULL; + config.tag_rules_count = 0; + config.cursor_theme = NULL; + + create_config_keymap(); + + if (server.cli_config_path) { + snprintf(filename, sizeof(filename), "%s", server.cli_config_path); + } else { + // 获取当前用户家目录 + const char *homedir = getenv("HOME"); + if (!homedir) { + // 如果获取失败,则无法继续 + return; + } + // 构建日志文件路径 + snprintf(filename, sizeof(filename), "%s/.config/mango/config.conf", + homedir); + + // 检查文件是否存在 + if (access(filename, F_OK) != 0) { + // 如果文件不存在,则使用 /etc/mango/config.conf + snprintf(filename, sizeof(filename), "%s/mango/config.conf", + SYSCONFDIR); + } + } + + set_value_default(); + parse_config_file(&config, filename); + set_current_keymode(&config, "default"); + set_default_key_bindings(&config); + override_config(); +} + +void reset_blur_params(void) { + if (blur) { + Monitor *m = NULL; + wl_list_for_each(m, &server.mons, link) { + if (m->blur != NULL) { + wlr_scene_node_destroy(&m->blur->node); + } + m->blur = + wlr_scene_optimized_blur_create(&server.scene->tree, 0, 0); + wlr_scene_node_reparent(&m->blur->node, server.layers[LyrBlur]); + wlr_scene_optimized_blur_set_size(m->blur, m->m.width, m->m.height); + wlr_scene_set_blur_data( + server.scene, blur_params.num_passes, blur_params.radius, + blur_params.noise, blur_params.brightness, blur_params.contrast, + blur_params.saturation); + } + } else { + Monitor *m = NULL; + wl_list_for_each(m, &server.mons, link) { + + if (m->blur) { + wlr_scene_node_destroy(&m->blur->node); + m->blur = NULL; + } + } + } +} + +void reapply_monitor_rules(void) { + ConfigMonitorRule *mr; + Monitor *m = NULL; + int32_t ji, vrr; + int32_t mx, my; + struct wlr_output_state state; + struct wlr_output_mode *internal_mode = NULL; + wlr_output_state_init(&state); + + wl_list_for_each(m, &server.mons, link) { + if (!m->wlr_output->enabled) { + continue; + } + + for (ji = 0; ji < config.monitor_rules_count; ji++) { + if (config.monitor_rules_count < 1) + break; + + mr = &config.monitor_rules[ji]; + if (regex_match(mr->name, m->wlr_output->name)) { + + mx = mr->x == INT32_MAX ? m->m.x : mr->x; + my = mr->y == INT32_MAX ? m->m.y : mr->y; + vrr = mr->vrr >= 0 ? mr->vrr : 0; + + if (mr->width > 0 && mr->height > 0 && mr->refresh > 0) { + internal_mode = get_nearest_output_mode( + m->wlr_output, mr->width, mr->height, mr->refresh); + if (internal_mode) { + wlr_output_state_set_mode(&state, internal_mode); + } else if (wlr_output_is_headless(m->wlr_output)) { + wlr_output_state_set_custom_mode( + &state, mr->width, mr->height, + (int32_t)roundf(mr->refresh * 1000)); + } + } + + if (vrr) { + enable_adaptive_sync(m, &state); + } else { + wlr_output_state_set_adaptive_sync_enabled(&state, false); + } + + wlr_output_state_set_scale(&state, mr->scale); + wlr_output_state_set_transform(&state, mr->rr); + wlr_output_layout_add(server.output_layout, m->wlr_output, mx, + my); + } + } + + wlr_output_commit_state(m->wlr_output, &state); + wlr_output_state_finish(&state); + updatemons(NULL, NULL); + } +} + +void reapply_cursor_style(void) { + if (server.hide_source) { + wl_event_source_timer_update(server.hide_source, 0); + wl_event_source_remove(server.hide_source); + server.hide_source = NULL; + } + + wlr_cursor_unset_image(server.cursor); + + wlr_cursor_set_surface(server.cursor, NULL, 0, 0); + + if (server.cursor_mgr) { + wlr_xcursor_manager_destroy(server.cursor_mgr); + server.cursor_mgr = NULL; + } + + server.cursor_mgr = + wlr_xcursor_manager_create(config.cursor_theme, cursor_size); + + Monitor *m = NULL; + wl_list_for_each(m, &server.mons, link) { + wlr_xcursor_manager_load(server.cursor_mgr, m->wlr_output->scale); + } + + wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, "left_ptr"); + + server.hide_source = wl_event_loop_add_timer( + wl_display_get_event_loop(server.dpy), hidecursor, server.cursor); + if (server.cursor_hidden) { + wlr_cursor_unset_image(server.cursor); + } else { + wl_event_source_timer_update(server.hide_source, + cursor_hide_timeout * 1000); + } +} + +void reapply_border(void) { + Client *c = NULL; + + // reset border width when config change + wl_list_for_each(c, &server.clients, link) { + if (c && !c->iskilling) { + if (!c->isnoborder && !c->isfullscreen) { + c->bw = borderpx; + } + } + } +} + +void reapply_keyboard(void) { + InputDevice *id; + wl_list_for_each(id, &server.inputdevices, link) { + if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { + continue; + } + wlr_keyboard_set_repeat_info((struct wlr_keyboard *)id->device_data, + repeat_rate, repeat_delay); + } +} + +void reapply_pointer(void) { + InputDevice *id; + struct libinput_device *device; + wl_list_for_each(id, &server.inputdevices, link) { + + if (id->wlr_device->type != WLR_INPUT_DEVICE_POINTER) { + continue; + } + + device = id->libinput_device; + if (wlr_input_device_is_libinput(id->wlr_device) && device) { + configure_pointer(device); + } + } +} + +void reapply_master(void) { + + int32_t i; + Monitor *m = NULL; + for (i = 0; i <= LENGTH(tags); i++) { + wl_list_for_each(m, &server.mons, link) { + if (!m->wlr_output->enabled) { + continue; + } + m->pertag->nmasters[i] = default_nmaster; + m->pertag->mfacts[i] = default_mfact; + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + } + } +} + +void parse_tagrule(Monitor *m) { + int32_t i, jk; + ConfigTagRule tr; + Client *c = NULL; + + for (i = 0; i <= LENGTH(tags); i++) { + m->pertag->nmasters[i] = default_nmaster; + m->pertag->mfacts[i] = default_mfact; + } + + for (i = 0; i < config.tag_rules_count; i++) { + + tr = config.tag_rules[i]; + + if (config.tag_rules_count > 0 && + (!tr.monitor_name || + regex_match(tr.monitor_name, m->wlr_output->name))) { + + for (jk = 0; jk < layouts_len; jk++) { + if (tr.layout_name && + strcmp(layouts[jk].name, tr.layout_name) == 0) { + m->pertag->ltidxs[tr.id] = &layouts[jk]; + } + } + + if (tr.no_hide >= 0) + m->pertag->no_hide[tr.id] = tr.no_hide; + if (tr.nmaster >= 1) + m->pertag->nmasters[tr.id] = tr.nmaster; + if (tr.mfact > 0.0f) + m->pertag->mfacts[tr.id] = tr.mfact; + if (tr.no_render_border >= 0) + m->pertag->no_render_border[tr.id] = tr.no_render_border; + } + } + + for (i = 1; i <= LENGTH(tags); i++) { + wl_list_for_each(c, &server.clients, link) { + if ((c->tags & (1 << (i - 1)) & TAGMASK) && ISTILED(c)) { + if (m->pertag->mfacts[i] > 0.0f) + c->master_mfact_per = m->pertag->mfacts[i]; + } + } + } +} + +void reapply_tagrule(void) { + Monitor *m = NULL; + wl_list_for_each(m, &server.mons, link) { + if (!m->wlr_output->enabled) { + continue; + } + parse_tagrule(m); + } +} + +void reset_option(void) { + init_baked_points(); + handlecursoractivity(); + reset_keyboard_layout(); + reset_blur_params(); + set_env(); + run_exec(); + + reapply_cursor_style(); + reapply_border(); + reapply_keyboard(); + reapply_pointer(); + reapply_master(); + + reapply_tagrule(); + reapply_monitor_rules(); + + arrange(server.selmon, false, false); +} + +int32_t reload_config(const Arg *arg) { + parse_config(); + reset_option(); + printstatus(); + return 1; +} diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 2146a99..fbc9d6b 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1,111 +1,14 @@ -#include -#include -#include -#include -#include +#ifndef CONFIG_PARSER_H +#define CONFIG_PARSER_H +/* ====================== 宏定义和常量 ====================== */ #ifndef SYSCONFDIR #define SYSCONFDIR "/etc" #endif -// 整数版本 - 截断小数部分 -#define CLAMP_INT(x, min, max) \ - ((int32_t)(x) < (int32_t)(min) \ - ? (int32_t)(min) \ - : ((int32_t)(x) > (int32_t)(max) ? (int32_t)(max) : (int32_t)(x))) - -// 浮点数版本 - 保留小数部分 -#define CLAMP_FLOAT(x, min, max) \ - ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) - -enum { NUM_TYPE_MINUS, NUM_TYPE_PLUS, NUM_TYPE_DEFAULT }; - -enum { KEY_TYPE_CODE, KEY_TYPE_SYM }; - -typedef struct { - uint32_t keycode1; - uint32_t keycode2; - uint32_t keycode3; -} MultiKeycode; -typedef struct { - xkb_keysym_t keysym; - MultiKeycode keycode; - int32_t type; -} KeySymCode; - -typedef struct { - uint32_t mod; - KeySymCode keysymcode; - int32_t (*func)(const Arg *); - Arg arg; - int32_t keymode_idx; // 改为索引,指向config.keymodes数组 - bool islockapply; - bool isreleaseapply; - bool ispassapply; -} KeyBinding; - -typedef struct { - char *type; - char *value; -} ConfigEnv; - -typedef struct { - const char *id; - const char *title; - uint32_t tags; - int32_t isfloating; - int32_t isfullscreen; - float scroller_proportion; - const char *animation_type_open; - const char *animation_type_close; - const char *layer_animation_type_open; - const char *layer_animation_type_close; - int32_t isnoborder; - int32_t isnoshadow; - int32_t isnoradius; - int32_t isnoanimation; - int32_t isopensilent; - int32_t istagsilent; - int32_t isnamedscratchpad; - int32_t isunglobal; - int32_t isglobal; - int32_t isoverlay; - int32_t allow_shortcuts_inhibit; - int32_t ignore_maximize; - int32_t ignore_minimize; - int32_t isnosizehint; - const char *monitor; - int32_t offsetx; - int32_t offsety; - int32_t width; - int32_t height; - int32_t nofocus; - int32_t nofadein; - int32_t nofadeout; - int32_t no_force_center; - int32_t isterm; - int32_t allow_csd; - int32_t force_maximize; - int32_t force_tearing; - int32_t noswallow; - int32_t noblur; - float focused_opacity; - float unfocused_opacity; - float scroller_proportion_single; - uint32_t passmod; - xkb_keysym_t keysym; - KeyBinding globalkeybinding; -} ConfigWinRule; - -typedef struct { - const char *name; // Monitor name - int32_t rr; // Rotate and flip (assume integer) - float scale; // Monitor scale factor - int32_t x, y; // Monitor position - int32_t width, height; // Monitor resolution - float refresh; // Refresh rate - int32_t vrr; // variable refresh rate -} ConfigMonitorRule; +// 整数和浮点数截断宏 +#define CLAMP_INT(x, min, max) ((int32_t)(x) < (int32_t)(min) ? (int32_t)(min) : ((int32_t)(x) > (int32_t)(max) ? (int32_t)(max) : (int32_t)(x))) +#define CLAMP_FLOAT(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) // 修改后的宏定义 #define CHVT(n) \ @@ -117,3628 +20,414 @@ typedef struct { } \ } -// 默认按键绑定数组 -KeyBinding default_key_bindings[] = {CHVT(1), CHVT(2), CHVT(3), CHVT(4), - CHVT(5), CHVT(6), CHVT(7), CHVT(8), - CHVT(9), CHVT(10), CHVT(11), CHVT(12)}; +// 数字类型枚举 +enum { NUM_TYPE_MINUS, NUM_TYPE_PLUS, NUM_TYPE_DEFAULT }; +// 键类型枚举 +enum { KEY_TYPE_CODE, KEY_TYPE_SYM }; + + +/* ====================== 结构体定义 ====================== */ +typedef struct { + uint32_t keycode1; + uint32_t keycode2; + uint32_t keycode3; +} MultiKeycode; typedef struct { - uint32_t mod; - uint32_t button; - int32_t (*func)(const Arg *); - Arg arg; + xkb_keysym_t keysym; + MultiKeycode keycode; + int32_t type; +} KeySymCode; + +typedef struct { + uint32_t mod; + KeySymCode keysymcode; + int32_t (*func)(const Arg *); + Arg arg; + int32_t keymode_idx; + bool islockapply; + bool isreleaseapply; + bool ispassapply; +} KeyBinding; + +typedef struct { + char *type; + char *value; +} ConfigEnv; + +typedef struct { + const char *id; + const char *title; + uint32_t tags; + int32_t isfloating; + int32_t isfullscreen; + float scroller_proportion; + const char *animation_type_open; + const char *animation_type_close; + const char *layer_animation_type_open; + const char *layer_animation_type_close; + int32_t isnoborder; + int32_t isnoshadow; + int32_t isnoradius; + int32_t isnoanimation; + int32_t isopensilent; + int32_t istagsilent; + int32_t isnamedscratchpad; + int32_t isunglobal; + int32_t isglobal; + int32_t isoverlay; + int32_t allow_shortcuts_inhibit; + int32_t ignore_maximize; + int32_t ignore_minimize; + int32_t isnosizehint; + const char *monitor; + int32_t offsetx; + int32_t offsety; + int32_t width; + int32_t height; + int32_t nofocus; + int32_t nofadein; + int32_t nofadeout; + int32_t no_force_center; + int32_t isterm; + int32_t allow_csd; + int32_t force_maximize; + int32_t force_tearing; + int32_t noswallow; + int32_t noblur; + float focused_opacity; + float unfocused_opacity; + float scroller_proportion_single; + uint32_t passmod; + xkb_keysym_t keysym; + KeyBinding globalkeybinding; +} ConfigWinRule; + +typedef struct { + const char *name; + int32_t rr; + float scale; + int32_t x, y; + int32_t width, height; + float refresh; + int32_t vrr; +} ConfigMonitorRule; + +typedef struct { + uint32_t mod; + uint32_t button; + int32_t (*func)(const Arg *); + Arg arg; } MouseBinding; typedef struct { - uint32_t mod; - uint32_t dir; - int32_t (*func)(const Arg *); - Arg arg; + uint32_t mod; + uint32_t dir; + int32_t (*func)(const Arg *); + Arg arg; } AxisBinding; typedef struct { - uint32_t fold; - int32_t (*func)(const Arg *); - Arg arg; + uint32_t fold; + int32_t (*func)(const Arg *); + Arg arg; } SwitchBinding; typedef struct { - uint32_t mod; - uint32_t motion; - uint32_t fingers_count; - int32_t (*func)(const Arg *); - Arg arg; + uint32_t mod; + uint32_t motion; + uint32_t fingers_count; + int32_t (*func)(const Arg *); + Arg arg; } GestureBinding; typedef struct { - int32_t id; - char *layout_name; - char *monitor_name; - float mfact; - int32_t nmaster; - int32_t no_render_border; - int32_t no_hide; + int32_t id; + char *layout_name; + char *monitor_name; + float mfact; + int32_t nmaster; + int32_t no_render_border; + int32_t no_hide; } ConfigTagRule; typedef struct { - char *layer_name; // 布局名称 - char *animation_type_open; - char *animation_type_close; - int32_t noblur; - int32_t noanim; - int32_t noshadow; + char *layer_name; + char *animation_type_open; + char *animation_type_close; + int32_t noblur; + int32_t noanim; + int32_t noshadow; } ConfigLayerRule; typedef struct { - int32_t animations; - int32_t layer_animations; - char animation_type_open[10]; - char animation_type_close[10]; - char layer_animation_type_open[10]; - char layer_animation_type_close[10]; - int32_t animation_fade_in; - int32_t animation_fade_out; - int32_t tag_animation_direction; - float zoom_initial_ratio; - float zoom_end_ratio; - float fadein_begin_opacity; - float fadeout_begin_opacity; - uint32_t animation_duration_move; - uint32_t animation_duration_open; - uint32_t animation_duration_tag; - uint32_t animation_duration_close; - uint32_t animation_duration_focus; - double animation_curve_move[4]; - double animation_curve_open[4]; - double animation_curve_tag[4]; - double animation_curve_close[4]; - double animation_curve_focus[4]; - double animation_curve_opafadein[4]; - double animation_curve_opafadeout[4]; - - int32_t scroller_structs; - float scroller_default_proportion; - float scroller_default_proportion_single; - int32_t scroller_ignore_proportion_single; - int32_t scroller_focus_center; - int32_t scroller_prefer_center; - int32_t edge_scroller_pointer_focus; - int32_t focus_cross_monitor; - int32_t exchange_cross_monitor; - int32_t scratchpad_cross_monitor; - int32_t focus_cross_tag; - int32_t view_current_to_back; - int32_t no_border_when_single; - int32_t no_radius_when_single; - int32_t snap_distance; - int32_t enable_floating_snap; - int32_t drag_tile_to_tile; - uint32_t swipe_min_threshold; - float focused_opacity; - float unfocused_opacity; - float *scroller_proportion_preset; - int32_t scroller_proportion_preset_count; - - char **circle_layout; - int32_t circle_layout_count; - - uint32_t new_is_master; - float default_mfact; - uint32_t default_nmaster; - int32_t center_master_overspread; - int32_t center_when_single_stack; - - uint32_t hotarea_size; - uint32_t hotarea_corner; - uint32_t enable_hotarea; - uint32_t ov_tab_mode; - int32_t overviewgappi; - int32_t overviewgappo; - uint32_t cursor_hide_timeout; - - uint32_t axis_bind_apply_timeout; - uint32_t focus_on_activate; - int32_t idleinhibit_ignore_visible; - int32_t sloppyfocus; - int32_t warpcursor; - int32_t drag_corner; - int32_t drag_warp_cursor; - - /* keyboard */ - int32_t repeat_rate; - int32_t repeat_delay; - uint32_t numlockon; - - /* Trackpad */ - int32_t disable_trackpad; - int32_t tap_to_click; - int32_t tap_and_drag; - int32_t drag_lock; - int32_t mouse_natural_scrolling; - int32_t trackpad_natural_scrolling; - int32_t disable_while_typing; - int32_t left_handed; - int32_t middle_button_emulation; - uint32_t accel_profile; - double accel_speed; - uint32_t scroll_method; - uint32_t scroll_button; - uint32_t click_method; - uint32_t send_events_mode; - uint32_t button_map; - - double axis_scroll_factor; - - int32_t blur; - int32_t blur_layer; - int32_t blur_optimized; - int32_t border_radius; - struct blur_data blur_params; - int32_t shadows; - int32_t shadow_only_floating; - int32_t layer_shadows; - uint32_t shadows_size; - float shadows_blur; - int32_t shadows_position_x; - int32_t shadows_position_y; - float shadowscolor[4]; - - int32_t smartgaps; - uint32_t gappih; - uint32_t gappiv; - uint32_t gappoh; - uint32_t gappov; - uint32_t borderpx; - float scratchpad_width_ratio; - float scratchpad_height_ratio; - float rootcolor[4]; - float bordercolor[4]; - float focuscolor[4]; - float maximizescreencolor[4]; - float urgentcolor[4]; - float scratchpadcolor[4]; - float globalcolor[4]; - float overlaycolor[4]; - - char autostart[3][256]; - - ConfigTagRule *tag_rules; // 动态数组 - int32_t tag_rules_count; // 数量 - - ConfigLayerRule *layer_rules; // 动态数组 - int32_t layer_rules_count; // 数量 - - ConfigWinRule *window_rules; - int32_t window_rules_count; - - ConfigMonitorRule *monitor_rules; // 动态数组 - int32_t monitor_rules_count; // 条数 - - KeyBinding *key_bindings; - int32_t key_bindings_count; - - MouseBinding *mouse_bindings; - int32_t mouse_bindings_count; - - AxisBinding *axis_bindings; - int32_t axis_bindings_count; - - SwitchBinding *switch_bindings; - int32_t switch_bindings_count; - - GestureBinding *gesture_bindings; - int32_t gesture_bindings_count; - - ConfigEnv **env; - int32_t env_count; - - char **exec; - int32_t exec_count; - - char **exec_once; - int32_t exec_once_count; - - char *cursor_theme; - uint32_t cursor_size; - - int32_t single_scratchpad; - int32_t xwayland_persistence; - int32_t syncobj_enable; - int32_t allow_tearing; - int32_t allow_shortcuts_inhibit; - int32_t allow_lock_transparent; - - struct xkb_rule_names xkb_rules; - - KeyMode *keymodes; // 动态数组,存储所有keymode - int32_t keymodes_count; // keymode数量 - int32_t current_keymode_idx; // 当前使用的keymode索引 - - struct xkb_context *ctx; - struct xkb_keymap *keymap; + // 动画设置 + int32_t animations; + int32_t layer_animations; + char animation_type_open[10]; + char animation_type_close[10]; + char layer_animation_type_open[10]; + char layer_animation_type_close[10]; + int32_t animation_fade_in; + int32_t animation_fade_out; + int32_t tag_animation_direction; + float zoom_initial_ratio; + float zoom_end_ratio; + float fadein_begin_opacity; + float fadeout_begin_opacity; + uint32_t animation_duration_move; + uint32_t animation_duration_open; + uint32_t animation_duration_tag; + uint32_t animation_duration_close; + uint32_t animation_duration_focus; + double animation_curve_move[4]; + double animation_curve_open[4]; + double animation_curve_tag[4]; + double animation_curve_close[4]; + double animation_curve_focus[4]; + double animation_curve_opafadein[4]; + double animation_curve_opafadeout[4]; + + // 滚动布局设置 + int32_t scroller_structs; + float scroller_default_proportion; + float scroller_default_proportion_single; + int32_t scroller_ignore_proportion_single; + int32_t scroller_focus_center; + int32_t scroller_prefer_center; + int32_t edge_scroller_pointer_focus; + int32_t focus_cross_monitor; + int32_t exchange_cross_monitor; + int32_t scratchpad_cross_monitor; + int32_t focus_cross_tag; + int32_t view_current_to_back; + int32_t no_border_when_single; + int32_t no_radius_when_single; + int32_t snap_distance; + int32_t enable_floating_snap; + int32_t drag_tile_to_tile; + uint32_t swipe_min_threshold; + float focused_opacity; + float unfocused_opacity; + float *scroller_proportion_preset; + int32_t scroller_proportion_preset_count; + + // 布局设置 + char **circle_layout; + int32_t circle_layout_count; + uint32_t new_is_master; + float default_mfact; + uint32_t default_nmaster; + int32_t center_master_overspread; + int32_t center_when_single_stack; + + // 概述模式设置 + uint32_t hotarea_size; + uint32_t hotarea_corner; + uint32_t enable_hotarea; + uint32_t ov_tab_mode; + int32_t overviewgappi; + int32_t overviewgappo; + uint32_t cursor_hide_timeout; + + // 其他设置 + uint32_t axis_bind_apply_timeout; + uint32_t focus_on_activate; + int32_t idleinhibit_ignore_visible; + int32_t sloppyfocus; + int32_t warpcursor; + int32_t drag_corner; + int32_t drag_warp_cursor; + + // 键盘设置 + int32_t repeat_rate; + int32_t repeat_delay; + uint32_t numlockon; + + // 触控板和鼠标设置 + int32_t disable_trackpad; + int32_t tap_to_click; + int32_t tap_and_drag; + int32_t drag_lock; + int32_t mouse_natural_scrolling; + int32_t trackpad_natural_scrolling; + int32_t disable_while_typing; + int32_t left_handed; + int32_t middle_button_emulation; + uint32_t accel_profile; + double accel_speed; + uint32_t scroll_method; + uint32_t scroll_button; + uint32_t click_method; + uint32_t send_events_mode; + uint32_t button_map; + double axis_scroll_factor; + + // 外观效果 + int32_t blur; + int32_t blur_layer; + int32_t blur_optimized; + int32_t border_radius; + struct blur_data blur_params; + int32_t shadows; + int32_t shadow_only_floating; + int32_t layer_shadows; + uint32_t shadows_size; + float shadows_blur; + int32_t shadows_position_x; + int32_t shadows_position_y; + float shadowscolor[4]; + + // 间距和边框 + int32_t smartgaps; + uint32_t gappih; + uint32_t gappiv; + uint32_t gappoh; + uint32_t gappov; + uint32_t borderpx; + float scratchpad_width_ratio; + float scratchpad_height_ratio; + + // 颜色设置 + float rootcolor[4]; + float bordercolor[4]; + float focuscolor[4]; + float maximizescreencolor[4]; + float urgentcolor[4]; + float scratchpadcolor[4]; + float globalcolor[4]; + float overlaycolor[4]; + + // 自动启动 + char autostart[3][256]; + + // 规则数组 + ConfigTagRule *tag_rules; + int32_t tag_rules_count; + ConfigLayerRule *layer_rules; + int32_t layer_rules_count; + ConfigWinRule *window_rules; + int32_t window_rules_count; + ConfigMonitorRule *monitor_rules; + int32_t monitor_rules_count; + + // 绑定数组 + KeyBinding *key_bindings; + int32_t key_bindings_count; + MouseBinding *mouse_bindings; + int32_t mouse_bindings_count; + AxisBinding *axis_bindings; + int32_t axis_bindings_count; + SwitchBinding *switch_bindings; + int32_t switch_bindings_count; + GestureBinding *gesture_bindings; + int32_t gesture_bindings_count; + + // 环境变量和命令 + ConfigEnv **env; + int32_t env_count; + char **exec; + int32_t exec_count; + char **exec_once; + int32_t exec_once_count; + + // 光标设置 + char *cursor_theme; + uint32_t cursor_size; + + // 杂项 + int32_t single_scratchpad; + int32_t xwayland_persistence; + int32_t syncobj_enable; + int32_t allow_tearing; + int32_t allow_shortcuts_inhibit; + int32_t allow_lock_transparent; + + // 键盘布局 + struct xkb_rule_names xkb_rules; + + // 按键模式 + KeyMode *keymodes; + int32_t keymodes_count; + int32_t current_keymode_idx; + + // XKB上下文 + struct xkb_context *ctx; + struct xkb_keymap *keymap; } Config; +/* ====================== 全局变量 ====================== */ +extern KeyBinding default_key_bindings[]; typedef int32_t (*FuncType)(const Arg *); -Config config; +/* ====================== 字符串处理函数 ====================== */ +void trim_whitespace(char *str); +char *sanitize_string(char *str); +bool starts_with_ignore_case(const char *str, const char *prefix); +char *combine_args_until_empty(char *values[], int count); + +/* ====================== 解析函数 ====================== */ +// 通用解析 +int32_t parse_double_array(const char *input, double *output, int32_t max_count); +uint32_t parse_num_type(char *str); +int64_t parse_color(const char *hex_str); + +// 方向解析 +int32_t parse_circle_direction(const char *str); +int32_t parse_direction(const char *str); +int32_t parse_fold_state(const char *str); +uint32_t parse_button(const char *str); +int32_t parse_mouse_action(const char *str); + +// 按键绑定解析 +void parse_bind_flags(const char *str, KeyBinding *kb); +uint32_t parse_mod(const char *mod_str); +KeySymCode parse_key(const char *key_str, bool isbindsym); +int32_t find_keycodes_for_keysym(struct xkb_keymap *keymap, xkb_keysym_t sym, MultiKeycode *multi_kc); + +// 颜色转换 +void convert_hex_to_rgba(float *color, uint32_t hex); + +// 函数名解析 +FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, char *arg_value2, + char *arg_value3, char *arg_value4, char *arg_value5); + +/* ====================== 配置解析核心函数 ====================== */ void parse_config_file(Config *config, const char *file_path); - -// 查找或添加keymode,返回索引 -int32_t find_or_add_keymode(Config *config, const char *name) { - // 检查是否已存在 - for (int32_t i = 0; i < config->keymodes_count; i++) { - if (strcmp(config->keymodes[i].name, name) == 0) { - return i; - } - } - - // 添加新的keymode - config->keymodes = realloc(config->keymodes, - (config->keymodes_count + 1) * sizeof(KeyMode)); - if (!config->keymodes) { - fprintf(stderr, "Failed to allocate memory for keymodes\n"); - return -1; - } - - KeyMode *new_mode = &config->keymodes[config->keymodes_count]; - new_mode->name = strdup(name); - new_mode->is_default = (strcmp(name, "default") == 0); - new_mode->is_common = (strcmp(name, "common") == 0); - - return config->keymodes_count++; // 返回索引,然后增加计数 -} - -// 设置当前keymode -void set_current_keymode(Config *config, const char *name) { - int32_t idx = find_or_add_keymode(config, name); - if (idx >= 0) { - config->current_keymode_idx = idx; - } -} - -// Helper function to trim whitespace from start and end of a string -void trim_whitespace(char *str) { - if (str == NULL || *str == '\0') - return; - - // Trim leading space - char *start = str; - while (isspace((unsigned char)*start)) { - start++; - } - - // Trim trailing space - char *end = str + strlen(str) - 1; - while (end > start && isspace((unsigned char)*end)) { - end--; - } - - // Null-terminate the trimmed string - *(end + 1) = '\0'; - - // Move the trimmed part to the beginning if needed - if (start != str) { - memmove(str, start, end - start + 2); // +2 to include null terminator - } -} - -int32_t parse_double_array(const char *input, double *output, - int32_t max_count) { - char *dup = strdup(input); - char *token; - int32_t count = 0; - - // 先清空整个数组 - memset(output, 0, max_count * sizeof(double)); - - token = strtok(dup, ","); - while (token != NULL && count < max_count) { - trim_whitespace(token); - char *endptr; - double val = strtod(token, &endptr); - if (endptr == token || *endptr != '\0') { - fprintf( - stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid number in array: %s\n", - token); - free(dup); - return -1; - } - output[count] = val; // 赋值到当前count位置 - count++; // 然后才自增 - token = strtok(NULL, ","); - } - - free(dup); - return count; -} - -// 清理字符串中的不可见字符(包括 \r, \n, 空格等) -char *sanitize_string(char *str) { - // 去除首部不可见字符 - while (*str != '\0' && !isprint((unsigned char)*str)) - str++; - // 去除尾部不可见字符 - char *end = str + strlen(str) - 1; - while (end > str && !isprint((unsigned char)*end)) - end--; - *(end + 1) = '\0'; - return str; -} - -// 解析bind组合字符串 -void parse_bind_flags(const char *str, KeyBinding *kb) { - - // 检查是否以"bind"开头 - if (strncmp(str, "bind", 4) != 0) { - return; - } - - const char *suffix = str + 4; // 跳过"bind" - - // 遍历后缀字符 - for (int32_t i = 0; suffix[i] != '\0'; i++) { - switch (suffix[i]) { - case 's': - kb->keysymcode.type = KEY_TYPE_SYM; - break; - case 'l': - kb->islockapply = true; - break; - case 'r': - kb->isreleaseapply = true; - break; - case 'p': - kb->ispassapply = true; - break; - default: - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown bind flag: %c\n", - suffix[i]); - break; - } - } -} - -int32_t parse_circle_direction(const char *str) { - // 将输入字符串转换为小写 - char lowerStr[10]; - int32_t i = 0; - while (str[i] && i < 9) { - lowerStr[i] = tolower(str[i]); - i++; - } - lowerStr[i] = '\0'; - - // 根据转换后的小写字符串返回对应的枚举值 - if (strcmp(lowerStr, "next") == 0) { - return NEXT; - } else { - return PREV; - } -} - -int32_t parse_direction(const char *str) { - // 将输入字符串转换为小写 - char lowerStr[10]; - int32_t i = 0; - while (str[i] && i < 9) { - lowerStr[i] = tolower(str[i]); - i++; - } - lowerStr[i] = '\0'; - - // 根据转换后的小写字符串返回对应的枚举值 - if (strcmp(lowerStr, "up") == 0) { - return UP; - } else if (strcmp(lowerStr, "down") == 0) { - return DOWN; - } else if (strcmp(lowerStr, "left") == 0) { - return LEFT; - } else if (strcmp(lowerStr, "right") == 0) { - return RIGHT; - } else { - return UNDIR; - } -} - -int32_t parse_fold_state(const char *str) { - // 将输入字符串转换为小写 - char lowerStr[10]; - int32_t i = 0; - while (str[i] && i < 9) { - lowerStr[i] = tolower(str[i]); - i++; - } - lowerStr[i] = '\0'; - - // 根据转换后的小写字符串返回对应的枚举值 - if (strcmp(lowerStr, "fold") == 0) { - return FOLD; - } else if (strcmp(lowerStr, "unfold") == 0) { - return UNFOLD; - } else { - return INVALIDFOLD; - } -} -int64_t parse_color(const char *hex_str) { - char *endptr; - int64_t hex_num = strtol(hex_str, &endptr, 16); - if (*endptr != '\0') { - return -1; - } - return hex_num; -} - -// 辅助函数:检查字符串是否以指定的前缀开头(忽略大小写) -static bool starts_with_ignore_case(const char *str, const char *prefix) { - while (*prefix) { - if (tolower(*str) != tolower(*prefix)) { - return false; - } - str++; - prefix++; - } - return true; -} - -static char *combine_args_until_empty(char *values[], int count) { - // find the first empty string - int first_empty = count; - for (int i = 0; i < count; i++) { - // check if it's empty: empty string or only contains "0" (initialized) - if (values[i][0] == '\0' || - (strlen(values[i]) == 1 && values[i][0] == '0')) { - first_empty = i; - break; - } - } - - // if there are no valid parameters, return an empty string - if (first_empty == 0) { - return strdup(""); - } - - // calculate the total length - size_t total_len = 0; - for (int i = 0; i < first_empty; i++) { - total_len += strlen(values[i]); - } - // plus the number of commas (first_empty-1 commas) - total_len += (first_empty - 1); - - // allocate memory and concatenate - char *combined = malloc(total_len + 1); - if (combined == NULL) { - return strdup(""); - } - - combined[0] = '\0'; - for (int i = 0; i < first_empty; i++) { - if (i > 0) { - strcat(combined, ","); - } - strcat(combined, values[i]); - } - - return combined; -} - -uint32_t parse_mod(const char *mod_str) { - if (!mod_str || !*mod_str) { - return UINT32_MAX; - } - - uint32_t mod = 0; - char input_copy[256]; - char *token; - char *saveptr = NULL; - bool match_success = false; - - // 复制并转换为小写 - strncpy(input_copy, mod_str, sizeof(input_copy) - 1); - input_copy[sizeof(input_copy) - 1] = '\0'; - for (char *p = input_copy; *p; p++) { - *p = tolower(*p); - } - - // 分割处理每个部分 - token = strtok_r(input_copy, "+", &saveptr); - while (token != NULL) { - // 去除空白 - while (*token == ' ' || *token == '\t') - token++; - - if (strncmp(token, "code:", 5) == 0) { - // 处理 code: 形式 - char *endptr; - long keycode = strtol(token + 5, &endptr, 10); - if (endptr != token + 5 && (*endptr == '\0' || *endptr == ' ')) { - switch (keycode) { - case 133: - case 134: - mod |= WLR_MODIFIER_LOGO; - break; - case 37: - case 105: - mod |= WLR_MODIFIER_CTRL; - break; - case 50: - case 62: - mod |= WLR_MODIFIER_SHIFT; - break; - case 64: - case 108: - mod |= WLR_MODIFIER_ALT; - break; - default: - fprintf(stderr, - "unknown modifier keycode: \033[1m\033[31m%s\n", - token); - break; - } - } - } else { - // 完整的 modifier 检查(保留原始所有检查项) - if (!strcmp(token, "super") || !strcmp(token, "super_l") || - !strcmp(token, "super_r")) { - mod |= WLR_MODIFIER_LOGO; - match_success = true; - } - if (!strcmp(token, "ctrl") || !strcmp(token, "ctrl_l") || - !strcmp(token, "ctrl_r")) { - mod |= WLR_MODIFIER_CTRL; - match_success = true; - } - if (!strcmp(token, "shift") || !strcmp(token, "shift_l") || - !strcmp(token, "shift_r")) { - mod |= WLR_MODIFIER_SHIFT; - match_success = true; - } - if (!strcmp(token, "alt") || !strcmp(token, "alt_l") || - !strcmp(token, "alt_r")) { - mod |= WLR_MODIFIER_ALT; - match_success = true; - } - if (!strcmp(token, "hyper") || !strcmp(token, "hyper_l") || - !strcmp(token, "hyper_r")) { - mod |= WLR_MODIFIER_MOD3; - match_success = true; - } - if (!strcmp(token, "none")) { - match_success = true; - } - } - - token = strtok_r(NULL, "+", &saveptr); - } - - if (!match_success) { - mod = UINT32_MAX; - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown modifier: " - "\033[1m\033[31m%s\n", - mod_str); - } - - return mod; -} - -// 定义辅助函数:在 keymap 中查找 keysym 对应的多个 keycode -static int32_t find_keycodes_for_keysym(struct xkb_keymap *keymap, - xkb_keysym_t sym, - MultiKeycode *multi_kc) { - xkb_keycode_t min_keycode = xkb_keymap_min_keycode(keymap); - xkb_keycode_t max_keycode = xkb_keymap_max_keycode(keymap); - - multi_kc->keycode1 = 0; - multi_kc->keycode2 = 0; - multi_kc->keycode3 = 0; - - int32_t found_count = 0; - - for (xkb_keycode_t keycode = min_keycode; - keycode <= max_keycode && found_count < 3; keycode++) { - // 使用布局0和层级0 - const xkb_keysym_t *syms; - int32_t num_syms = - xkb_keymap_key_get_syms_by_level(keymap, keycode, 0, 0, &syms); - - for (int32_t i = 0; i < num_syms; i++) { - if (syms[i] == sym) { - switch (found_count) { - case 0: - multi_kc->keycode1 = keycode; - break; - case 1: - multi_kc->keycode2 = keycode; - break; - case 2: - multi_kc->keycode3 = keycode; - break; - } - found_count++; - break; - } - } - } - - return found_count; -} - -void cleanup_config_keymap(void) { - if (config.keymap != NULL) { - xkb_keymap_unref(config.keymap); - config.keymap = NULL; - } - if (config.ctx != NULL) { - xkb_context_unref(config.ctx); - config.ctx = NULL; - } -} - -void create_config_keymap(void) { - // 初始化 xkb 上下文和 keymap - - if (config.ctx == NULL) { - config.ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - } - - if (config.keymap == NULL) { - config.keymap = xkb_keymap_new_from_names( - config.ctx, &xkb_fallback_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - } -} - -KeySymCode parse_key(const char *key_str, bool isbindsym) { - KeySymCode kc = {0}; // 初始化为0 - - if (config.keymap == NULL || config.ctx == NULL) { - // 处理错误 - kc.type = KEY_TYPE_SYM; - kc.keysym = XKB_KEY_NoSymbol; - return kc; - } - - // 处理 code: 前缀的情况 - if (strncmp(key_str, "code:", 5) == 0) { - char *endptr; - xkb_keycode_t keycode = (xkb_keycode_t)strtol(key_str + 5, &endptr, 10); - kc.type = KEY_TYPE_CODE; - kc.keycode.keycode1 = keycode; // 只设置第一个 - kc.keycode.keycode2 = 0; - kc.keycode.keycode3 = 0; - return kc; - } - - // 普通键名直接转换 - xkb_keysym_t sym = xkb_keysym_from_name(key_str, XKB_KEYSYM_NO_FLAGS); - - if (isbindsym) { - kc.type = KEY_TYPE_SYM; - kc.keysym = sym; - return kc; - } - - if (sym != XKB_KEY_NoSymbol) { - // 尝试找到对应的多个 keycode - int32_t found_count = - find_keycodes_for_keysym(config.keymap, sym, &kc.keycode); - if (found_count > 0) { - kc.type = KEY_TYPE_CODE; - kc.keysym = sym; // 仍然保存 keysym 供参考 - } else { - kc.type = KEY_TYPE_SYM; - kc.keysym = sym; - // keycode 字段保持为0 - } - } else { - // 无法解析的键名 - kc.type = KEY_TYPE_SYM; - kc.keysym = XKB_KEY_NoSymbol; - fprintf( - stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown key: \033[1m\033[31m%s\n", - key_str); - // keycode 字段保持为0 - } - - return kc; -} - -uint32_t parse_button(const char *str) { - // 将输入字符串转换为小写 - char lowerStr[20]; - int32_t i = 0; - while (str[i] && i < 19) { - lowerStr[i] = tolower(str[i]); - i++; - } - lowerStr[i] = '\0'; // 确保字符串正确终止 - - // 根据转换后的小写字符串返回对应的按钮编号 - if (strcmp(lowerStr, "btn_left") == 0) { - return BTN_LEFT; - } else if (strcmp(lowerStr, "btn_right") == 0) { - return BTN_RIGHT; - } else if (strcmp(lowerStr, "btn_middle") == 0) { - return BTN_MIDDLE; - } else if (strcmp(lowerStr, "btn_side") == 0) { - return BTN_SIDE; - } else if (strcmp(lowerStr, "btn_extra") == 0) { - return BTN_EXTRA; - } else if (strcmp(lowerStr, "btn_forward") == 0) { - return BTN_FORWARD; - } else if (strcmp(lowerStr, "btn_back") == 0) { - return BTN_BACK; - } else if (strcmp(lowerStr, "btn_task") == 0) { - return BTN_TASK; - } else { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown button: " - "\033[1m\033[31m%s\n", - str); - return UINT32_MAX; - } -} - -int32_t parse_mouse_action(const char *str) { - // 将输入字符串转换为小写 - char lowerStr[20]; - int32_t i = 0; - while (str[i] && i < 19) { - lowerStr[i] = tolower(str[i]); - i++; - } - lowerStr[i] = '\0'; // 确保字符串正确终止 - - // 根据转换后的小写字符串返回对应的按钮编号 - if (strcmp(lowerStr, "curmove") == 0) { - return CurMove; - } else if (strcmp(lowerStr, "curresize") == 0) { - return CurResize; - } else if (strcmp(lowerStr, "curnormal") == 0) { - return CurNormal; - } else if (strcmp(lowerStr, "curpressed") == 0) { - return CurPressed; - } else { - return 0; - } -} - -void convert_hex_to_rgba(float *color, uint32_t hex) { - color[0] = ((hex >> 24) & 0xFF) / 255.0f; - color[1] = ((hex >> 16) & 0xFF) / 255.0f; - color[2] = ((hex >> 8) & 0xFF) / 255.0f; - color[3] = (hex & 0xFF) / 255.0f; -} - -uint32_t parse_num_type(char *str) { - switch (str[0]) { - case '-': - return NUM_TYPE_MINUS; - case '+': - return NUM_TYPE_PLUS; - default: - return NUM_TYPE_DEFAULT; - } -} - -FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, - char *arg_value2, char *arg_value3, char *arg_value4, - char *arg_value5) { - - FuncType func = NULL; - (*arg).i = 0; - (*arg).i2 = 0; - (*arg).f = 0.0f; - (*arg).f2 = 0.0f; - (*arg).ui = 0; - (*arg).ui2 = 0; - (*arg).v = NULL; - (*arg).v2 = NULL; - (*arg).v3 = NULL; - - if (strcmp(func_name, "focusstack") == 0) { - func = focusstack; - (*arg).i = parse_circle_direction(arg_value); - } else if (strcmp(func_name, "focusdir") == 0) { - func = focusdir; - (*arg).i = parse_direction(arg_value); - } else if (strcmp(func_name, "incnmaster") == 0) { - func = incnmaster; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "setmfact") == 0) { - func = setmfact; - (*arg).f = atof(arg_value); - } else if (strcmp(func_name, "zoom") == 0) { - func = zoom; - } else if (strcmp(func_name, "exchange_client") == 0) { - func = exchange_client; - (*arg).i = parse_direction(arg_value); - } else if (strcmp(func_name, "exchange_stack_client") == 0) { - func = exchange_stack_client; - (*arg).i = parse_circle_direction(arg_value); - } else if (strcmp(func_name, "toggleglobal") == 0) { - func = toggleglobal; - } else if (strcmp(func_name, "toggleoverview") == 0) { - func = toggleoverview; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "set_proportion") == 0) { - func = set_proportion; - (*arg).f = atof(arg_value); - } else if (strcmp(func_name, "switch_proportion_preset") == 0) { - func = switch_proportion_preset; - } else if (strcmp(func_name, "viewtoleft") == 0) { - func = viewtoleft; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "viewtoright") == 0) { - func = viewtoright; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "tagsilent") == 0) { - func = tagsilent; - (*arg).ui = 1 << (atoi(arg_value) - 1); - } else if (strcmp(func_name, "tagtoleft") == 0) { - func = tagtoleft; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "tagtoright") == 0) { - func = tagtoright; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "killclient") == 0) { - func = killclient; - } else if (strcmp(func_name, "centerwin") == 0) { - func = centerwin; - } else if (strcmp(func_name, "focuslast") == 0) { - func = focuslast; - } else if (strcmp(func_name, "toggle_trackpad_enable") == 0) { - func = toggle_trackpad_enable; - } else if (strcmp(func_name, "setoption") == 0) { - func = setoption; - - (*arg).v = strdup(arg_value); - - // 收集需要拼接的参数 - const char *non_empty_params[4] = {NULL}; - int32_t param_index = 0; - - if (arg_value2 && arg_value2[0] != '\0') - non_empty_params[param_index++] = arg_value2; - if (arg_value3 && arg_value3[0] != '\0') - non_empty_params[param_index++] = arg_value3; - if (arg_value4 && arg_value4[0] != '\0') - non_empty_params[param_index++] = arg_value4; - if (arg_value5 && arg_value5[0] != '\0') - non_empty_params[param_index++] = arg_value5; - - // 处理拼接 - if (param_index == 0) { - (*arg).v2 = strdup(""); - } else { - // 计算总长度 - size_t len = 0; - for (int32_t i = 0; i < param_index; i++) { - len += strlen(non_empty_params[i]); - } - len += (param_index - 1) + 1; // 逗号数 + null终止符 - - char *temp = malloc(len); - if (temp) { - char *cursor = temp; - for (int32_t i = 0; i < param_index; i++) { - if (i > 0) { - *cursor++ = ','; - } - size_t param_len = strlen(non_empty_params[i]); - memcpy(cursor, non_empty_params[i], param_len); - cursor += param_len; - } - *cursor = '\0'; - (*arg).v2 = temp; - } - } - } else if (strcmp(func_name, "setkeymode") == 0) { - func = setkeymode; - (*arg).v = strdup(arg_value); - } else if (strcmp(func_name, "switch_keyboard_layout") == 0) { - func = switch_keyboard_layout; - } else if (strcmp(func_name, "setlayout") == 0) { - func = setlayout; - (*arg).v = strdup(arg_value); - } else if (strcmp(func_name, "switch_layout") == 0) { - func = switch_layout; - } else if (strcmp(func_name, "togglefloating") == 0) { - func = togglefloating; - } else if (strcmp(func_name, "togglefullscreen") == 0) { - func = togglefullscreen; - } else if (strcmp(func_name, "togglefakefullscreen") == 0) { - func = togglefakefullscreen; - } else if (strcmp(func_name, "toggleoverlay") == 0) { - func = toggleoverlay; - } else if (strcmp(func_name, "minimized") == 0) { - func = minimized; - } else if (strcmp(func_name, "restore_minimized") == 0) { - func = restore_minimized; - } else if (strcmp(func_name, "toggle_scratchpad") == 0) { - func = toggle_scratchpad; - } else if (strcmp(func_name, "toggle_render_border") == 0) { - func = toggle_render_border; - } else if (strcmp(func_name, "focusmon") == 0) { - func = focusmon; - (*arg).i = parse_direction(arg_value); - if ((*arg).i == UNDIR) { - (*arg).v = strdup(arg_value); - } - } else if (strcmp(func_name, "tagmon") == 0) { - func = tagmon; - (*arg).i = parse_direction(arg_value); - (*arg).i2 = atoi(arg_value2); - if ((*arg).i == UNDIR) { - (*arg).v = strdup(arg_value); - }; - } else if (strcmp(func_name, "incgaps") == 0) { - func = incgaps; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "togglegaps") == 0) { - func = togglegaps; - } else if (strcmp(func_name, "chvt") == 0) { - func = chvt; - (*arg).ui = atoi(arg_value); - } else if (strcmp(func_name, "spawn") == 0) { - func = spawn; - char *values[] = {arg_value, arg_value2, arg_value3, arg_value4, - arg_value5}; - (*arg).v = combine_args_until_empty(values, 5); - } else if (strcmp(func_name, "spawn_shell") == 0) { - func = spawn_shell; - char *values[] = {arg_value, arg_value2, arg_value3, arg_value4, - arg_value5}; - (*arg).v = combine_args_until_empty(values, 5); - } else if (strcmp(func_name, "spawn_on_empty") == 0) { - func = spawn_on_empty; - (*arg).v = strdup(arg_value); // 注意:之后需要释放这个内存 - (*arg).ui = 1 << (atoi(arg_value2) - 1); - } else if (strcmp(func_name, "quit") == 0) { - func = quit; - } else if (strcmp(func_name, "create_virtual_output") == 0) { - func = create_virtual_output; - } else if (strcmp(func_name, "destroy_all_virtual_output") == 0) { - func = destroy_all_virtual_output; - } else if (strcmp(func_name, "moveresize") == 0) { - func = moveresize; - (*arg).ui = parse_mouse_action(arg_value); - } else if (strcmp(func_name, "togglemaximizescreen") == 0) { - func = togglemaximizescreen; - } else if (strcmp(func_name, "viewtoleft_have_client") == 0) { - func = viewtoleft_have_client; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "viewtoright_have_client") == 0) { - func = viewtoright_have_client; - (*arg).i = atoi(arg_value); - } else if (strcmp(func_name, "reload_config") == 0) { - func = reload_config; - } else if (strcmp(func_name, "tag") == 0) { - func = tag; - (*arg).ui = 1 << (atoi(arg_value) - 1); - (*arg).i = atoi(arg_value2); - } else if (strcmp(func_name, "view") == 0) { - func = bind_to_view; - - uint32_t mask = 0; - char *token; - char *arg_copy = strdup(arg_value); - - if (arg_copy != NULL) { - char *saveptr = NULL; - token = strtok_r(arg_copy, "|", &saveptr); - - while (token != NULL) { - int32_t num = atoi(token); - if (num > 0 && num <= LENGTH(tags)) { - mask |= (1 << (num - 1)); - } - token = strtok_r(NULL, "|", &saveptr); - } - - free(arg_copy); - } - - if (mask) { - (*arg).ui = mask; - } else { - (*arg).ui = atoi(arg_value); - } - (*arg).i = atoi(arg_value2); - } else if (strcmp(func_name, "viewcrossmon") == 0) { - func = viewcrossmon; - (*arg).ui = 1 << (atoi(arg_value) - 1); - (*arg).v = strdup(arg_value2); - } else if (strcmp(func_name, "tagcrossmon") == 0) { - func = tagcrossmon; - (*arg).ui = 1 << (atoi(arg_value) - 1); - (*arg).v = strdup(arg_value2); - } else if (strcmp(func_name, "toggletag") == 0) { - func = toggletag; - (*arg).ui = 1 << (atoi(arg_value) - 1); - } else if (strcmp(func_name, "toggleview") == 0) { - func = toggleview; - (*arg).ui = 1 << (atoi(arg_value) - 1); - } else if (strcmp(func_name, "comboview") == 0) { - func = comboview; - (*arg).ui = 1 << (atoi(arg_value) - 1); - } else if (strcmp(func_name, "smartmovewin") == 0) { - func = smartmovewin; - (*arg).i = parse_direction(arg_value); - } else if (strcmp(func_name, "smartresizewin") == 0) { - func = smartresizewin; - (*arg).i = parse_direction(arg_value); - } else if (strcmp(func_name, "resizewin") == 0) { - func = resizewin; - (*arg).ui = parse_num_type(arg_value); - (*arg).ui2 = parse_num_type(arg_value2); - (*arg).i = (*arg).ui == NUM_TYPE_DEFAULT ? atoi(arg_value) - : atoi(arg_value + 1); - (*arg).i2 = (*arg).ui2 == NUM_TYPE_DEFAULT ? atoi(arg_value2) - : atoi(arg_value2 + 1); - } else if (strcmp(func_name, "movewin") == 0) { - func = movewin; - (*arg).ui = parse_num_type(arg_value); - (*arg).ui2 = parse_num_type(arg_value2); - (*arg).i = (*arg).ui == NUM_TYPE_DEFAULT ? atoi(arg_value) - : atoi(arg_value + 1); - (*arg).i2 = (*arg).ui2 == NUM_TYPE_DEFAULT ? atoi(arg_value2) - : atoi(arg_value2 + 1); - } else if (strcmp(func_name, "toggle_named_scratchpad") == 0) { - func = toggle_named_scratchpad; - (*arg).v = strdup(arg_value); - (*arg).v2 = strdup(arg_value2); - (*arg).v3 = strdup(arg_value3); - } else if (strcmp(func_name, "disable_monitor") == 0) { - func = disable_monitor; - (*arg).v = strdup(arg_value); - } else if (strcmp(func_name, "enable_monitor") == 0) { - func = enable_monitor; - (*arg).v = strdup(arg_value); - } else if (strcmp(func_name, "toggle_monitor") == 0) { - func = toggle_monitor; - (*arg).v = strdup(arg_value); - } else if (strcmp(func_name, "scroller_stack") == 0) { - func = scroller_stack; - (*arg).i = parse_direction(arg_value); - } else { - return NULL; - } - return func; -} - -void set_env() { - for (int32_t i = 0; i < config.env_count; i++) { - setenv(config.env[i]->type, config.env[i]->value, 1); - } -} - -void run_exec() { - Arg arg; - - for (int32_t i = 0; i < config.exec_count; i++) { - arg.v = config.exec[i]; - spawn_shell(&arg); - } -} - -void run_exec_once() { - Arg arg; - - for (int32_t i = 0; i < config.exec_once_count; i++) { - arg.v = config.exec_once[i]; - spawn_shell(&arg); - } -} - -bool parse_option(Config *config, char *key, char *value) { - if (strcmp(key, "keymode") == 0) { - set_current_keymode(config, value); - } else if (strcmp(key, "animations") == 0) { - config->animations = atoi(value); - } else if (strcmp(key, "layer_animations") == 0) { - config->layer_animations = atoi(value); - } else if (strcmp(key, "animation_type_open") == 0) { - snprintf(config->animation_type_open, - sizeof(config->animation_type_open), "%.9s", - value); // string limit to 9 char - } else if (strcmp(key, "animation_type_close") == 0) { - snprintf(config->animation_type_close, - sizeof(config->animation_type_close), "%.9s", - value); // string limit to 9 char - } else if (strcmp(key, "layer_animation_type_open") == 0) { - snprintf(config->layer_animation_type_open, - sizeof(config->layer_animation_type_open), "%.9s", - value); // string limit to 9 char - } else if (strcmp(key, "layer_animation_type_close") == 0) { - snprintf(config->layer_animation_type_close, - sizeof(config->layer_animation_type_close), "%.9s", - value); // string limit to 9 char - } else if (strcmp(key, "animation_fade_in") == 0) { - config->animation_fade_in = atoi(value); - } else if (strcmp(key, "animation_fade_out") == 0) { - config->animation_fade_out = atoi(value); - } else if (strcmp(key, "tag_animation_direction") == 0) { - config->tag_animation_direction = atoi(value); - } else if (strcmp(key, "zoom_initial_ratio") == 0) { - config->zoom_initial_ratio = atof(value); - } else if (strcmp(key, "zoom_end_ratio") == 0) { - config->zoom_end_ratio = atof(value); - } else if (strcmp(key, "fadein_begin_opacity") == 0) { - config->fadein_begin_opacity = atof(value); - } else if (strcmp(key, "fadeout_begin_opacity") == 0) { - config->fadeout_begin_opacity = atof(value); - } else if (strcmp(key, "animation_duration_move") == 0) { - config->animation_duration_move = atoi(value); - } else if (strcmp(key, "animation_duration_open") == 0) { - config->animation_duration_open = atoi(value); - } else if (strcmp(key, "animation_duration_tag") == 0) { - config->animation_duration_tag = atoi(value); - } else if (strcmp(key, "animation_duration_close") == 0) { - config->animation_duration_close = atoi(value); - } else if (strcmp(key, "animation_duration_focus") == 0) { - config->animation_duration_focus = atoi(value); - } else if (strcmp(key, "animation_curve_move") == 0) { - int32_t num = - parse_double_array(value, config->animation_curve_move, 4); - if (num != 4) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " - "animation_curve_move: %s\n", - value); - return false; - } - } else if (strcmp(key, "animation_curve_open") == 0) { - int32_t num = - parse_double_array(value, config->animation_curve_open, 4); - if (num != 4) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " - "animation_curve_open: %s\n", - value); - return false; - } - } else if (strcmp(key, "animation_curve_tag") == 0) { - int32_t num = parse_double_array(value, config->animation_curve_tag, 4); - if (num != 4) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " - "animation_curve_tag: %s\n", - value); - return false; - } - } else if (strcmp(key, "animation_curve_close") == 0) { - int32_t num = - parse_double_array(value, config->animation_curve_close, 4); - if (num != 4) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " - "animation_curve_close: %s\n", - value); - return false; - } - } else if (strcmp(key, "animation_curve_focus") == 0) { - int32_t num = - parse_double_array(value, config->animation_curve_focus, 4); - if (num != 4) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " - "animation_curve_focus: %s\n", - value); - return false; - } - } else if (strcmp(key, "animation_curve_opafadein") == 0) { - int32_t num = - parse_double_array(value, config->animation_curve_opafadein, 4); - if (num != 4) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " - "animation_curve_opafadein: %s\n", - value); - return false; - } - } else if (strcmp(key, "animation_curve_opafadeout") == 0) { - int32_t num = - parse_double_array(value, config->animation_curve_opafadeout, 4); - if (num != 4) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to parse " - "animation_curve_opafadeout: %s\n", - value); - return false; - } - } else if (strcmp(key, "scroller_structs") == 0) { - config->scroller_structs = atoi(value); - } else if (strcmp(key, "scroller_default_proportion") == 0) { - config->scroller_default_proportion = atof(value); - } else if (strcmp(key, "scroller_default_proportion_single") == 0) { - config->scroller_default_proportion_single = atof(value); - } else if (strcmp(key, "scroller_ignore_proportion_single") == 0) { - config->scroller_ignore_proportion_single = atoi(value); - } else if (strcmp(key, "scroller_focus_center") == 0) { - config->scroller_focus_center = atoi(value); - } else if (strcmp(key, "scroller_prefer_center") == 0) { - config->scroller_prefer_center = atoi(value); - } else if (strcmp(key, "edge_scroller_pointer_focus") == 0) { - config->edge_scroller_pointer_focus = atoi(value); - } else if (strcmp(key, "focus_cross_monitor") == 0) { - config->focus_cross_monitor = atoi(value); - } else if (strcmp(key, "exchange_cross_monitor") == 0) { - config->exchange_cross_monitor = atoi(value); - } else if (strcmp(key, "scratchpad_cross_monitor") == 0) { - config->scratchpad_cross_monitor = atoi(value); - } else if (strcmp(key, "focus_cross_tag") == 0) { - config->focus_cross_tag = atoi(value); - } else if (strcmp(key, "view_current_to_back") == 0) { - config->view_current_to_back = atoi(value); - } else if (strcmp(key, "blur") == 0) { - config->blur = atoi(value); - } else if (strcmp(key, "blur_layer") == 0) { - config->blur_layer = atoi(value); - } else if (strcmp(key, "blur_optimized") == 0) { - config->blur_optimized = atoi(value); - } else if (strcmp(key, "border_radius") == 0) { - config->border_radius = atoi(value); - } else if (strcmp(key, "blur_params_num_passes") == 0) { - config->blur_params.num_passes = atoi(value); - } else if (strcmp(key, "blur_params_radius") == 0) { - config->blur_params.radius = atoi(value); - } else if (strcmp(key, "blur_params_noise") == 0) { - config->blur_params.noise = atof(value); - } else if (strcmp(key, "blur_params_brightness") == 0) { - config->blur_params.brightness = atof(value); - } else if (strcmp(key, "blur_params_contrast") == 0) { - config->blur_params.contrast = atof(value); - } else if (strcmp(key, "blur_params_saturation") == 0) { - config->blur_params.saturation = atof(value); - } else if (strcmp(key, "shadows") == 0) { - config->shadows = atoi(value); - } else if (strcmp(key, "shadow_only_floating") == 0) { - config->shadow_only_floating = atoi(value); - } else if (strcmp(key, "layer_shadows") == 0) { - config->layer_shadows = atoi(value); - } else if (strcmp(key, "shadows_size") == 0) { - config->shadows_size = atoi(value); - } else if (strcmp(key, "shadows_blur") == 0) { - config->shadows_blur = atof(value); - } else if (strcmp(key, "shadows_position_x") == 0) { - config->shadows_position_x = atoi(value); - } else if (strcmp(key, "shadows_position_y") == 0) { - config->shadows_position_y = atoi(value); - } else if (strcmp(key, "single_scratchpad") == 0) { - config->single_scratchpad = atoi(value); - } else if (strcmp(key, "xwayland_persistence") == 0) { - config->xwayland_persistence = atoi(value); - } else if (strcmp(key, "syncobj_enable") == 0) { - config->syncobj_enable = atoi(value); - } else if (strcmp(key, "allow_tearing") == 0) { - config->allow_tearing = atoi(value); - } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { - config->allow_shortcuts_inhibit = atoi(value); - } else if (strcmp(key, "allow_lock_transparent") == 0) { - config->allow_lock_transparent = atoi(value); - } else if (strcmp(key, "no_border_when_single") == 0) { - config->no_border_when_single = atoi(value); - } else if (strcmp(key, "no_radius_when_single") == 0) { - config->no_radius_when_single = atoi(value); - } else if (strcmp(key, "snap_distance") == 0) { - config->snap_distance = atoi(value); - } else if (strcmp(key, "enable_floating_snap") == 0) { - config->enable_floating_snap = atoi(value); - } else if (strcmp(key, "drag_tile_to_tile") == 0) { - config->drag_tile_to_tile = atoi(value); - } else if (strcmp(key, "swipe_min_threshold") == 0) { - config->swipe_min_threshold = atoi(value); - } else if (strcmp(key, "focused_opacity") == 0) { - config->focused_opacity = atof(value); - } else if (strcmp(key, "unfocused_opacity") == 0) { - config->unfocused_opacity = atof(value); - } else if (strcmp(key, "xkb_rules_rules") == 0) { - strncpy(xkb_rules_rules, value, sizeof(xkb_rules_rules) - 1); - xkb_rules_rules[sizeof(xkb_rules_rules) - 1] = - '\0'; // 确保字符串以 null 结尾 - } else if (strcmp(key, "xkb_rules_model") == 0) { - strncpy(xkb_rules_model, value, sizeof(xkb_rules_model) - 1); - xkb_rules_model[sizeof(xkb_rules_model) - 1] = - '\0'; // 确保字符串以 null 结尾 - } else if (strcmp(key, "xkb_rules_layout") == 0) { - strncpy(xkb_rules_layout, value, sizeof(xkb_rules_layout) - 1); - xkb_rules_layout[sizeof(xkb_rules_layout) - 1] = - '\0'; // 确保字符串以 null 结尾 - } else if (strcmp(key, "xkb_rules_variant") == 0) { - strncpy(xkb_rules_variant, value, sizeof(xkb_rules_variant) - 1); - xkb_rules_variant[sizeof(xkb_rules_variant) - 1] = - '\0'; // 确保字符串以 null 结尾 - } else if (strcmp(key, "xkb_rules_options") == 0) { - strncpy(xkb_rules_options, value, sizeof(xkb_rules_options) - 1); - xkb_rules_options[sizeof(xkb_rules_options) - 1] = - '\0'; // 确保字符串以 null 结尾 - } else if (strcmp(key, "scroller_proportion_preset") == 0) { - // 1. 统计 value 中有多少个逗号,确定需要解析的浮点数个数 - int32_t count = 0; // 初始化为 0 - for (const char *p = value; *p; p++) { - if (*p == ',') - count++; - } - int32_t float_count = count + 1; // 浮点数的数量是逗号数量加 1 - - // 2. 动态分配内存,存储浮点数 - config->scroller_proportion_preset = - (float *)malloc(float_count * sizeof(float)); - if (!config->scroller_proportion_preset) { - fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Memory " - "allocation failed\n"); - return false; - } - - // 3. 解析 value 中的浮点数 - char *value_copy = - strdup(value); // 复制 value,因为 strtok 会修改原字符串 - char *token = strtok(value_copy, ","); - int32_t i = 0; - float value_set; - - while (token != NULL && i < float_count) { - if (sscanf(token, "%f", &value_set) != 1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid float " - "value in " - "scroller_proportion_preset: %s\n", - token); - free(value_copy); - free(config->scroller_proportion_preset); - config->scroller_proportion_preset = NULL; - return false; - } - - // Clamp the value between 0.0 and 1.0 (or your desired - // range) - config->scroller_proportion_preset[i] = - CLAMP_FLOAT(value_set, 0.1f, 1.0f); - - token = strtok(NULL, ","); - i++; - } - - // 4. 检查解析的浮点数数量是否匹配 - if (i != float_count) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "scroller_proportion_preset format: %s\n", - value); - free(value_copy); - free(config->scroller_proportion_preset); // 释放已分配的内存 - config->scroller_proportion_preset = NULL; // 防止野指针 - config->scroller_proportion_preset_count = 0; - return false; - } - config->scroller_proportion_preset_count = float_count; - - // 5. 释放临时复制的字符串 - free(value_copy); - } else if (strcmp(key, "circle_layout") == 0) { - // 1. 统计 value 中有多少个逗号,确定需要解析的字符串个数 - int32_t count = 0; // 初始化为 0 - for (const char *p = value; *p; p++) { - if (*p == ',') - count++; - } - int32_t string_count = count + 1; // 字符串的数量是逗号数量加 1 - - // 2. 动态分配内存,存储字符串指针 - config->circle_layout = (char **)malloc(string_count * sizeof(char *)); - memset(config->circle_layout, 0, string_count * sizeof(char *)); - if (!config->circle_layout) { - fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Memory " - "allocation failed\n"); - return false; - } - - // 3. 解析 value 中的字符串 - char *value_copy = - strdup(value); // 复制 value,因为 strtok 会修改原字符串 - char *token = strtok(value_copy, ","); - int32_t i = 0; - char *cleaned_token; - while (token != NULL && i < string_count) { - // 为每个字符串分配内存并复制内容 - cleaned_token = sanitize_string(token); - config->circle_layout[i] = strdup(cleaned_token); - if (!config->circle_layout[i]) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Memory allocation " - "failed for " - "string: %s\n", - token); - // 释放之前分配的内存 - for (int32_t j = 0; j < i; j++) { - free(config->circle_layout[j]); - } - free(config->circle_layout); - free(value_copy); - config->circle_layout = NULL; // 防止野指针 - config->circle_layout_count = 0; - return false; - } - token = strtok(NULL, ","); - i++; - } - - // 4. 检查解析的字符串数量是否匹配 - if (i != string_count) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid circle_layout " - "format: %s\n", - value); - // 释放之前分配的内存 - for (int32_t j = 0; j < i; j++) { - free(config->circle_layout[j]); - } - free(config->circle_layout); - free(value_copy); - config->circle_layout = NULL; // 防止野指针 - config->circle_layout_count = 0; - return false; - } - config->circle_layout_count = string_count; - - // 5. 释放临时复制的字符串 - free(value_copy); - } else if (strcmp(key, "new_is_master") == 0) { - config->new_is_master = atoi(value); - } else if (strcmp(key, "default_mfact") == 0) { - config->default_mfact = atof(value); - } else if (strcmp(key, "default_nmaster") == 0) { - config->default_nmaster = atoi(value); - } else if (strcmp(key, "center_master_overspread") == 0) { - config->center_master_overspread = atoi(value); - } else if (strcmp(key, "center_when_single_stack") == 0) { - config->center_when_single_stack = atoi(value); - } else if (strcmp(key, "hotarea_size") == 0) { - config->hotarea_size = atoi(value); - } else if (strcmp(key, "hotarea_corner") == 0) { - config->hotarea_corner = atoi(value); - } else if (strcmp(key, "enable_hotarea") == 0) { - config->enable_hotarea = atoi(value); - } else if (strcmp(key, "ov_tab_mode") == 0) { - config->ov_tab_mode = atoi(value); - } else if (strcmp(key, "overviewgappi") == 0) { - config->overviewgappi = atoi(value); - } else if (strcmp(key, "overviewgappo") == 0) { - config->overviewgappo = atoi(value); - } else if (strcmp(key, "cursor_hide_timeout") == 0) { - config->cursor_hide_timeout = atoi(value); - } else if (strcmp(key, "axis_bind_apply_timeout") == 0) { - config->axis_bind_apply_timeout = atoi(value); - } else if (strcmp(key, "focus_on_activate") == 0) { - config->focus_on_activate = atoi(value); - } else if (strcmp(key, "numlockon") == 0) { - config->numlockon = atoi(value); - } else if (strcmp(key, "idleinhibit_ignore_visible") == 0) { - config->idleinhibit_ignore_visible = atoi(value); - } else if (strcmp(key, "sloppyfocus") == 0) { - config->sloppyfocus = atoi(value); - } else if (strcmp(key, "warpcursor") == 0) { - config->warpcursor = atoi(value); - } else if (strcmp(key, "drag_corner") == 0) { - config->drag_corner = atoi(value); - } else if (strcmp(key, "drag_warp_cursor") == 0) { - config->drag_warp_cursor = atoi(value); - } else if (strcmp(key, "smartgaps") == 0) { - config->smartgaps = atoi(value); - } else if (strcmp(key, "repeat_rate") == 0) { - config->repeat_rate = atoi(value); - } else if (strcmp(key, "repeat_delay") == 0) { - config->repeat_delay = atoi(value); - } else if (strcmp(key, "disable_trackpad") == 0) { - config->disable_trackpad = atoi(value); - } else if (strcmp(key, "tap_to_click") == 0) { - config->tap_to_click = atoi(value); - } else if (strcmp(key, "tap_and_drag") == 0) { - config->tap_and_drag = atoi(value); - } else if (strcmp(key, "drag_lock") == 0) { - config->drag_lock = atoi(value); - } else if (strcmp(key, "mouse_natural_scrolling") == 0) { - config->mouse_natural_scrolling = atoi(value); - } else if (strcmp(key, "trackpad_natural_scrolling") == 0) { - config->trackpad_natural_scrolling = atoi(value); - } else if (strcmp(key, "cursor_size") == 0) { - config->cursor_size = atoi(value); - } else if (strcmp(key, "cursor_theme") == 0) { - config->cursor_theme = strdup(value); - } else if (strcmp(key, "disable_while_typing") == 0) { - config->disable_while_typing = atoi(value); - } else if (strcmp(key, "left_handed") == 0) { - config->left_handed = atoi(value); - } else if (strcmp(key, "middle_button_emulation") == 0) { - config->middle_button_emulation = atoi(value); - } else if (strcmp(key, "accel_profile") == 0) { - config->accel_profile = atoi(value); - } else if (strcmp(key, "accel_speed") == 0) { - config->accel_speed = atof(value); - } else if (strcmp(key, "scroll_method") == 0) { - config->scroll_method = atoi(value); - } else if (strcmp(key, "scroll_button") == 0) { - config->scroll_button = atoi(value); - } else if (strcmp(key, "click_method") == 0) { - config->click_method = atoi(value); - } else if (strcmp(key, "send_events_mode") == 0) { - config->send_events_mode = atoi(value); - } else if (strcmp(key, "button_map") == 0) { - config->button_map = atoi(value); - } else if (strcmp(key, "axis_scroll_factor") == 0) { - config->axis_scroll_factor = atof(value); - } else if (strcmp(key, "gappih") == 0) { - config->gappih = atoi(value); - } else if (strcmp(key, "gappiv") == 0) { - config->gappiv = atoi(value); - } else if (strcmp(key, "gappoh") == 0) { - config->gappoh = atoi(value); - } else if (strcmp(key, "gappov") == 0) { - config->gappov = atoi(value); - } else if (strcmp(key, "scratchpad_width_ratio") == 0) { - config->scratchpad_width_ratio = atof(value); - } else if (strcmp(key, "scratchpad_height_ratio") == 0) { - config->scratchpad_height_ratio = atof(value); - } else if (strcmp(key, "borderpx") == 0) { - config->borderpx = atoi(value); - } else if (strcmp(key, "rootcolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid rootcolor " - "format: " - "%s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->rootcolor, color); - } - - } else if (strcmp(key, "shadowscolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid shadowscolor " - "format: %s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->shadowscolor, color); - } - } else if (strcmp(key, "bordercolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid bordercolor " - "format: %s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->bordercolor, color); - } - } else if (strcmp(key, "focuscolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid focuscolor " - "format: %s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->focuscolor, color); - } - } else if (strcmp(key, "maximizescreencolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "maximizescreencolor " - "format: %s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->maximizescreencolor, color); - } - } else if (strcmp(key, "urgentcolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid urgentcolor " - "format: %s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->urgentcolor, color); - } - } else if (strcmp(key, "scratchpadcolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "scratchpadcolor " - "format: %s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->scratchpadcolor, color); - } - } else if (strcmp(key, "globalcolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid globalcolor " - "format: %s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->globalcolor, color); - } - } else if (strcmp(key, "overlaycolor") == 0) { - int64_t color = parse_color(value); - if (color == -1) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid overlaycolor " - "format: %s\n", - value); - return false; - } else { - convert_hex_to_rgba(config->overlaycolor, color); - } - } else if (strcmp(key, "monitorrule") == 0) { - config->monitor_rules = - realloc(config->monitor_rules, (config->monitor_rules_count + 1) * - sizeof(ConfigMonitorRule)); - if (!config->monitor_rules) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for monitor rules\n"); - return false; - } - - ConfigMonitorRule *rule = - &config->monitor_rules[config->monitor_rules_count]; - memset(rule, 0, sizeof(ConfigMonitorRule)); - - // 设置默认值 - rule->name = NULL; - rule->rr = 0; - rule->scale = 1.0f; - rule->x = INT32_MAX; - rule->y = INT32_MAX; - rule->width = -1; - rule->height = -1; - rule->refresh = 0.0f; - rule->vrr = 0; - - bool parse_error = false; - char *token = strtok(value, ","); - while (token != NULL) { - char *colon = strchr(token, ':'); - if (colon != NULL) { - *colon = '\0'; - char *key = token; - char *val = colon + 1; - - trim_whitespace(key); - trim_whitespace(val); - - if (strcmp(key, "name") == 0) { - rule->name = strdup(val); - } else if (strcmp(key, "rr") == 0) { - rule->rr = CLAMP_INT(atoi(val), 0, 7); - } else if (strcmp(key, "scale") == 0) { - rule->scale = CLAMP_FLOAT(atof(val), 0.001f, 1000.0f); - } else if (strcmp(key, "x") == 0) { - rule->x = atoi(val); - } else if (strcmp(key, "y") == 0) { - rule->y = atoi(val); - } else if (strcmp(key, "width") == 0) { - rule->width = CLAMP_INT(atoi(val), 1, INT32_MAX); - } else if (strcmp(key, "height") == 0) { - rule->height = CLAMP_INT(atoi(val), 1, INT32_MAX); - } else if (strcmp(key, "refresh") == 0) { - rule->refresh = CLAMP_FLOAT(atof(val), 0.001f, 1000.0f); - } else if (strcmp(key, "vrr") == 0) { - rule->vrr = CLAMP_INT(atoi(val), 0, 1); - } else { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown " - "monitor rule " - "option:\033[1m\033[31m %s\n", - key); - parse_error = true; - } - } - token = strtok(NULL, ","); - } - - config->monitor_rules_count++; - return !parse_error; - } else if (strcmp(key, "tagrule") == 0) { - config->tag_rules = - realloc(config->tag_rules, - (config->tag_rules_count + 1) * sizeof(ConfigTagRule)); - if (!config->tag_rules) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for tag rules\n"); - return false; - } - - ConfigTagRule *rule = &config->tag_rules[config->tag_rules_count]; - memset(rule, 0, sizeof(ConfigTagRule)); - - // 设置默认值 - rule->id = 0; - rule->layout_name = NULL; - rule->monitor_name = NULL; - rule->nmaster = 0; - rule->mfact = 0.0f; - rule->no_render_border = 0; - rule->no_hide = 0; - - bool parse_error = false; - char *token = strtok(value, ","); - while (token != NULL) { - char *colon = strchr(token, ':'); - if (colon != NULL) { - *colon = '\0'; - char *key = token; - char *val = colon + 1; - - trim_whitespace(key); - trim_whitespace(val); - - if (strcmp(key, "id") == 0) { - rule->id = CLAMP_INT(atoi(val), 0, LENGTH(tags)); - } else if (strcmp(key, "layout_name") == 0) { - rule->layout_name = strdup(val); - } else if (strcmp(key, "monitor_name") == 0) { - rule->monitor_name = strdup(val); - } else if (strcmp(key, "no_render_border") == 0) { - rule->no_render_border = CLAMP_INT(atoi(val), 0, 1); - } else if (strcmp(key, "no_hide") == 0) { - rule->no_hide = CLAMP_INT(atoi(val), 0, 1); - } else if (strcmp(key, "nmaster") == 0) { - rule->nmaster = CLAMP_INT(atoi(val), 1, 99); - } else if (strcmp(key, "mfact") == 0) { - rule->mfact = CLAMP_FLOAT(atof(val), 0.1f, 0.9f); - } else { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown " - "tag rule " - "option:\033[1m\033[31m %s\n", - key); - parse_error = true; - } - } - token = strtok(NULL, ","); - } - - config->tag_rules_count++; - return !parse_error; - } else if (strcmp(key, "layerrule") == 0) { - config->layer_rules = - realloc(config->layer_rules, - (config->layer_rules_count + 1) * sizeof(ConfigLayerRule)); - if (!config->layer_rules) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for layer rules\n"); - return false; - } - - ConfigLayerRule *rule = &config->layer_rules[config->layer_rules_count]; - memset(rule, 0, sizeof(ConfigLayerRule)); - - // 设置默认值 - rule->layer_name = NULL; - rule->animation_type_open = NULL; - rule->animation_type_close = NULL; - rule->noblur = 0; - rule->noanim = 0; - rule->noshadow = 0; - - bool parse_error = false; - char *token = strtok(value, ","); - while (token != NULL) { - char *colon = strchr(token, ':'); - if (colon != NULL) { - *colon = '\0'; - char *key = token; - char *val = colon + 1; - - trim_whitespace(key); - trim_whitespace(val); - - if (strcmp(key, "layer_name") == 0) { - rule->layer_name = strdup(val); - } else if (strcmp(key, "animation_type_open") == 0) { - rule->animation_type_open = strdup(val); - } else if (strcmp(key, "animation_type_close") == 0) { - rule->animation_type_close = strdup(val); - } else if (strcmp(key, "noblur") == 0) { - rule->noblur = CLAMP_INT(atoi(val), 0, 1); - } else if (strcmp(key, "noanim") == 0) { - rule->noanim = CLAMP_INT(atoi(val), 0, 1); - } else if (strcmp(key, "noshadow") == 0) { - rule->noshadow = CLAMP_INT(atoi(val), 0, 1); - } else { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown " - "layer rule " - "option:\033[1m\033[31m %s\n", - key); - parse_error = true; - } - } - token = strtok(NULL, ","); - } - - // 如果没有指定布局名称,则使用默认值 - if (rule->layer_name == NULL) { - rule->layer_name = strdup("default"); - } - - config->layer_rules_count++; - return !parse_error; - } else if (strcmp(key, "windowrule") == 0) { - config->window_rules = - realloc(config->window_rules, - (config->window_rules_count + 1) * sizeof(ConfigWinRule)); - if (!config->window_rules) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for window rules\n"); - return false; - } - - ConfigWinRule *rule = &config->window_rules[config->window_rules_count]; - memset(rule, 0, sizeof(ConfigWinRule)); - - // int32_t rule value, relay to a client property - rule->isfloating = -1; - rule->isfullscreen = -1; - rule->isnoborder = -1; - rule->isnoshadow = -1; - rule->isnoradius = -1; - rule->isnoanimation = -1; - rule->isopensilent = -1; - rule->istagsilent = -1; - rule->isnamedscratchpad = -1; - rule->isunglobal = -1; - rule->isglobal = -1; - rule->isoverlay = -1; - rule->allow_shortcuts_inhibit = -1; - rule->ignore_maximize = -1; - rule->ignore_minimize = -1; - rule->isnosizehint = -1; - rule->isterm = -1; - rule->allow_csd = -1; - rule->force_maximize = -1; - rule->force_tearing = -1; - rule->noswallow = -1; - rule->noblur = -1; - rule->nofocus = -1; - rule->nofadein = -1; - rule->nofadeout = -1; - rule->no_force_center = -1; - - // string rule value, relay to a client property - rule->animation_type_open = NULL; - rule->animation_type_close = NULL; - - // float rule value, relay to a client property - rule->focused_opacity = 0; - rule->unfocused_opacity = 0; - rule->scroller_proportion_single = 0.0f; - rule->scroller_proportion = 0; - - // special rule value,not directly set to client property - rule->tags = 0; - rule->offsetx = 0; - rule->offsety = 0; - rule->width = 0; - rule->height = 0; - rule->monitor = NULL; - rule->id = NULL; - rule->title = NULL; - - rule->globalkeybinding = (KeyBinding){0}; - - bool parse_error = false; - char *token = strtok(value, ","); - while (token != NULL) { - char *colon = strchr(token, ':'); - if (colon != NULL) { - *colon = '\0'; - char *key = token; - char *val = colon + 1; - - trim_whitespace(key); - trim_whitespace(val); - - if (strcmp(key, "isfloating") == 0) { - rule->isfloating = atoi(val); - } else if (strcmp(key, "title") == 0) { - rule->title = strdup(val); - } else if (strcmp(key, "appid") == 0) { - rule->id = strdup(val); - } else if (strcmp(key, "animation_type_open") == 0) { - rule->animation_type_open = strdup(val); - } else if (strcmp(key, "animation_type_close") == 0) { - rule->animation_type_close = strdup(val); - } else if (strcmp(key, "tags") == 0) { - rule->tags = 1 << (atoi(val) - 1); - } else if (strcmp(key, "monitor") == 0) { - rule->monitor = strdup(val); - } else if (strcmp(key, "offsetx") == 0) { - rule->offsetx = atoi(val); - } else if (strcmp(key, "offsety") == 0) { - rule->offsety = atoi(val); - } else if (strcmp(key, "nofocus") == 0) { - rule->nofocus = atoi(val); - } else if (strcmp(key, "nofadein") == 0) { - rule->nofadein = atoi(val); - } else if (strcmp(key, "nofadeout") == 0) { - rule->nofadeout = atoi(val); - } else if (strcmp(key, "no_force_center") == 0) { - rule->no_force_center = atoi(val); - } else if (strcmp(key, "width") == 0) { - rule->width = atoi(val); - } else if (strcmp(key, "height") == 0) { - rule->height = atoi(val); - } else if (strcmp(key, "isnoborder") == 0) { - rule->isnoborder = atoi(val); - } else if (strcmp(key, "isnoshadow") == 0) { - rule->isnoshadow = atoi(val); - } else if (strcmp(key, "isnoradius") == 0) { - rule->isnoradius = atoi(val); - } else if (strcmp(key, "isnoanimation") == 0) { - rule->isnoanimation = atoi(val); - } else if (strcmp(key, "isopensilent") == 0) { - rule->isopensilent = atoi(val); - } else if (strcmp(key, "istagsilent") == 0) { - rule->istagsilent = atoi(val); - } else if (strcmp(key, "isnamedscratchpad") == 0) { - rule->isnamedscratchpad = atoi(val); - } else if (strcmp(key, "isunglobal") == 0) { - rule->isunglobal = atoi(val); - } else if (strcmp(key, "isglobal") == 0) { - rule->isglobal = atoi(val); - } else if (strcmp(key, "scroller_proportion_single") == 0) { - rule->scroller_proportion_single = atof(val); - } else if (strcmp(key, "unfocused_opacity") == 0) { - rule->unfocused_opacity = atof(val); - } else if (strcmp(key, "focused_opacity") == 0) { - rule->focused_opacity = atof(val); - } else if (strcmp(key, "isoverlay") == 0) { - rule->isoverlay = atoi(val); - } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { - rule->allow_shortcuts_inhibit = atoi(val); - } else if (strcmp(key, "ignore_maximize") == 0) { - rule->ignore_maximize = atoi(val); - } else if (strcmp(key, "ignore_minimize") == 0) { - rule->ignore_minimize = atoi(val); - } else if (strcmp(key, "isnosizehint") == 0) { - rule->isnosizehint = atoi(val); - } else if (strcmp(key, "isterm") == 0) { - rule->isterm = atoi(val); - } else if (strcmp(key, "allow_csd") == 0) { - rule->allow_csd = atoi(val); - } else if (strcmp(key, "force_maximize") == 0) { - rule->force_maximize = atoi(val); - } else if (strcmp(key, "force_tearing") == 0) { - rule->force_tearing = atoi(val); - } else if (strcmp(key, "noswallow") == 0) { - rule->noswallow = atoi(val); - } else if (strcmp(key, "noblur") == 0) { - rule->noblur = atoi(val); - } else if (strcmp(key, "scroller_proportion") == 0) { - rule->scroller_proportion = atof(val); - } else if (strcmp(key, "isfullscreen") == 0) { - rule->isfullscreen = atoi(val); - } else if (strcmp(key, "globalkeybinding") == 0) { - char mod_str[256], keysym_str[256]; - sscanf(val, "%255[^-]-%255[a-zA-Z]", mod_str, keysym_str); - trim_whitespace(mod_str); - trim_whitespace(keysym_str); - rule->globalkeybinding.mod = parse_mod(mod_str); - rule->globalkeybinding.keysymcode = - parse_key(keysym_str, false); - if (rule->globalkeybinding.mod == UINT32_MAX) { - return false; - } - if (rule->globalkeybinding.keysymcode.type == - KEY_TYPE_SYM && - rule->globalkeybinding.keysymcode.keysym == - XKB_KEY_NoSymbol) { - return false; - } - } else { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown " - "window rule " - "option:\033[1m\033[31m %s\n", - key); - parse_error = true; - } - } - token = strtok(NULL, ","); - } - config->window_rules_count++; - return !parse_error; - } else if (strncmp(key, "env", 3) == 0) { - - char env_type[256], env_value[256]; - if (sscanf(value, "%255[^,],%255[^\n]", env_type, env_value) < 2) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid bind format: " - "\033[1m\033[31m%s\n", - value); - return false; - } - trim_whitespace(env_type); - trim_whitespace(env_value); - - ConfigEnv *env = calloc(1, sizeof(ConfigEnv)); - env->type = strdup(env_type); - env->value = strdup(env_value); - - config->env = - realloc(config->env, (config->env_count + 1) * sizeof(ConfigEnv)); - if (!config->env) { - free(env->type); - free(env->value); - free(env); - fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Failed to " - "allocate memory for env\n"); - return false; - } - - config->env[config->env_count] = env; - config->env_count++; - - } else if (strncmp(key, "exec", 9) == 0) { - char **new_exec = - realloc(config->exec, (config->exec_count + 1) * sizeof(char *)); - if (!new_exec) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for exec\n"); - return false; - } - config->exec = new_exec; - - config->exec[config->exec_count] = strdup(value); - if (!config->exec[config->exec_count]) { - fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Failed to " - "duplicate exec string\n"); - return false; - } - - config->exec_count++; - - } else if (strncmp(key, "exec-once", 9) == 0) { - - char **new_exec_once = realloc( - config->exec_once, (config->exec_once_count + 1) * sizeof(char *)); - if (!new_exec_once) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for exec_once\n"); - return false; - } - config->exec_once = new_exec_once; - - config->exec_once[config->exec_once_count] = strdup(value); - if (!config->exec_once[config->exec_once_count]) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to duplicate " - "exec_once string\n"); - return false; - } - - config->exec_once_count++; - - } else if (regex_match("^bind[s|l|r|p]*$", key)) { - config->key_bindings = - realloc(config->key_bindings, - (config->key_bindings_count + 1) * sizeof(KeyBinding)); - if (!config->key_bindings) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for key bindings\n"); - return false; - } - - KeyBinding *binding = &config->key_bindings[config->key_bindings_count]; - memset(binding, 0, sizeof(KeyBinding)); - - char mod_str[256], keysym_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[^\n]", - mod_str, keysym_str, func_name, arg_value, arg_value2, - arg_value3, arg_value4, arg_value5) < 3) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid bind format: " - "\033[1m\033[31m%s\n", - value); - return false; - } - trim_whitespace(mod_str); - trim_whitespace(keysym_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->keymode_idx = config->current_keymode_idx; - - parse_bind_flags(key, binding); - binding->keysymcode = - parse_key(keysym_str, binding->keysymcode.type == KEY_TYPE_SYM); - binding->mod = parse_mod(mod_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 || binding->mod == UINT32_MAX || - (binding->keysymcode.type == KEY_TYPE_SYM && - binding->keysymcode.keysym == XKB_KEY_NoSymbol)) { - 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; - } - if (!binding->func) - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown " - "dispatch in bind: " - "\033[1m\033[31m%s\n", - func_name); - return false; - } else { - config->key_bindings_count++; - } - - } else if (strncmp(key, "mousebind", 9) == 0) { - config->mouse_bindings = - realloc(config->mouse_bindings, - (config->mouse_bindings_count + 1) * sizeof(MouseBinding)); - if (!config->mouse_bindings) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for mouse bindings\n"); - return false; - } - - MouseBinding *binding = - &config->mouse_bindings[config->mouse_bindings_count]; - memset(binding, 0, sizeof(MouseBinding)); - - char mod_str[256], button_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[^\n]", - mod_str, button_str, func_name, arg_value, arg_value2, - arg_value3, arg_value4, arg_value5) < 3) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid mousebind " - "format: " - "%s\n", - value); - return false; - } - trim_whitespace(mod_str); - trim_whitespace(button_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->mod = parse_mod(mod_str); - binding->button = parse_button(button_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 || binding->mod == UINT32_MAX || - binding->button == UINT32_MAX) { - 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; - } - if (!binding->func) - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown " - "dispatch in " - "mousebind: \033[1m\033[31m%s\n", - func_name); - return false; - } else { - config->mouse_bindings_count++; - } - } else if (strncmp(key, "axisbind", 8) == 0) { - config->axis_bindings = - realloc(config->axis_bindings, - (config->axis_bindings_count + 1) * sizeof(AxisBinding)); - if (!config->axis_bindings) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for axis bindings\n"); - return false; - } - - AxisBinding *binding = - &config->axis_bindings[config->axis_bindings_count]; - memset(binding, 0, sizeof(AxisBinding)); - - char mod_str[256], dir_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[^\n]", - mod_str, dir_str, func_name, arg_value, arg_value2, - arg_value3, arg_value4, arg_value5) < 3) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid axisbind " - "format: %s\n", - value); - return false; - } - - trim_whitespace(mod_str); - trim_whitespace(dir_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->mod = parse_mod(mod_str); - binding->dir = parse_direction(dir_str); - 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 || binding->mod == UINT32_MAX) { - 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; - } - if (!binding->func) - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown " - "dispatch in " - "axisbind: \033[1m\033[31m%s\n", - func_name); - return false; - } else { - config->axis_bindings_count++; - } - - } else if (strncmp(key, "switchbind", 10) == 0) { - config->switch_bindings = realloc(config->switch_bindings, - (config->switch_bindings_count + 1) * - sizeof(SwitchBinding)); - if (!config->switch_bindings) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for switch bindings\n"); - return false; - } - - SwitchBinding *binding = - &config->switch_bindings[config->switch_bindings_count]; - memset(binding, 0, sizeof(SwitchBinding)); - - char fold_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[" - "^\n]", - fold_str, func_name, arg_value, arg_value2, arg_value3, - arg_value4, arg_value5) < 3) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid switchbind " - "format: %s\n", - value); - return false; - } - trim_whitespace(fold_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->fold = parse_fold_state(fold_str); - 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 " - "switchbind: " - "\033[1m\033[31m%s\n", - func_name); - return false; - } else { - config->switch_bindings_count++; - } - - } else if (strncmp(key, "gesturebind", 11) == 0) { - config->gesture_bindings = realloc( - config->gesture_bindings, - (config->gesture_bindings_count + 1) * sizeof(GestureBinding)); - if (!config->gesture_bindings) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Failed to allocate " - "memory for axis gesturebind\n"); - return false; - } - - 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] = "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[^\n]", - mod_str, motion_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 gesturebind " - "format: %s\n", - value); - return false; - } - - trim_whitespace(mod_str); - trim_whitespace(motion_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->mod = parse_mod(mod_str); - binding->motion = parse_direction(motion_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 || binding->mod == UINT32_MAX) { - 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; - } - if (!binding->func) - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown " - "dispatch in " - "axisbind: \033[1m\033[31m%s\n", - func_name); - return false; - } else { - config->gesture_bindings_count++; - } - - } else if (strncmp(key, "source", 6) == 0) { - parse_config_file(config, value); - } else { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Unknown keyword: " - "\033[1m\033[31m%s\n", - key); - return false; - } - - return true; -} - -bool parse_config_line(Config *config, const char *line) { - char key[256], value[256]; - if (sscanf(line, "%255[^=]=%255[^\n]", key, value) != 2) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid line format: %s", - line); - return false; - } - - // Then trim each part separately - trim_whitespace(key); - trim_whitespace(value); - - return parse_option(config, key, value); -} - -void parse_config_file(Config *config, const char *file_path) { - FILE *file; - char full_path[1024]; - - if (file_path[0] == '.' && file_path[1] == '/') { - // Relative path - - if (server.cli_config_path) { - char *config_path = strdup(server.cli_config_path); - char *config_dir = dirname(config_path); - snprintf(full_path, sizeof(full_path), "%s/%s", config_dir, - file_path + 1); - free(config_path); - } else { - const char *home = getenv("HOME"); - if (!home) { - fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m HOME environment " - "variable not set.\n"); - return; - } - snprintf(full_path, sizeof(full_path), "%s/.config/mango/%s", home, - file_path + 1); - } - file = fopen(full_path, "r"); - - } else if (file_path[0] == '~' && - (file_path[1] == '/' || file_path[1] == '\0')) { - // Home directory - - const char *home = getenv("HOME"); - if (!home) { - fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m HOME environment " - "variable not set.\n"); - return; - } - snprintf(full_path, sizeof(full_path), "%s%s", home, file_path + 1); - file = fopen(full_path, "r"); - - } else { - // Absolute path - file = fopen(file_path, "r"); - } - - if (!file) { - fprintf(stderr, - "\033[1;31m\033[1;33m[ERROR]:\033[0m Failed to open " - "config file: %s\n", - file_path); - return; - } - - char line[512]; - bool parse_correct = true; - uint32_t line_count = 0; - while (fgets(line, sizeof(line), file)) { - line_count++; - if (line[0] == '#' || line[0] == '\n') { - continue; - } - parse_correct = parse_config_line(config, line); - if (!parse_correct) { - fprintf(stderr, - "\033[1;31m╰─\033[1;33m[Index]\033[0m " - "\033[1;36m%s\033[0m:\033[1;35m%d\033[0m\n" - " \033[1;36m╰─\033[0;33m%s\033[0m\n\n", - file_path, line_count, line); - } - } - - fclose(file); -} - -void free_circle_layout(Config *config) { - if (config->circle_layout) { - // 释放每个字符串 - for (int32_t i = 0; i < config->circle_layout_count; i++) { - if (config->circle_layout[i]) { - free(config->circle_layout[i]); // 释放单个字符串 - config->circle_layout[i] = NULL; // 防止野指针 - } - } - // 释放 circle_layout 数组本身 - free(config->circle_layout); - config->circle_layout = NULL; // 防止野指针 - } - config->circle_layout_count = 0; // 重置计数 -} - -void free_baked_points(void) { - if (server.baked_points_move) { - free(server.baked_points_move); - server.baked_points_move = NULL; - } - if (server.baked_points_open) { - free(server.baked_points_open); - server.baked_points_open = NULL; - } - if (server.baked_points_close) { - free(server.baked_points_close); - server.baked_points_close = NULL; - } - if (server.baked_points_tag) { - free(server.baked_points_tag); - server.baked_points_tag = NULL; - } - if (server.baked_points_focus) { - free(server.baked_points_focus); - server.baked_points_focus = NULL; - } - if (server.baked_points_opafadein) { - free(server.baked_points_opafadein); - server.baked_points_opafadein = NULL; - } - if (server.baked_points_opafadeout) { - free(server.baked_points_opafadeout); - server.baked_points_opafadeout = NULL; - } -} - -void free_config(void) { - // 释放内存 - int32_t i; - - // 释放 window_rules - if (config.window_rules) { - for (int32_t i = 0; i < config.window_rules_count; i++) { - ConfigWinRule *rule = &config.window_rules[i]; - if (rule->id) - free((void *)rule->id); - if (rule->title) - free((void *)rule->title); - if (rule->animation_type_open) - free((void *)rule->animation_type_open); - if (rule->animation_type_close) - free((void *)rule->animation_type_close); - if (rule->monitor) - free((void *)rule->monitor); - rule->id = NULL; - rule->title = NULL; - rule->animation_type_open = NULL; - rule->animation_type_close = NULL; - rule->monitor = NULL; - // 释放 globalkeybinding 的 arg.v(如果动态分配) - if (rule->globalkeybinding.arg.v) { - free((void *)rule->globalkeybinding.arg.v); - } - } - free(config.window_rules); - config.window_rules = NULL; - config.window_rules_count = 0; - } - - // 释放 key_bindings - if (config.key_bindings) { - for (i = 0; i < config.key_bindings_count; i++) { - if (config.key_bindings[i].arg.v) { - free((void *)config.key_bindings[i].arg.v); - config.key_bindings[i].arg.v = NULL; - } - if (config.key_bindings[i].arg.v2) { - free((void *)config.key_bindings[i].arg.v2); - config.key_bindings[i].arg.v2 = NULL; - } - if (config.key_bindings[i].arg.v3) { - free((void *)config.key_bindings[i].arg.v3); - config.key_bindings[i].arg.v3 = NULL; - } - } - free(config.key_bindings); - config.key_bindings = NULL; - config.key_bindings_count = 0; - } - - // 释放 mouse_bindings - if (config.mouse_bindings) { - for (i = 0; i < config.mouse_bindings_count; i++) { - if (config.mouse_bindings[i].arg.v) { - free((void *)config.mouse_bindings[i].arg.v); - config.mouse_bindings[i].arg.v = NULL; - } - if (config.mouse_bindings[i].arg.v2) { - free((void *)config.mouse_bindings[i].arg.v2); - config.mouse_bindings[i].arg.v2 = NULL; - } - if (config.mouse_bindings[i].arg.v3) { - free((void *)config.mouse_bindings[i].arg.v3); - config.mouse_bindings[i].arg.v3 = NULL; - } - } - free(config.mouse_bindings); - config.mouse_bindings = NULL; - config.mouse_bindings_count = 0; - } - - // 释放 axis_bindings - if (config.axis_bindings) { - for (i = 0; i < config.axis_bindings_count; i++) { - if (config.axis_bindings[i].arg.v) { - free((void *)config.axis_bindings[i].arg.v); - config.axis_bindings[i].arg.v = NULL; - } - if (config.axis_bindings[i].arg.v2) { - free((void *)config.axis_bindings[i].arg.v2); - config.axis_bindings[i].arg.v2 = NULL; - } - if (config.axis_bindings[i].arg.v3) { - free((void *)config.axis_bindings[i].arg.v3); - config.axis_bindings[i].arg.v3 = NULL; - } - } - free(config.axis_bindings); - config.axis_bindings = NULL; - config.axis_bindings_count = 0; - } - - // 释放 switch_bindings - if (config.switch_bindings) { - for (i = 0; i < config.switch_bindings_count; i++) { - if (config.switch_bindings[i].arg.v) { - free((void *)config.switch_bindings[i].arg.v); - config.switch_bindings[i].arg.v = NULL; - } - if (config.switch_bindings[i].arg.v2) { - free((void *)config.switch_bindings[i].arg.v2); - config.switch_bindings[i].arg.v2 = NULL; - } - if (config.switch_bindings[i].arg.v3) { - free((void *)config.switch_bindings[i].arg.v3); - config.switch_bindings[i].arg.v3 = NULL; - } - } - free(config.switch_bindings); - config.switch_bindings = NULL; - config.switch_bindings_count = 0; - } - - // 释放 gesture_bindings - if (config.gesture_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; - } - if (config.gesture_bindings[i].arg.v2) { - free((void *)config.gesture_bindings[i].arg.v2); - config.gesture_bindings[i].arg.v2 = NULL; - } - if (config.gesture_bindings[i].arg.v3) { - free((void *)config.gesture_bindings[i].arg.v3); - config.gesture_bindings[i].arg.v3 = NULL; - } - } - free(config.gesture_bindings); - config.gesture_bindings = NULL; - config.gesture_bindings_count = 0; - } - - // 释放 tag_rules - if (config.tag_rules) { - for (int32_t i = 0; i < config.tag_rules_count; i++) { - if (config.tag_rules[i].layout_name) - free((void *)config.tag_rules[i].layout_name); - if (config.tag_rules[i].monitor_name) - free((void *)config.tag_rules[i].monitor_name); - } - free(config.tag_rules); - config.tag_rules = NULL; - config.tag_rules_count = 0; - } - - // 释放 monitor_rules - if (config.monitor_rules) { - for (int32_t i = 0; i < config.monitor_rules_count; i++) { - if (config.monitor_rules[i].name) - free((void *)config.monitor_rules[i].name); - } - free(config.monitor_rules); - config.monitor_rules = NULL; - config.monitor_rules_count = 0; - } - - // 释放 layer_rules - if (config.layer_rules) { - for (int32_t i = 0; i < config.layer_rules_count; i++) { - if (config.layer_rules[i].layer_name) - free((void *)config.layer_rules[i].layer_name); - if (config.layer_rules[i].animation_type_open) - free((void *)config.layer_rules[i].animation_type_open); - if (config.layer_rules[i].animation_type_close) - free((void *)config.layer_rules[i].animation_type_close); - } - free(config.layer_rules); - config.layer_rules = NULL; - config.layer_rules_count = 0; - } - - // 释放 env - if (config.env) { - for (int32_t i = 0; i < config.env_count; i++) { - if (config.env[i]->type) { - free((void *)config.env[i]->type); - } - if (config.env[i]->value) { - free((void *)config.env[i]->value); - } - free(config.env[i]); - } - free(config.env); - config.env = NULL; - config.env_count = 0; - } - - // 释放 exec - if (config.exec) { - for (i = 0; i < config.exec_count; i++) { - free(config.exec[i]); - } - free(config.exec); - config.exec = NULL; - config.exec_count = 0; - } - - // 释放 exec_once - if (config.exec_once) { - for (i = 0; i < config.exec_once_count; i++) { - free(config.exec_once[i]); - } - free(config.exec_once); - config.exec_once = NULL; - config.exec_once_count = 0; - } - - // 释放 scroller_proportion_preset - if (config.scroller_proportion_preset) { - free(config.scroller_proportion_preset); - config.scroller_proportion_preset = NULL; - config.scroller_proportion_preset_count = 0; - } - - if (config.cursor_theme) { - free(config.cursor_theme); - config.cursor_theme = NULL; - } - - if (config.keymodes) { - for (int32_t i = 0; i < config.keymodes_count; i++) { - free(config.keymodes[i].name); - } - free(config.keymodes); - config.keymodes = NULL; - config.keymodes_count = 0; - config.current_keymode_idx = -1; - } - - // 释放 circle_layout - free_circle_layout(&config); - - // 释放动画资源 - free_baked_points(); - - // 清理解析按键用的keymap - cleanup_config_keymap(); -} - -void override_config(void) { - // 动画启用 - animations = CLAMP_INT(config.animations, 0, 1); - layer_animations = CLAMP_INT(config.layer_animations, 0, 1); - - // 标签动画方向 - tag_animation_direction = CLAMP_INT(config.tag_animation_direction, 0, 1); - - // 动画淡入淡出设置 - animation_fade_in = CLAMP_INT(config.animation_fade_in, 0, 1); - animation_fade_out = CLAMP_INT(config.animation_fade_out, 0, 1); - zoom_initial_ratio = CLAMP_FLOAT(config.zoom_initial_ratio, 0.1f, 1.0f); - zoom_end_ratio = CLAMP_FLOAT(config.zoom_end_ratio, 0.1f, 1.0f); - fadein_begin_opacity = CLAMP_FLOAT(config.fadein_begin_opacity, 0.0f, 1.0f); - fadeout_begin_opacity = - CLAMP_FLOAT(config.fadeout_begin_opacity, 0.0f, 1.0f); - - // 打开关闭动画类型 - animation_type_open = config.animation_type_open; - animation_type_close = config.animation_type_close; - - // layer打开关闭动画类型 - layer_animation_type_open = config.layer_animation_type_open; - layer_animation_type_close = config.layer_animation_type_close; - - // 动画时间限制在合理范围(1-50000ms) - animation_duration_move = - CLAMP_INT(config.animation_duration_move, 1, 50000); - animation_duration_open = - CLAMP_INT(config.animation_duration_open, 1, 50000); - animation_duration_tag = CLAMP_INT(config.animation_duration_tag, 1, 50000); - animation_duration_close = - CLAMP_INT(config.animation_duration_close, 1, 50000); - animation_duration_focus = - CLAMP_INT(config.animation_duration_focus, 1, 50000); - - // 滚动布局设置 - scroller_default_proportion = - CLAMP_FLOAT(config.scroller_default_proportion, 0.1f, 1.0f); - scroller_default_proportion_single = - CLAMP_FLOAT(config.scroller_default_proportion_single, 0.1f, 1.0f); - scroller_ignore_proportion_single = - CLAMP_INT(config.scroller_ignore_proportion_single, 0, 1); - scroller_focus_center = CLAMP_INT(config.scroller_focus_center, 0, 1); - scroller_prefer_center = CLAMP_INT(config.scroller_prefer_center, 0, 1); - edge_scroller_pointer_focus = - CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1); - scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000); - - // 主从布局设置 - default_mfact = CLAMP_FLOAT(config.default_mfact, 0.1f, 0.9f); - default_nmaster = CLAMP_INT(config.default_nmaster, 1, 1000); - center_master_overspread = CLAMP_INT(config.center_master_overspread, 0, 1); - center_when_single_stack = CLAMP_INT(config.center_when_single_stack, 0, 1); - new_is_master = CLAMP_INT(config.new_is_master, 0, 1); - - // 概述模式设置 - hotarea_size = CLAMP_INT(config.hotarea_size, 1, 1000); - hotarea_corner = CLAMP_INT(config.hotarea_corner, 0, 3); - enable_hotarea = CLAMP_INT(config.enable_hotarea, 0, 1); - ov_tab_mode = CLAMP_INT(config.ov_tab_mode, 0, 1); - overviewgappi = CLAMP_INT(config.overviewgappi, 0, 1000); - overviewgappo = CLAMP_INT(config.overviewgappo, 0, 1000); - - // 杂项设置 - xwayland_persistence = CLAMP_INT(config.xwayland_persistence, 0, 1); - syncobj_enable = CLAMP_INT(config.syncobj_enable, 0, 1); - allow_tearing = CLAMP_INT(config.allow_tearing, 0, 2); - allow_shortcuts_inhibit = CLAMP_INT(config.allow_shortcuts_inhibit, 0, 1); - allow_lock_transparent = CLAMP_INT(config.allow_lock_transparent, 0, 1); - axis_bind_apply_timeout = - CLAMP_INT(config.axis_bind_apply_timeout, 0, 1000); - focus_on_activate = CLAMP_INT(config.focus_on_activate, 0, 1); - idleinhibit_ignore_visible = - CLAMP_INT(config.idleinhibit_ignore_visible, 0, 1); - sloppyfocus = CLAMP_INT(config.sloppyfocus, 0, 1); - warpcursor = CLAMP_INT(config.warpcursor, 0, 1); - drag_corner = CLAMP_INT(config.drag_corner, 0, 4); - drag_warp_cursor = CLAMP_INT(config.drag_warp_cursor, 0, 1); - focus_cross_monitor = CLAMP_INT(config.focus_cross_monitor, 0, 1); - exchange_cross_monitor = CLAMP_INT(config.exchange_cross_monitor, 0, 1); - scratchpad_cross_monitor = CLAMP_INT(config.scratchpad_cross_monitor, 0, 1); - focus_cross_tag = CLAMP_INT(config.focus_cross_tag, 0, 1); - view_current_to_back = CLAMP_INT(config.view_current_to_back, 0, 1); - enable_floating_snap = CLAMP_INT(config.enable_floating_snap, 0, 1); - snap_distance = CLAMP_INT(config.snap_distance, 0, 99999); - cursor_size = CLAMP_INT(config.cursor_size, 4, 512); - no_border_when_single = CLAMP_INT(config.no_border_when_single, 0, 1); - no_radius_when_single = CLAMP_INT(config.no_radius_when_single, 0, 1); - cursor_hide_timeout = - CLAMP_INT(config.cursor_hide_timeout, 0, 36000); // 0-10小时 - drag_tile_to_tile = CLAMP_INT(config.drag_tile_to_tile, 0, 1); - single_scratchpad = CLAMP_INT(config.single_scratchpad, 0, 1); - - // 键盘设置 - repeat_rate = CLAMP_INT(config.repeat_rate, 1, 1000); - repeat_delay = CLAMP_INT(config.repeat_delay, 1, 20000); - numlockon = CLAMP_INT(config.numlockon, 0, 1); - - // 触控板设置 - disable_trackpad = CLAMP_INT(config.disable_trackpad, 0, 1); - tap_to_click = CLAMP_INT(config.tap_to_click, 0, 1); - tap_and_drag = CLAMP_INT(config.tap_and_drag, 0, 1); - drag_lock = CLAMP_INT(config.drag_lock, 0, 1); - trackpad_natural_scrolling = - CLAMP_INT(config.trackpad_natural_scrolling, 0, 1); - disable_while_typing = CLAMP_INT(config.disable_while_typing, 0, 1); - left_handed = CLAMP_INT(config.left_handed, 0, 1); - middle_button_emulation = CLAMP_INT(config.middle_button_emulation, 0, 1); - swipe_min_threshold = CLAMP_INT(config.swipe_min_threshold, 1, 1000); - - // 鼠标设置 - mouse_natural_scrolling = CLAMP_INT(config.mouse_natural_scrolling, 0, 1); - accel_profile = CLAMP_INT(config.accel_profile, 0, 2); - accel_speed = CLAMP_FLOAT(config.accel_speed, -1.0f, 1.0f); - scroll_method = CLAMP_INT(config.scroll_method, 0, 4); - scroll_button = CLAMP_INT(config.scroll_button, 272, 276); - click_method = CLAMP_INT(config.click_method, 0, 2); - send_events_mode = CLAMP_INT(config.send_events_mode, 0, 2); - button_map = CLAMP_INT(config.button_map, 0, 1); - axis_scroll_factor = CLAMP_FLOAT(config.axis_scroll_factor, 0.1f, 10.0f); - - // 外观设置 - gappih = CLAMP_INT(config.gappih, 0, 1000); - gappiv = CLAMP_INT(config.gappiv, 0, 1000); - gappoh = CLAMP_INT(config.gappoh, 0, 1000); - gappov = CLAMP_INT(config.gappov, 0, 1000); - scratchpad_width_ratio = - CLAMP_FLOAT(config.scratchpad_width_ratio, 0.1f, 1.0f); - scratchpad_height_ratio = - CLAMP_FLOAT(config.scratchpad_height_ratio, 0.1f, 1.0f); - borderpx = CLAMP_INT(config.borderpx, 0, 200); - smartgaps = CLAMP_INT(config.smartgaps, 0, 1); - - blur = CLAMP_INT(config.blur, 0, 1); - blur_layer = CLAMP_INT(config.blur_layer, 0, 1); - blur_optimized = CLAMP_INT(config.blur_optimized, 0, 1); - border_radius = CLAMP_INT(config.border_radius, 0, 100); - blur_params.num_passes = CLAMP_INT(config.blur_params.num_passes, 0, 10); - blur_params.radius = CLAMP_INT(config.blur_params.radius, 0, 100); - blur_params.noise = CLAMP_FLOAT(config.blur_params.noise, 0, 1); - blur_params.brightness = CLAMP_FLOAT(config.blur_params.brightness, 0, 1); - blur_params.contrast = CLAMP_FLOAT(config.blur_params.contrast, 0, 1); - blur_params.saturation = CLAMP_FLOAT(config.blur_params.saturation, 0, 1); - shadows = CLAMP_INT(config.shadows, 0, 1); - shadow_only_floating = CLAMP_INT(config.shadow_only_floating, 0, 1); - layer_shadows = CLAMP_INT(config.layer_shadows, 0, 1); - shadows_size = CLAMP_INT(config.shadows_size, 0, 100); - shadows_blur = CLAMP_INT(config.shadows_blur, 0, 100); - shadows_position_x = CLAMP_INT(config.shadows_position_x, -1000, 1000); - shadows_position_y = CLAMP_INT(config.shadows_position_y, -1000, 1000); - focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f); - unfocused_opacity = CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f); - memcpy(shadowscolor, config.shadowscolor, sizeof(shadowscolor)); - - // 复制颜色数组 - memcpy(rootcolor, config.rootcolor, sizeof(rootcolor)); - memcpy(bordercolor, config.bordercolor, sizeof(bordercolor)); - memcpy(focuscolor, config.focuscolor, sizeof(focuscolor)); - memcpy(maximizescreencolor, config.maximizescreencolor, - sizeof(maximizescreencolor)); - memcpy(urgentcolor, config.urgentcolor, sizeof(urgentcolor)); - memcpy(scratchpadcolor, config.scratchpadcolor, sizeof(scratchpadcolor)); - memcpy(globalcolor, config.globalcolor, sizeof(globalcolor)); - memcpy(overlaycolor, config.overlaycolor, sizeof(overlaycolor)); - - // 复制动画曲线 - memcpy(animation_curve_move, config.animation_curve_move, - sizeof(animation_curve_move)); - memcpy(animation_curve_open, config.animation_curve_open, - sizeof(animation_curve_open)); - memcpy(animation_curve_tag, config.animation_curve_tag, - sizeof(animation_curve_tag)); - memcpy(animation_curve_close, config.animation_curve_close, - sizeof(animation_curve_close)); - memcpy(animation_curve_focus, config.animation_curve_focus, - sizeof(animation_curve_focus)); - memcpy(animation_curve_opafadein, config.animation_curve_opafadein, - sizeof(animation_curve_opafadein)); - memcpy(animation_curve_opafadeout, config.animation_curve_opafadeout, - sizeof(animation_curve_opafadeout)); -} - -void set_value_default() { - /* animaion */ - config.animations = animations; // 是否启用动画 - config.layer_animations = layer_animations; // 是否启用layer动画 - config.animation_fade_in = animation_fade_in; // Enable animation fade in - config.animation_fade_out = animation_fade_out; // Enable animation fade out - config.tag_animation_direction = tag_animation_direction; // 标签动画方向 - config.zoom_initial_ratio = zoom_initial_ratio; // 动画起始窗口比例 - config.zoom_end_ratio = zoom_end_ratio; // 动画结束窗口比例 - config.fadein_begin_opacity = - fadein_begin_opacity; // Begin opac window ratio for animations - config.fadeout_begin_opacity = fadeout_begin_opacity; - config.animation_duration_move = - animation_duration_move; // Animation move speed - config.animation_duration_open = - animation_duration_open; // Animation open speed - config.animation_duration_tag = - animation_duration_tag; // Animation tag speed - config.animation_duration_close = - animation_duration_close; // Animation tag speed - config.animation_duration_focus = - animation_duration_focus; // Animation focus opacity speed - - /* appearance */ - config.axis_bind_apply_timeout = - axis_bind_apply_timeout; // 滚轮绑定动作的触发的时间间隔 - config.focus_on_activate = - focus_on_activate; // 收到窗口激活请求是否自动跳转聚焦 - config.new_is_master = new_is_master; // 新窗口是否插在头部 - config.default_mfact = default_mfact; // master 窗口比例 - config.default_nmaster = default_nmaster; // 默认master数量 - config.center_master_overspread = - center_master_overspread; // 中心master时是否铺满 - config.center_when_single_stack = - center_when_single_stack; // 单个stack时是否居中 - - config.numlockon = numlockon; // 是否打开右边小键盘 - - config.ov_tab_mode = ov_tab_mode; // alt tab切换模式 - config.hotarea_size = hotarea_size; // 热区大小,10x10 - config.hotarea_corner = hotarea_corner; - config.enable_hotarea = enable_hotarea; // 是否启用鼠标热区 - config.smartgaps = smartgaps; /* 1 means no outer gap when there is - only one window */ - config.sloppyfocus = sloppyfocus; /* focus follows mouse */ - config.gappih = gappih; /* horiz inner gap between windows */ - config.gappiv = gappiv; /* vert inner gap between windows */ - config.gappoh = - gappoh; /* horiz outer gap between windows and screen edge */ - config.gappov = gappov; /* vert outer gap between windows and screen edge */ - config.scratchpad_width_ratio = scratchpad_width_ratio; - config.scratchpad_height_ratio = scratchpad_height_ratio; - - config.scroller_structs = scroller_structs; - config.scroller_default_proportion = scroller_default_proportion; - config.scroller_default_proportion_single = - scroller_default_proportion_single; - config.scroller_ignore_proportion_single = - scroller_ignore_proportion_single; - config.scroller_focus_center = scroller_focus_center; - config.scroller_prefer_center = scroller_prefer_center; - config.edge_scroller_pointer_focus = edge_scroller_pointer_focus; - config.focus_cross_monitor = focus_cross_monitor; - config.exchange_cross_monitor = exchange_cross_monitor; - config.scratchpad_cross_monitor = scratchpad_cross_monitor; - config.focus_cross_tag = focus_cross_tag; - config.axis_scroll_factor = axis_scroll_factor; - config.view_current_to_back = view_current_to_back; - config.single_scratchpad = single_scratchpad; - config.xwayland_persistence = xwayland_persistence; - config.syncobj_enable = syncobj_enable; - config.allow_tearing = allow_tearing; - config.allow_shortcuts_inhibit = allow_shortcuts_inhibit; - config.allow_lock_transparent = allow_lock_transparent; - config.no_border_when_single = no_border_when_single; - config.no_radius_when_single = no_radius_when_single; - config.snap_distance = snap_distance; - config.drag_tile_to_tile = drag_tile_to_tile; - config.enable_floating_snap = enable_floating_snap; - config.swipe_min_threshold = swipe_min_threshold; - - config.idleinhibit_ignore_visible = - idleinhibit_ignore_visible; /* 1 means idle inhibitors will - disable idle tracking even if it's - surface isn't visible - */ - - config.borderpx = borderpx; - config.overviewgappi = overviewgappi; /* overview时 窗口与边缘 缝隙大小 */ - config.overviewgappo = overviewgappo; /* overview时 窗口与窗口 缝隙大小 */ - config.cursor_hide_timeout = cursor_hide_timeout; - - config.warpcursor = warpcursor; /* Warp server.cursor to focused client */ - config.drag_corner = drag_corner; - config.drag_warp_cursor = drag_warp_cursor; - - config.repeat_rate = repeat_rate; - config.repeat_delay = repeat_delay; - - /* Trackpad */ - config.disable_trackpad = disable_trackpad; - config.tap_to_click = tap_to_click; - config.tap_and_drag = tap_and_drag; - config.drag_lock = drag_lock; - config.mouse_natural_scrolling = mouse_natural_scrolling; - config.cursor_size = cursor_size; - config.trackpad_natural_scrolling = trackpad_natural_scrolling; - config.disable_while_typing = disable_while_typing; - config.left_handed = left_handed; - config.middle_button_emulation = middle_button_emulation; - config.accel_profile = accel_profile; - config.accel_speed = accel_speed; - config.scroll_method = scroll_method; - config.scroll_button = scroll_button; - config.click_method = click_method; - config.send_events_mode = send_events_mode; - config.button_map = button_map; - - config.blur = blur; - config.blur_layer = blur_layer; - config.blur_optimized = blur_optimized; - config.border_radius = border_radius; - config.blur_params.num_passes = blur_params_num_passes; - config.blur_params.radius = blur_params_radius; - config.blur_params.noise = blur_params_noise; - config.blur_params.brightness = blur_params_brightness; - config.blur_params.contrast = blur_params_contrast; - config.blur_params.saturation = blur_params_saturation; - config.shadows = shadows; - config.shadow_only_floating = shadow_only_floating; - config.layer_shadows = layer_shadows; - config.shadows_size = shadows_size; - config.shadows_blur = shadows_blur; - config.shadows_position_x = shadows_position_x; - config.shadows_position_y = shadows_position_y; - config.focused_opacity = focused_opacity; - config.unfocused_opacity = unfocused_opacity; - memcpy(config.shadowscolor, shadowscolor, sizeof(shadowscolor)); - - memcpy(config.animation_curve_move, animation_curve_move, - sizeof(animation_curve_move)); - memcpy(config.animation_curve_open, animation_curve_open, - sizeof(animation_curve_open)); - memcpy(config.animation_curve_tag, animation_curve_tag, - sizeof(animation_curve_tag)); - memcpy(config.animation_curve_close, animation_curve_close, - sizeof(animation_curve_close)); - memcpy(config.animation_curve_focus, animation_curve_focus, - sizeof(animation_curve_focus)); - memcpy(config.animation_curve_opafadein, animation_curve_opafadein, - sizeof(animation_curve_opafadein)); - memcpy(config.animation_curve_opafadeout, animation_curve_opafadeout, - sizeof(animation_curve_opafadeout)); - - memcpy(config.rootcolor, rootcolor, sizeof(rootcolor)); - memcpy(config.bordercolor, bordercolor, sizeof(bordercolor)); - memcpy(config.focuscolor, focuscolor, sizeof(focuscolor)); - memcpy(config.maximizescreencolor, maximizescreencolor, - sizeof(maximizescreencolor)); - memcpy(config.urgentcolor, urgentcolor, sizeof(urgentcolor)); - memcpy(config.scratchpadcolor, scratchpadcolor, sizeof(scratchpadcolor)); - memcpy(config.globalcolor, globalcolor, sizeof(globalcolor)); - memcpy(config.overlaycolor, overlaycolor, sizeof(overlaycolor)); -} - -void set_default_key_bindings(Config *config) { - // 确保common模式存在 - int32_t common_idx = find_or_add_keymode(config, "common"); - - // 计算默认按键绑定的数量 - size_t default_key_bindings_count = - sizeof(default_key_bindings) / sizeof(KeyBinding); - - // 重新分配内存以容纳新的默认按键绑定 - config->key_bindings = - realloc(config->key_bindings, - (config->key_bindings_count + default_key_bindings_count) * - sizeof(KeyBinding)); - if (!config->key_bindings) { - return; - } - - // 将默认按键绑定复制到配置的按键绑定数组中 - for (size_t i = 0; i < default_key_bindings_count; i++) { - config->key_bindings[config->key_bindings_count + i] = - default_key_bindings[i]; - // 设置keymode为common模式 - config->key_bindings[config->key_bindings_count + i].keymode_idx = - common_idx; - // 设置其他标志 - config->key_bindings[config->key_bindings_count + i].islockapply = true; - } - - // 更新按键绑定的总数 - config->key_bindings_count += default_key_bindings_count; -} - -void parse_config(void) { - - char filename[1024]; - - free_config(); - - // 重置config结构体,确保所有指针初始化为NULL - memset(&config, 0, sizeof(config)); - memset(&xkb_rules_rules, 0, sizeof(xkb_rules_rules)); - memset(&xkb_rules_model, 0, sizeof(xkb_rules_model)); - memset(&xkb_rules_layout, 0, sizeof(xkb_rules_layout)); - memset(&xkb_rules_variant, 0, sizeof(xkb_rules_variant)); - memset(&xkb_rules_options, 0, sizeof(xkb_rules_options)); - - // 初始化动态数组的指针为NULL,避免野指针 - config.keymodes = NULL; - config.keymodes_count = 0; - config.current_keymode_idx = -1; - config.window_rules = NULL; - config.window_rules_count = 0; - config.monitor_rules = NULL; - config.monitor_rules_count = 0; - config.key_bindings = NULL; - config.key_bindings_count = 0; - config.mouse_bindings = NULL; - config.mouse_bindings_count = 0; - config.axis_bindings = NULL; - config.axis_bindings_count = 0; - config.switch_bindings = NULL; - config.switch_bindings_count = 0; - config.gesture_bindings = NULL; - config.gesture_bindings_count = 0; - config.env = NULL; - config.env_count = 0; - config.exec = NULL; - config.exec_count = 0; - config.exec_once = NULL; - config.exec_once_count = 0; - config.scroller_proportion_preset = NULL; - config.scroller_proportion_preset_count = 0; - config.circle_layout = NULL; - config.circle_layout_count = 0; - config.tag_rules = NULL; - config.tag_rules_count = 0; - config.cursor_theme = NULL; - - create_config_keymap(); - - if (server.cli_config_path) { - snprintf(filename, sizeof(filename), "%s", server.cli_config_path); - } else { - // 获取当前用户家目录 - const char *homedir = getenv("HOME"); - if (!homedir) { - // 如果获取失败,则无法继续 - return; - } - // 构建日志文件路径 - snprintf(filename, sizeof(filename), "%s/.config/mango/config.conf", - homedir); - - // 检查文件是否存在 - if (access(filename, F_OK) != 0) { - // 如果文件不存在,则使用 /etc/mango/config.conf - snprintf(filename, sizeof(filename), "%s/mango/config.conf", - SYSCONFDIR); - } - } - - set_value_default(); - parse_config_file(&config, filename); - set_current_keymode(&config, "default"); - set_default_key_bindings(&config); - override_config(); -} - -void reset_blur_params(void) { - if (blur) { - Monitor *m = NULL; - wl_list_for_each(m, &server.mons, link) { - if (m->blur != NULL) { - wlr_scene_node_destroy(&m->blur->node); - } - m->blur = - wlr_scene_optimized_blur_create(&server.scene->tree, 0, 0); - wlr_scene_node_reparent(&m->blur->node, server.layers[LyrBlur]); - wlr_scene_optimized_blur_set_size(m->blur, m->m.width, m->m.height); - wlr_scene_set_blur_data( - server.scene, blur_params.num_passes, blur_params.radius, - blur_params.noise, blur_params.brightness, blur_params.contrast, - blur_params.saturation); - } - } else { - Monitor *m = NULL; - wl_list_for_each(m, &server.mons, link) { - - if (m->blur) { - wlr_scene_node_destroy(&m->blur->node); - m->blur = NULL; - } - } - } -} - -void reapply_monitor_rules(void) { - ConfigMonitorRule *mr; - Monitor *m = NULL; - int32_t ji, vrr; - int32_t mx, my; - struct wlr_output_state state; - struct wlr_output_mode *internal_mode = NULL; - wlr_output_state_init(&state); - - wl_list_for_each(m, &server.mons, link) { - if (!m->wlr_output->enabled) { - continue; - } - - for (ji = 0; ji < config.monitor_rules_count; ji++) { - if (config.monitor_rules_count < 1) - break; - - mr = &config.monitor_rules[ji]; - if (regex_match(mr->name, m->wlr_output->name)) { - - mx = mr->x == INT32_MAX ? m->m.x : mr->x; - my = mr->y == INT32_MAX ? m->m.y : mr->y; - vrr = mr->vrr >= 0 ? mr->vrr : 0; - - if (mr->width > 0 && mr->height > 0 && mr->refresh > 0) { - internal_mode = get_nearest_output_mode( - m->wlr_output, mr->width, mr->height, mr->refresh); - if (internal_mode) { - wlr_output_state_set_mode(&state, internal_mode); - } else if (wlr_output_is_headless(m->wlr_output)) { - wlr_output_state_set_custom_mode( - &state, mr->width, mr->height, - (int32_t)roundf(mr->refresh * 1000)); - } - } - - if (vrr) { - enable_adaptive_sync(m, &state); - } else { - wlr_output_state_set_adaptive_sync_enabled(&state, false); - } - - wlr_output_state_set_scale(&state, mr->scale); - wlr_output_state_set_transform(&state, mr->rr); - wlr_output_layout_add(server.output_layout, m->wlr_output, mx, - my); - } - } - - wlr_output_commit_state(m->wlr_output, &state); - wlr_output_state_finish(&state); - updatemons(NULL, NULL); - } -} - -void reapply_cursor_style(void) { - if (server.hide_source) { - wl_event_source_timer_update(server.hide_source, 0); - wl_event_source_remove(server.hide_source); - server.hide_source = NULL; - } - - wlr_cursor_unset_image(server.cursor); - - wlr_cursor_set_surface(server.cursor, NULL, 0, 0); - - if (server.cursor_mgr) { - wlr_xcursor_manager_destroy(server.cursor_mgr); - server.cursor_mgr = NULL; - } - - server.cursor_mgr = - wlr_xcursor_manager_create(config.cursor_theme, cursor_size); - - Monitor *m = NULL; - wl_list_for_each(m, &server.mons, link) { - wlr_xcursor_manager_load(server.cursor_mgr, m->wlr_output->scale); - } - - wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, "left_ptr"); - - server.hide_source = wl_event_loop_add_timer( - wl_display_get_event_loop(server.dpy), hidecursor, server.cursor); - if (server.cursor_hidden) { - wlr_cursor_unset_image(server.cursor); - } else { - wl_event_source_timer_update(server.hide_source, - cursor_hide_timeout * 1000); - } -} - -void reapply_border(void) { - Client *c = NULL; - - // reset border width when config change - wl_list_for_each(c, &server.clients, link) { - if (c && !c->iskilling) { - if (!c->isnoborder && !c->isfullscreen) { - c->bw = borderpx; - } - } - } -} - -void reapply_keyboard(void) { - InputDevice *id; - wl_list_for_each(id, &server.inputdevices, link) { - if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { - continue; - } - wlr_keyboard_set_repeat_info((struct wlr_keyboard *)id->device_data, - repeat_rate, repeat_delay); - } -} - -void reapply_pointer(void) { - InputDevice *id; - struct libinput_device *device; - wl_list_for_each(id, &server.inputdevices, link) { - - if (id->wlr_device->type != WLR_INPUT_DEVICE_POINTER) { - continue; - } - - device = id->libinput_device; - if (wlr_input_device_is_libinput(id->wlr_device) && device) { - configure_pointer(device); - } - } -} - -void reapply_master(void) { - - int32_t i; - Monitor *m = NULL; - for (i = 0; i <= LENGTH(tags); i++) { - wl_list_for_each(m, &server.mons, link) { - if (!m->wlr_output->enabled) { - continue; - } - m->pertag->nmasters[i] = default_nmaster; - m->pertag->mfacts[i] = default_mfact; - m->gappih = gappih; - m->gappiv = gappiv; - m->gappoh = gappoh; - m->gappov = gappov; - } - } -} - -void parse_tagrule(Monitor *m) { - int32_t i, jk; - ConfigTagRule tr; - Client *c = NULL; - - for (i = 0; i <= LENGTH(tags); i++) { - m->pertag->nmasters[i] = default_nmaster; - m->pertag->mfacts[i] = default_mfact; - } - - for (i = 0; i < config.tag_rules_count; i++) { - - tr = config.tag_rules[i]; - - if (config.tag_rules_count > 0 && - (!tr.monitor_name || - regex_match(tr.monitor_name, m->wlr_output->name))) { - - for (jk = 0; jk < LENGTH(layouts); jk++) { - if (tr.layout_name && - strcmp(layouts[jk].name, tr.layout_name) == 0) { - m->pertag->ltidxs[tr.id] = &layouts[jk]; - } - } - - if (tr.no_hide >= 0) - m->pertag->no_hide[tr.id] = tr.no_hide; - if (tr.nmaster >= 1) - m->pertag->nmasters[tr.id] = tr.nmaster; - if (tr.mfact > 0.0f) - m->pertag->mfacts[tr.id] = tr.mfact; - if (tr.no_render_border >= 0) - m->pertag->no_render_border[tr.id] = tr.no_render_border; - } - } - - for (i = 1; i <= LENGTH(tags); i++) { - wl_list_for_each(c, &server.clients, link) { - if ((c->tags & (1 << (i - 1)) & TAGMASK) && ISTILED(c)) { - if (m->pertag->mfacts[i] > 0.0f) - c->master_mfact_per = m->pertag->mfacts[i]; - } - } - } -} - -void reapply_tagrule(void) { - Monitor *m = NULL; - wl_list_for_each(m, &server.mons, link) { - if (!m->wlr_output->enabled) { - continue; - } - parse_tagrule(m); - } -} - -void reset_option(void) { - init_baked_points(); - handlecursoractivity(); - reset_keyboard_layout(); - reset_blur_params(); - set_env(); - run_exec(); - - reapply_cursor_style(); - reapply_border(); - reapply_keyboard(); - reapply_pointer(); - reapply_master(); - - reapply_tagrule(); - reapply_monitor_rules(); - - arrange(server.selmon, false, false); -} - -int32_t reload_config(const Arg *arg) { - parse_config(); - reset_option(); - printstatus(); - return 1; -} +bool parse_config_line(Config *config, const char *line); +bool parse_option(Config *config, char *key, char *value); +void parse_config(void); + +/* ====================== KeyMode 管理函数 ====================== */ +int32_t find_or_add_keymode(Config *config, const char *name); +void set_current_keymode(Config *config, const char *name); + +/* ====================== 配置应用函数 ====================== */ +void set_env(void); +void run_exec(void); +void run_exec_once(void); +void set_default_key_bindings(Config *config); +void override_config(void); +void set_value_default(void); + +/* ====================== 配置重新应用函数 ====================== */ +void reset_option(void); +int32_t reload_config(const Arg *arg); +void reset_blur_params(void); +void reapply_monitor_rules(void); +void reapply_cursor_style(void); +void reapply_border(void); +void reapply_keyboard(void); +void reapply_pointer(void); +void reapply_master(void); +void reapply_tagrule(void); +void parse_tagrule(Monitor *m); + +/* ====================== 配置清理函数 ====================== */ +void free_config(void); +void free_circle_layout(Config *config); +void free_baked_points(void); +void cleanup_config_keymap(void); +void create_config_keymap(void); + +#endif // CONFIG_PARSER_H \ No newline at end of file diff --git a/src/config/preset.h b/src/config/preset.h index ae4424f..3716fe1 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -126,7 +126,7 @@ char xkb_rules_variant[256]; char xkb_rules_options[256]; /* keyboard */ -static const struct xkb_rule_names xkb_fallback_rules = { +const struct xkb_rule_names xkb_fallback_rules = { .layout = "us", .variant = NULL, .model = NULL, @@ -134,7 +134,7 @@ static const struct xkb_rule_names xkb_fallback_rules = { .options = NULL, }; -static const struct xkb_rule_names xkb_default_rules = { +const struct xkb_rule_names xkb_default_rules = { .options = NULL, }; @@ -206,7 +206,7 @@ enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; /* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ #define MODKEY WLR_MODIFIER_ALT -static const char *tags[] = { +const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", }; diff --git a/src/data/static_keymap.h b/src/data/static_keymap.h index 8a0c1f7..6d3e039 100644 --- a/src/data/static_keymap.h +++ b/src/data/static_keymap.h @@ -3,7 +3,7 @@ typedef struct { const char *abbr; // 全部使用小写 } LayoutMapping; -static const LayoutMapping layout_mappings[] = { +const LayoutMapping layout_mappings[] = { {"English (US)", "us"}, {"English (UK)", "gb"}, {"Russian", "ru"}, diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h deleted file mode 100644 index 22ef612..0000000 --- a/src/dispatch/bind_declare.h +++ /dev/null @@ -1,72 +0,0 @@ -int32_t minimized(const Arg *arg); -int32_t restore_minimized(const Arg *arg); -int32_t toggle_scratchpad(const Arg *arg); -int32_t focusdir(const Arg *arg); -int32_t toggleoverview(const Arg *arg); -int32_t set_proportion(const Arg *arg); -int32_t switch_proportion_preset(const Arg *arg); -int32_t zoom(const Arg *arg); -int32_t tagsilent(const Arg *arg); -int32_t tagtoleft(const Arg *arg); -int32_t tagtoright(const Arg *arg); -int32_t tagcrossmon(const Arg *arg); -int32_t viewtoleft(const Arg *arg); -int32_t viewtoright(const Arg *arg); -int32_t viewtoleft_have_client(const Arg *arg); -int32_t viewtoright_have_client(const Arg *arg); -int32_t viewcrossmon(const Arg *arg); -int32_t togglefloating(const Arg *arg); -int32_t togglefullscreen(const Arg *arg); -int32_t togglemaximizescreen(const Arg *arg); -int32_t togglegaps(const Arg *arg); -int32_t tagmon(const Arg *arg); -int32_t spawn(const Arg *arg); -int32_t spawn_shell(const Arg *arg); -int32_t spawn_on_empty(const Arg *arg); -int32_t setkeymode(const Arg *arg); -int32_t switch_keyboard_layout(const Arg *arg); -int32_t setlayout(const Arg *arg); -int32_t switch_layout(const Arg *arg); -int32_t setmfact(const Arg *arg); -int32_t quit(const Arg *arg); -int32_t moveresize(const Arg *arg); -int32_t exchange_client(const Arg *arg); -int32_t exchange_stack_client(const Arg *arg); -int32_t killclient(const Arg *arg); -int32_t toggleglobal(const Arg *arg); -int32_t incnmaster(const Arg *arg); -int32_t focusmon(const Arg *arg); -int32_t focusstack(const Arg *arg); -int32_t chvt(const Arg *arg); -int32_t reload_config(const Arg *arg); -int32_t smartmovewin(const Arg *arg); -int32_t smartresizewin(const Arg *arg); -int32_t centerwin(const Arg *arg); -int32_t bind_to_view(const Arg *arg); -int32_t toggletag(const Arg *arg); -int32_t toggleview(const Arg *arg); -int32_t tag(const Arg *arg); -int32_t comboview(const Arg *arg); -int32_t incgaps(const Arg *arg); -int32_t incigaps(const Arg *arg); -int32_t incihgaps(const Arg *arg); -int32_t incivgaps(const Arg *arg); -int32_t incogaps(const Arg *arg); -int32_t incohgaps(const Arg *arg); -int32_t incovgaps(const Arg *arg); -int32_t defaultgaps(const Arg *arg); -int32_t togglefakefullscreen(const Arg *arg); -int32_t toggleoverlay(const Arg *arg); -int32_t movewin(const Arg *arg); -int32_t resizewin(const Arg *arg); -int32_t toggle_named_scratchpad(const Arg *arg); -int32_t toggle_render_border(const Arg *arg); -int32_t create_virtual_output(const Arg *arg); -int32_t destroy_all_virtual_output(const Arg *arg); -int32_t focuslast(const Arg *arg); -int32_t toggle_trackpad_enable(const Arg *arg); -int32_t setoption(const Arg *arg); -int32_t disable_monitor(const Arg *arg); -int32_t enable_monitor(const Arg *arg); -int32_t toggle_monitor(const Arg *arg); -int32_t scroller_stack(const Arg *arg); \ No newline at end of file diff --git a/src/dispatch/bind_define.c b/src/dispatch/bind_define.c new file mode 100644 index 0000000..888caac --- /dev/null +++ b/src/dispatch/bind_define.c @@ -0,0 +1,1795 @@ +#include "../type.h" +#include "../layout/layout.h" +#include "../config/parse_config.h" +#include "bind_define.h" +#include "../client/client.h" + +extern Server server; +extern Config config; +extern Layout layouts[]; +extern const uint32_t layouts_len; + +int32_t bind_to_view(const Arg *arg) { + + uint32_t target = arg->ui; + + if (view_current_to_back && server.selmon->pertag->curtag && + (target & TAGMASK) == (server.selmon->tagset[server.selmon->seltags])) { + if (server.selmon->pertag->prevtag) + target = 1 << (server.selmon->pertag->prevtag - 1); + else + target = 0; + } + + if (!view_current_to_back && + (target & TAGMASK) == (server.selmon->tagset[server.selmon->seltags])) { + return 0; + } + + if ((int32_t)target == INT_MIN && server.selmon->pertag->curtag == 0) { + if (view_current_to_back && server.selmon->pertag->prevtag) + target = 1 << (server.selmon->pertag->prevtag - 1); + else + target = 0; + } + + if (target == 0 || (int32_t)target == INT_MIN) { + view(&(Arg){.ui = ~0 & TAGMASK, .i = arg->i}, false); + } else { + view(&(Arg){.ui = target, .i = arg->i}, true); + } + return 0; +} + +int32_t chvt(const Arg *arg) { + struct timespec ts; + + // prevent the animation to rquest the new frame + server.allow_frame_scheduling = false; + + // backup current tag and monitor name + if (server.selmon) { + server.chvt_backup_tag = server.selmon->pertag->curtag; + strncpy(server.chvt_backup_selmon, server.selmon->wlr_output->name, + sizeof(server.chvt_backup_selmon) - 1); + } + + wlr_session_change_vt(server.session, arg->ui); + + // wait for DRM device to stabilize and ensure the server.session state is + // inactive + ts.tv_sec = 0; + ts.tv_nsec = 100000000; // 200ms + nanosleep(&ts, NULL); + + // allow frame scheduling, + // because server.session state is now inactive, rendermon will not enter + server.allow_frame_scheduling = true; + return 1; +} + +int32_t create_virtual_output(const Arg *arg) { + + if (!wlr_backend_is_multi(server.backend)) { + wlr_log(WLR_ERROR, "Expected a multi server.backend"); + return 0; + } + + bool done = false; + wlr_multi_for_each_backend(server.backend, create_output, &done); + + if (!done) { + wlr_log(WLR_ERROR, "Failed to create virtual output"); + return 0; + } + + wlr_log(WLR_INFO, "Virtual output created"); + return 0; +} + +int32_t destroy_all_virtual_output(const Arg *arg) { + + if (!wlr_backend_is_multi(server.backend)) { + wlr_log(WLR_ERROR, "Expected a multi server.backend"); + return 0; + } + + Monitor *m, *tmp; + wl_list_for_each_safe(m, tmp, &server.mons, link) { + if (wlr_output_is_headless(m->wlr_output)) { + // if(server.selmon == m) + // server.selmon = NULL; + wlr_output_destroy(m->wlr_output); + wlr_log(WLR_INFO, "Virtual output destroyed"); + } + } + return 0; +} + +int32_t defaultgaps(const Arg *arg) { + setgaps(gappoh, gappov, gappih, gappiv); + return 0; +} + +int32_t exchange_client(const Arg *arg) { + Client *c = server.selmon->sel; + if (!c || c->isfloating) + return 0; + + if ((c->isfullscreen || c->ismaximizescreen) && !is_scroller_layout(c->mon)) + return 0; + + exchange_two_client(c, direction_select(arg)); + return 0; +} + +int32_t exchange_stack_client(const Arg *arg) { + Client *c = server.selmon->sel; + Client *tc = NULL; + if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen) + return 0; + if (arg->i == NEXT) { + tc = get_next_stack_client(c, false); + } else { + tc = get_next_stack_client(c, true); + } + if (tc) + exchange_two_client(c, tc); + return 0; +} + +int32_t focusdir(const Arg *arg) { + Client *c = NULL; + c = direction_select(arg); + c = get_focused_stack_client(c); + if (c) { + focusclient(c, 1); + if (warpcursor) + warp_cursor(c); + } else { + if (config.focus_cross_tag) { + if (arg->i == LEFT || arg->i == UP) + viewtoleft_have_client(&(Arg){0}); + if (arg->i == RIGHT || arg->i == DOWN) + viewtoright_have_client(&(Arg){0}); + } else if (config.focus_cross_monitor) { + focusmon(arg); + } + } + return 0; +} + +int32_t focuslast(const Arg *arg) { + + Client *c = NULL; + Client *tc = NULL; + bool begin = false; + uint32_t target = 0; + + wl_list_for_each(c, &server.fstack, flink) { + if (c->iskilling || c->isminimized || c->isunglobal || + !client_surface(c)->mapped || client_is_unmanaged(c) || + client_is_x11_popup(c)) + continue; + + if (server.selmon && !server.selmon->sel) { + tc = c; + break; + } + + if (server.selmon && c == server.selmon->sel && !begin) { + begin = true; + continue; + } + + if (begin) { + tc = c; + break; + } + } + + if (!tc || !client_surface(tc)->mapped) + return 0; + + if ((int32_t)tc->tags > 0) { + focusclient(tc, 1); + target = get_tags_first_tag(tc->tags); + view(&(Arg){.ui = target}, true); + } + return 0; +} + +int32_t toggle_trackpad_enable(const Arg *arg) { + disable_trackpad = !disable_trackpad; + return 0; +} + +int32_t focusmon(const Arg *arg) { + Client *c = NULL; + Monitor *m = NULL; + Monitor *tm = NULL; + + if (arg->i != UNDIR) { + tm = dirtomon(arg->i); + } else if (arg->v) { + wl_list_for_each(m, &server.mons, link) { + if (!m->wlr_output->enabled) { + continue; + } + if (regex_match(arg->v, m->wlr_output->name)) { + tm = m; + break; + } + } + } else { + return 0; + } + + if (!tm || !tm->wlr_output->enabled || tm == server.selmon) + return 0; + + server.selmon = tm; + if (warpcursor) { + warp_cursor_to_selmon(server.selmon); + } + c = focustop(server.selmon); + if (!c) { + server.selmon->sel = NULL; + wlr_seat_pointer_notify_clear_focus(server.seat); + wlr_seat_keyboard_notify_clear_focus(server.seat); + focusclient(NULL, 0); + } else + focusclient(c, 1); + + return 0; +} + +int32_t focusstack(const Arg *arg) { + /* Focus the next or previous client (in tiling order) on server.selmon */ + Client *sel = focustop(server.selmon); + Client *tc = NULL; + + if (!sel) + return 0; + if (arg->i == NEXT) { + tc = get_next_stack_client(sel, false); + } else { + tc = get_next_stack_client(sel, true); + } + /* If only one client is visible on server.selmon , then c == sel */ + + if (!tc) + return 0; + + focusclient(tc, 1); + if (warpcursor) + warp_cursor(tc); + return 0; +} + +int32_t incnmaster(const Arg *arg) { + if (!arg || !server.selmon) + return 0; + server.selmon->pertag->nmasters[server.selmon->pertag->curtag] = MAX( + server.selmon->pertag->nmasters[server.selmon->pertag->curtag] + arg->i, + 0); + arrange(server.selmon, false, false); + return 0; +} + +int32_t incgaps(const Arg *arg) { + setgaps(server.selmon->gappoh + arg->i, server.selmon->gappov + arg->i, + server.selmon->gappih + arg->i, server.selmon->gappiv + arg->i); + return 0; +} + +int32_t incigaps(const Arg *arg) { + setgaps(server.selmon->gappoh, server.selmon->gappov, + server.selmon->gappih + arg->i, server.selmon->gappiv + arg->i); + return 0; +} + +int32_t incogaps(const Arg *arg) { + setgaps(server.selmon->gappoh + arg->i, server.selmon->gappov + arg->i, + server.selmon->gappih, server.selmon->gappiv); + return 0; +} + +int32_t incihgaps(const Arg *arg) { + setgaps(server.selmon->gappoh, server.selmon->gappov, + server.selmon->gappih + arg->i, server.selmon->gappiv); + return 0; +} + +int32_t incivgaps(const Arg *arg) { + setgaps(server.selmon->gappoh, server.selmon->gappov, server.selmon->gappih, + server.selmon->gappiv + arg->i); + return 0; +} + +int32_t incohgaps(const Arg *arg) { + setgaps(server.selmon->gappoh + arg->i, server.selmon->gappov, + server.selmon->gappih, server.selmon->gappiv); + return 0; +} + +int32_t incovgaps(const Arg *arg) { + setgaps(server.selmon->gappoh, server.selmon->gappov + arg->i, + server.selmon->gappih, server.selmon->gappiv); + return 0; +} + +int32_t setmfact(const Arg *arg) { + float f; + Client *c = NULL; + + if (!arg || !server.selmon || + !server.selmon->pertag->ltidxs[server.selmon->pertag->curtag]->arrange) + return 0; + f = arg->f < 1.0 + ? arg->f + + server.selmon->pertag->mfacts[server.selmon->pertag->curtag] + : arg->f - 1.0; + if (f < 0.1 || f > 0.9) + return 0; + + server.selmon->pertag->mfacts[server.selmon->pertag->curtag] = f; + wl_list_for_each(c, &server.clients, link) { + if (VISIBLEON(c, server.selmon) && ISTILED(c)) { + c->master_mfact_per = f; + } + } + arrange(server.selmon, false, false); + return 0; +} + +int32_t killclient(const Arg *arg) { + Client *c = NULL; + c = server.selmon->sel; + if (c) { + pending_kill_client(c); + } + return 0; +} + +int32_t moveresize(const Arg *arg) { + const char *cursors[] = {"nw-resize", "ne-resize", "sw-resize", + "se-resize"}; + + if (server.cursor_mode != CurNormal && server.cursor_mode != CurPressed) + return 0; + xytonode(server.cursor->x, server.cursor->y, NULL, &server.grabc, NULL, + NULL, NULL); + if (!server.grabc || client_is_unmanaged(server.grabc) || + server.grabc->isfullscreen || server.grabc->ismaximizescreen) { + server.grabc = NULL; + return 0; + } + /* Float the window and tell motionnotify to grab it */ + if (server.grabc->isfloating == 0 && arg->ui == CurMove) { + server.grabc->drag_to_tile = true; + setfloating(server.grabc, 1); + } + + switch (server.cursor_mode = arg->ui) { + case CurMove: + + server.grabcx = server.cursor->x - server.grabc->geom.x; + server.grabcy = server.cursor->y - server.grabc->geom.y; + wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, "grab"); + break; + case CurResize: + /* Doesn't work for X11 output - the next absolute motion event + * returns the server.cursor to where it started */ + if (server.grabc->isfloating) { + server.rzcorner = drag_corner; + server.grabcx = (int)round(server.cursor->x); + server.grabcy = (int)round(server.cursor->y); + if (server.rzcorner == 4) + /* identify the closest corner index */ + server.rzcorner = + (server.grabcx - server.grabc->geom.x < + server.grabc->geom.x + server.grabc->geom.width - + server.grabcx + ? 0 + : 1) + + (server.grabcy - server.grabc->geom.y < + server.grabc->geom.y + server.grabc->geom.height - + server.grabcy + ? 0 + : 2); + + if (drag_warp_cursor) { + server.grabcx = + server.rzcorner & 1 + ? server.grabc->geom.x + server.grabc->geom.width + : server.grabc->geom.x; + server.grabcy = + server.rzcorner & 2 + ? server.grabc->geom.y + server.grabc->geom.height + : server.grabc->geom.y; + wlr_cursor_warp_closest(server.cursor, NULL, server.grabcx, + server.grabcy); + } + + wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, + cursors[server.rzcorner]); + } else { + wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, "grab"); + } + break; + } + return 0; +} + +int32_t movewin(const Arg *arg) { + Client *c = NULL; + c = server.selmon->sel; + if (!c || c->isfullscreen) + return 0; + if (!c->isfloating) + togglefloating(NULL); + + switch (arg->ui) { + case NUM_TYPE_MINUS: + c->geom.x -= arg->i; + break; + case NUM_TYPE_PLUS: + c->geom.x += arg->i; + break; + default: + c->geom.x = arg->i; + break; + } + + switch (arg->ui2) { + case NUM_TYPE_MINUS: + c->geom.y -= arg->i2; + break; + case NUM_TYPE_PLUS: + c->geom.y += arg->i2; + break; + default: + c->geom.y = arg->i2; + break; + } + + c->iscustomsize = 1; + c->float_geom = c->geom; + resize(c, c->geom, 0); + return 0; +} + +int32_t quit(const Arg *arg) { + wl_display_terminate(server.dpy); + return 0; +} + +int32_t resizewin(const Arg *arg) { + Client *c = NULL; + c = server.selmon->sel; + int32_t offsetx = 0, offsety = 0; + + if (!c || c->isfullscreen || c->ismaximizescreen) + return 0; + + int32_t animations_state_backup = animations; + if (!c->isfloating) + animations = 0; + + if (ISTILED(c)) { + switch (arg->ui) { + case NUM_TYPE_MINUS: + offsetx = -arg->i; + break; + case NUM_TYPE_PLUS: + offsetx = arg->i; + break; + default: + offsetx = arg->i; + break; + } + + switch (arg->ui2) { + case NUM_TYPE_MINUS: + offsety = -arg->i2; + break; + case NUM_TYPE_PLUS: + offsety = arg->i2; + break; + default: + offsety = arg->i2; + break; + } + resize_tile_client(c, false, offsetx, offsety, 0); + animations = animations_state_backup; + return 0; + } + + switch (arg->ui) { + case NUM_TYPE_MINUS: + c->geom.width -= arg->i; + break; + case NUM_TYPE_PLUS: + c->geom.width += arg->i; + break; + default: + c->geom.width = arg->i; + break; + } + + switch (arg->ui2) { + case NUM_TYPE_MINUS: + c->geom.height -= arg->i2; + break; + case NUM_TYPE_PLUS: + c->geom.height += arg->i2; + break; + default: + c->geom.height = arg->i2; + break; + } + + c->iscustomsize = 1; + c->float_geom = c->geom; + resize(c, c->geom, 0); + animations = animations_state_backup; + return 0; +} + +int32_t restore_minimized(const Arg *arg) { + Client *c = NULL; + + if (server.selmon && server.selmon->isoverview) + return 0; + + if (server.selmon && server.selmon->sel && + server.selmon->sel->is_in_scratchpad && + server.selmon->sel->is_scratchpad_show) { + server.selmon->sel->isminimized = 0; + server.selmon->sel->is_scratchpad_show = 0; + server.selmon->sel->is_in_scratchpad = 0; + server.selmon->sel->isnamedscratchpad = 0; + setborder_color(server.selmon->sel); + return 0; + } + + wl_list_for_each(c, &server.clients, link) { + if (c->isminimized && !c->isnamedscratchpad) { + c->is_scratchpad_show = 0; + c->is_in_scratchpad = 0; + c->isnamedscratchpad = 0; + show_hide_client(c); + setborder_color(c); + arrange(c->mon, false, false); + focusclient(c, 0); + warp_cursor(c); + return 0; + } + } + return 0; +} + +int32_t setlayout(const Arg *arg) { + int32_t jk; + + for (jk = 0; jk < layouts_len; jk++) { + if (strcmp(layouts[jk].name, arg->v) == 0) { + server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] = + &layouts[jk]; + clear_fullscreen_and_maximized_state(server.selmon); + arrange(server.selmon, false, false); + printstatus(); + return 0; + } + } + return 0; +} + +int32_t setkeymode(const Arg *arg) { + free(server.keymode.name); + server.keymode.name = strdup(arg->v); + if (strcmp(server.keymode.name, "default") == 0) { + server.keymode.is_default = true; + } else { + server.keymode.is_default = false; + } + if (strcmp(server.keymode.name, "common") == 0) { + server.keymode.is_common = true; + } else { + server.keymode.is_common = false; + } + printstatus(); + return 1; +} + +int32_t set_proportion(const Arg *arg) { + + if (server.selmon->isoverview || !is_scroller_layout(server.selmon)) + return 0; + + if (server.selmon->visible_tiling_clients == 1 && + !scroller_ignore_proportion_single) + return 0; + + Client *tc = server.selmon->sel; + + if (tc) { + tc = get_scroll_stack_head(tc); + uint32_t max_client_width = + server.selmon->w.width - 2 * scroller_structs - gappih; + tc->scroller_proportion = arg->f; + tc->geom.width = max_client_width * arg->f; + arrange(server.selmon, false, false); + } + return 0; +} + +int32_t smartmovewin(const Arg *arg) { + Client *c = NULL, *tc = NULL; + int32_t nx, ny; + int32_t buttom, top, left, right, tar; + c = server.selmon->sel; + if (!c || c->isfullscreen) + return 0; + if (!c->isfloating) + setfloating(server.selmon->sel, true); + nx = c->geom.x; + ny = c->geom.y; + + switch (arg->i) { + case UP: + tar = -99999; + top = c->geom.y; + ny -= c->mon->w.height / 4; + + wl_list_for_each(tc, &server.clients, link) { + if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) + continue; + if (c->geom.x + c->geom.width < tc->geom.x || + c->geom.x > tc->geom.x + tc->geom.width) + continue; + buttom = tc->geom.y + tc->geom.height + gappiv; + if (top > buttom && ny < buttom) { + tar = MAX(tar, buttom); + }; + } + + ny = tar == -99999 ? ny : tar; + ny = MAX(ny, c->mon->w.y + c->mon->gappov); + break; + case DOWN: + tar = 99999; + buttom = c->geom.y + c->geom.height; + ny += c->mon->w.height / 4; + + wl_list_for_each(tc, &server.clients, link) { + if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) + continue; + if (c->geom.x + c->geom.width < tc->geom.x || + c->geom.x > tc->geom.x + tc->geom.width) + continue; + top = tc->geom.y - gappiv; + if (buttom < top && (ny + c->geom.height) > top) { + tar = MIN(tar, top - c->geom.height); + }; + } + ny = tar == 99999 ? ny : tar; + ny = MIN(ny, c->mon->w.y + c->mon->w.height - c->geom.height - + c->mon->gappov); + break; + case LEFT: + tar = -99999; + left = c->geom.x; + nx -= c->mon->w.width / 6; + + wl_list_for_each(tc, &server.clients, link) { + if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) + continue; + if (c->geom.y + c->geom.height < tc->geom.y || + c->geom.y > tc->geom.y + tc->geom.height) + continue; + right = tc->geom.x + tc->geom.width + gappih; + if (left > right && nx < right) { + tar = MAX(tar, right); + }; + } + + nx = tar == -99999 ? nx : tar; + nx = MAX(nx, c->mon->w.x + c->mon->gappoh); + break; + case RIGHT: + tar = 99999; + right = c->geom.x + c->geom.width; + nx += c->mon->w.width / 6; + wl_list_for_each(tc, &server.clients, link) { + if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) + continue; + if (c->geom.y + c->geom.height < tc->geom.y || + c->geom.y > tc->geom.y + tc->geom.height) + continue; + left = tc->geom.x - gappih; + if (right < left && (nx + c->geom.width) > left) { + tar = MIN(tar, left - c->geom.width); + }; + } + nx = tar == 99999 ? nx : tar; + nx = MIN(nx, c->mon->w.x + c->mon->w.width - c->geom.width - + c->mon->gappoh); + break; + } + + c->float_geom = (struct wlr_box){ + .x = nx, .y = ny, .width = c->geom.width, .height = c->geom.height}; + c->iscustomsize = 1; + resize(c, c->float_geom, 1); + return 0; +} + +int32_t smartresizewin(const Arg *arg) { + Client *c = NULL, *tc = NULL; + int32_t nw, nh; + int32_t buttom, top, left, right, tar; + c = server.selmon->sel; + if (!c || c->isfullscreen) + return 0; + if (!c->isfloating) + setfloating(c, true); + nw = c->geom.width; + nh = c->geom.height; + + switch (arg->i) { + case UP: + nh -= server.selmon->w.height / 8; + nh = MAX(nh, server.selmon->w.height / 10); + break; + case DOWN: + tar = -99999; + buttom = c->geom.y + c->geom.height; + nh += server.selmon->w.height / 8; + + wl_list_for_each(tc, &server.clients, link) { + if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) + continue; + if (c->geom.x + c->geom.width < tc->geom.x || + c->geom.x > tc->geom.x + tc->geom.width) + continue; + top = tc->geom.y - gappiv; + if (buttom < top && (nh + c->geom.y) > top) { + tar = MAX(tar, top - c->geom.y); + }; + } + nh = tar == -99999 ? nh : tar; + if (c->geom.y + nh + gappov > + server.selmon->w.y + server.selmon->w.height) + nh = server.selmon->w.y + server.selmon->w.height - c->geom.y - + gappov; + break; + case LEFT: + nw -= server.selmon->w.width / 16; + nw = MAX(nw, server.selmon->w.width / 10); + break; + case RIGHT: + tar = 99999; + right = c->geom.x + c->geom.width; + nw += server.selmon->w.width / 16; + wl_list_for_each(tc, &server.clients, link) { + if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) + continue; + if (c->geom.y + c->geom.height < tc->geom.y || + c->geom.y > tc->geom.y + tc->geom.height) + continue; + left = tc->geom.x - gappih; + if (right < left && (nw + c->geom.x) > left) { + tar = MIN(tar, left - c->geom.x); + }; + } + + nw = tar == 99999 ? nw : tar; + if (c->geom.x + nw + gappoh > + server.selmon->w.x + server.selmon->w.width) + nw = server.selmon->w.x + server.selmon->w.width - c->geom.x - + gappoh; + break; + } + + c->float_geom = (struct wlr_box){ + .x = c->geom.x, .y = c->geom.y, .width = nw, .height = nh}; + c->iscustomsize = 1; + resize(c, c->float_geom, 1); + return 0; +} + +int32_t centerwin(const Arg *arg) { + Client *c = NULL; + c = server.selmon->sel; + + if (!c || c->isfullscreen || c->ismaximizescreen) + return 0; + + if (c->isfloating) { + c->float_geom = setclient_coordinate_center(c, c->mon, c->geom, 0, 0); + c->iscustomsize = 1; + resize(c, c->float_geom, 1); + return 0; + } + + if (!is_scroller_layout(server.selmon)) + return 0; + + Client *stack_head = get_scroll_stack_head(c); + if (server.selmon->pertag->ltidxs[server.selmon->pertag->curtag]->id == + SCROLLER) { + stack_head->geom.x = + server.selmon->w.x + + (server.selmon->w.width - stack_head->geom.width) / 2; + } else { + stack_head->geom.y = + server.selmon->w.y + + (server.selmon->w.height - stack_head->geom.height) / 2; + } + + arrange(server.selmon, false, false); + return 0; +} + +int32_t spawn_shell(const Arg *arg) { + if (!arg->v) + return 0; + + if (fork() == 0) { + // 1. 忽略可能导致 coredump 的信号 + signal(SIGSEGV, SIG_IGN); + signal(SIGABRT, SIG_IGN); + signal(SIGILL, SIG_IGN); + + dup2(STDERR_FILENO, STDOUT_FILENO); + setsid(); + + execlp("sh", "sh", "-c", arg->v, (char *)NULL); + + // fallback to bash + execlp("bash", "bash", "-c", arg->v, (char *)NULL); + + // if execlp fails, we should not reach here + wlr_log(WLR_ERROR, + "mango: failed to execute command '%s' with shell: %s\n", + arg->v, strerror(errno)); + _exit(EXIT_FAILURE); + } + return 0; +} + +int32_t spawn(const Arg *arg) { + + if (!arg->v) + return 0; + + if (fork() == 0) { + // 1. 忽略可能导致 coredump 的信号 + signal(SIGSEGV, SIG_IGN); + signal(SIGABRT, SIG_IGN); + signal(SIGILL, SIG_IGN); + + dup2(STDERR_FILENO, STDOUT_FILENO); + setsid(); + + // 2. 解析参数 + char *argv[64]; + int32_t argc = 0; + char *token = strtok((char *)arg->v, " "); + while (token != NULL && argc < 63) { + wordexp_t p; + if (wordexp(token, &p, 0) == 0) { + argv[argc++] = p.we_wordv[0]; + } else { + argv[argc++] = token; + } + token = strtok(NULL, " "); + } + argv[argc] = NULL; + + // 3. 执行命令 + execvp(argv[0], argv); + + // 4. execvp 失败时:打印错误并直接退出(避免 coredump) + wlr_log(WLR_ERROR, "mango: execvp '%s' failed: %s\n", argv[0], + strerror(errno)); + _exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作 + } + return 0; +} + +int32_t spawn_on_empty(const Arg *arg) { + bool is_empty = true; + Client *c = NULL; + + wl_list_for_each(c, &server.clients, link) { + if (arg->ui & c->tags && c->mon == server.selmon) { + is_empty = false; + break; + } + } + if (!is_empty) { + view(arg, true); + return 0; + } else { + view(arg, true); + spawn_shell(arg); + } + return 0; +} + +int32_t switch_keyboard_layout(const Arg *arg) { + if (!server.kb_group || !server.kb_group->wlr_group || !server.seat) { + wlr_log(WLR_ERROR, "Invalid keyboard group or server.seat "); + return 0; + } + + struct wlr_keyboard *keyboard = &server.kb_group->wlr_group->keyboard; + if (!keyboard || !keyboard->keymap) { + wlr_log(WLR_ERROR, "Invalid keyboard or keymap"); + return 0; + } + + // 1. 获取当前布局和计算下一个布局 + xkb_layout_index_t current = xkb_state_serialize_layout( + keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); + const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap); + if (num_layouts < 2) { + wlr_log(WLR_INFO, "Only one layout available"); + return 0; + } + xkb_layout_index_t next = (current + 1) % num_layouts; + + // 6. 应用新 keymap + uint32_t depressed = keyboard->modifiers.depressed; + uint32_t latched = keyboard->modifiers.latched; + // uint32_t locked = keyboard->modifiers.locked; + + wlr_keyboard_set_keymap(keyboard, keyboard->keymap); + wlr_keyboard_notify_modifiers(keyboard, depressed, latched, server.locked, + next); + keyboard->modifiers.group = 0; + + // 7. 更新 server.seat + wlr_seat_set_keyboard(server.seat, keyboard); + wlr_seat_keyboard_notify_modifiers(server.seat, &keyboard->modifiers); + + InputDevice *id; + wl_list_for_each(id, &server.inputdevices, link) { + if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { + continue; + } + + struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data; + + wlr_keyboard_set_keymap(tkb, keyboard->keymap); + wlr_keyboard_notify_modifiers(tkb, depressed, latched, server.locked, + next); + tkb->modifiers.group = 0; + + // 7. 更新 server.seat + wlr_seat_set_keyboard(server.seat, tkb); + wlr_seat_keyboard_notify_modifiers(server.seat, &tkb->modifiers); + } + + printstatus(); + return 0; +} + +int32_t switch_layout(const Arg *arg) { + + int32_t jk, ji; + char *target_layout_name = NULL; + uint32_t len; + + if (config.circle_layout_count != 0) { + for (jk = 0; jk < config.circle_layout_count; jk++) { + + len = MAX(strlen(config.circle_layout[jk]), + strlen(server.selmon->pertag + ->ltidxs[server.selmon->pertag->curtag] + ->name)); + + if (strncmp( + config.circle_layout[jk], + server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] + ->name, + len) == 0) { + target_layout_name = jk == config.circle_layout_count - 1 + ? config.circle_layout[0] + : config.circle_layout[jk + 1]; + break; + } + } + + if (!target_layout_name) { + target_layout_name = config.circle_layout[0]; + } + + for (ji = 0; ji < layouts_len; ji++) { + len = MAX(strlen(layouts[ji].name), strlen(target_layout_name)); + if (strncmp(layouts[ji].name, target_layout_name, len) == 0) { + server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] = + &layouts[ji]; + + break; + } + } + clear_fullscreen_and_maximized_state(server.selmon); + arrange(server.selmon, false, false); + printstatus(); + return 0; + } + + for (jk = 0; jk < layouts_len; jk++) { + if (strcmp(layouts[jk].name, + server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] + ->name) == 0) { + server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] = + jk == layouts_len- 1 ? &layouts[0] : &layouts[jk + 1]; + clear_fullscreen_and_maximized_state(server.selmon); + arrange(server.selmon, false, false); + printstatus(); + return 0; + } + } + return 0; +} + +int32_t switch_proportion_preset(const Arg *arg) { + float target_proportion = 0; + + if (config.scroller_proportion_preset_count == 0) { + return 0; + } + + if (server.selmon->isoverview || !is_scroller_layout(server.selmon)) + return 0; + + if (server.selmon->visible_tiling_clients == 1 && + !scroller_ignore_proportion_single) + return 0; + + Client *tc = server.selmon->sel; + + if (tc) { + tc = get_scroll_stack_head(tc); + for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) { + if (config.scroller_proportion_preset[i] == + tc->scroller_proportion) { + if (i == config.scroller_proportion_preset_count - 1) { + target_proportion = config.scroller_proportion_preset[0]; + break; + } else { + target_proportion = + config.scroller_proportion_preset[i + 1]; + break; + } + } + } + + if (target_proportion == 0) { + target_proportion = config.scroller_proportion_preset[0]; + } + + uint32_t max_client_width = + server.selmon->w.width - 2 * scroller_structs - gappih; + tc->scroller_proportion = target_proportion; + tc->geom.width = max_client_width * target_proportion; + arrange(server.selmon, false, false); + } + return 0; +} + +int32_t tag(const Arg *arg) { + Client *target_client = server.selmon->sel; + tag_client(arg, target_client); + return 0; +} + +int32_t tagmon(const Arg *arg) { + Monitor *m = NULL, *cm = NULL; + Client *c = focustop(server.selmon); + + if (!c) + return 0; + + if (arg->i != UNDIR) { + m = dirtomon(arg->i); + } else if (arg->v) { + wl_list_for_each(cm, &server.mons, link) { + if (!cm->wlr_output->enabled) { + continue; + } + if (regex_match(arg->v, cm->wlr_output->name)) { + m = cm; + break; + } + } + } else { + return 0; + } + + if (!m || !m->wlr_output->enabled) + return 0; + + uint32_t newtags = arg->ui ? arg->ui : arg->i2 ? c->tags : 0; + uint32_t target; + + if (c->mon == m) { + view(&(Arg){.ui = newtags}, true); + return 0; + } + + if (c == server.selmon->sel) { + server.selmon->sel = NULL; + } + + setmon(c, m, newtags, true); + client_update_oldmonname_record(c, m); + + reset_foreign_tolevel(c); + + c->float_geom.width = (int32_t)(c->float_geom.width * c->mon->w.width / + server.selmon->w.width); + c->float_geom.height = (int32_t)(c->float_geom.height * c->mon->w.height / + server.selmon->w.height); + server.selmon = c->mon; + c->float_geom = setclient_coordinate_center(c, c->mon, c->float_geom, 0, 0); + + // 重新计算居中的坐标 + // 重新计算居中的坐标 + if (c->isfloating) { + c->geom = c->float_geom; + target = get_tags_first_tag(c->tags); + view(&(Arg){.ui = target}, true); + focusclient(c, 1); + resize(c, c->geom, 1); + } else { + server.selmon = c->mon; + target = get_tags_first_tag(c->tags); + view(&(Arg){.ui = target}, true); + focusclient(c, 1); + arrange(server.selmon, false, false); + } + if (warpcursor) { + warp_cursor_to_selmon(c->mon); + } + return 0; +} + +int32_t tagsilent(const Arg *arg) { + Client *fc = NULL; + Client *target_client = NULL; + + if (!server.selmon || !server.selmon->sel) + return 0; + + target_client = server.selmon->sel; + target_client->tags = arg->ui & TAGMASK; + wl_list_for_each(fc, &server.clients, link) { + if (fc && fc != target_client && target_client->tags & fc->tags && + ISFULLSCREEN(fc) && !target_client->isfloating) { + clear_fullscreen_flag(fc); + } + } + exit_scroller_stack(target_client); + focusclient(focustop(server.selmon), 1); + arrange(target_client->mon, false, false); + return 0; +} + +int32_t tagtoleft(const Arg *arg) { + if (server.selmon->sel != NULL && + __builtin_popcount(server.selmon->tagset[server.selmon->seltags] & + TAGMASK) == 1 && + server.selmon->tagset[server.selmon->seltags] > 1) { + tag(&(Arg){.ui = server.selmon->tagset[server.selmon->seltags] >> 1, + .i = arg->i}); + } + return 0; +} + +int32_t tagtoright(const Arg *arg) { + if (server.selmon->sel != NULL && + __builtin_popcount(server.selmon->tagset[server.selmon->seltags] & + TAGMASK) == 1 && + server.selmon->tagset[server.selmon->seltags] & (TAGMASK >> 1)) { + tag(&(Arg){.ui = server.selmon->tagset[server.selmon->seltags] << 1, + .i = arg->i}); + } + return 0; +} + +int32_t toggle_named_scratchpad(const Arg *arg) { + Client *target_client = NULL; + char *arg_id = arg->v; + char *arg_title = arg->v2; + + target_client = get_client_by_id_or_title(arg_id, arg_title); + + if (!target_client && arg->v3) { + Arg arg_spawn = {.v = arg->v3}; + spawn_shell(&arg_spawn); + return 0; + } + + target_client->isnamedscratchpad = 1; + + apply_named_scratchpad(target_client); + return 0; +} + +int32_t toggle_render_border(const Arg *arg) { + server.render_border = !server.render_border; + arrange(server.selmon, false, false); + return 0; +} + +int32_t toggle_scratchpad(const Arg *arg) { + Client *c = NULL; + bool hit = false; + Client *tmp = NULL; + + if (server.selmon && server.selmon->isoverview) + return 0; + + wl_list_for_each_safe(c, tmp, &server.clients, link) { + if (!scratchpad_cross_monitor && c->mon != server.selmon) { + continue; + } + + if (single_scratchpad && c->isnamedscratchpad && !c->isminimized) { + set_minimized(c); + continue; + } + + if (c->isnamedscratchpad) + continue; + + if (hit) + continue; + + hit = switch_scratchpad_client_state(c); + } + return 0; +} + +int32_t togglefakefullscreen(const Arg *arg) { + Client *sel = focustop(server.selmon); + if (sel) + setfakefullscreen(sel, !sel->isfakefullscreen); + return 0; +} + +int32_t togglefloating(const Arg *arg) { + Client *sel = focustop(server.selmon); + + if (server.selmon && server.selmon->isoverview) + return 0; + + if (!sel) + return 0; + + if ((sel->isfullscreen || sel->ismaximizescreen)) { + sel->isfloating = 1; + } else { + sel->isfloating = !sel->isfloating; + } + + setfloating(sel, sel->isfloating); + return 0; +} + +int32_t togglefullscreen(const Arg *arg) { + Client *sel = focustop(server.selmon); + if (!sel) + return 0; + + sel->is_scratchpad_show = 0; + sel->is_in_scratchpad = 0; + sel->isnamedscratchpad = 0; + + if (sel->isfullscreen) + setfullscreen(sel, 0); + else + setfullscreen(sel, 1); + return 0; +} + +int32_t toggleglobal(const Arg *arg) { + if (!server.selmon->sel) + return 0; + if (server.selmon->sel->is_in_scratchpad) { + server.selmon->sel->is_in_scratchpad = 0; + server.selmon->sel->is_scratchpad_show = 0; + server.selmon->sel->isnamedscratchpad = 0; + } + server.selmon->sel->isglobal ^= 1; + if (server.selmon->sel->isglobal && (server.selmon->sel->prev_in_stack || + server.selmon->sel->next_in_stack)) { + exit_scroller_stack(server.selmon->sel); + arrange(server.selmon, false, false); + } + setborder_color(server.selmon->sel); + return 0; +} + +int32_t togglegaps(const Arg *arg) { + server.enablegaps ^= 1; + arrange(server.selmon, false, false); + return 0; +} + +int32_t togglemaximizescreen(const Arg *arg) { + Client *sel = focustop(server.selmon); + if (!sel) + return 0; + + sel->is_scratchpad_show = 0; + sel->is_in_scratchpad = 0; + sel->isnamedscratchpad = 0; + + if (sel->ismaximizescreen) + setmaximizescreen(sel, 0); + else + setmaximizescreen(sel, 1); + + setborder_color(sel); + return 0; +} + +int32_t toggleoverlay(const Arg *arg) { + if (!server.selmon->sel || !server.selmon->sel->mon || + server.selmon->sel->isfullscreen) { + return 0; + } + + server.selmon->sel->isoverlay ^= 1; + + if (server.selmon->sel->isoverlay) { + wlr_scene_node_reparent(&server.selmon->sel->scene->node, + server.layers[LyrOverlay]); + wlr_scene_node_raise_to_top(&server.selmon->sel->scene->node); + } else if (client_should_overtop(server.selmon->sel) && + server.selmon->sel->isfloating) { + wlr_scene_node_reparent(&server.selmon->sel->scene->node, + server.layers[LyrTop]); + } else { + wlr_scene_node_reparent( + &server.selmon->sel->scene->node, + server.layers[server.selmon->sel->isfloating ? LyrTop : LyrTile]); + } + setborder_color(server.selmon->sel); + return 0; +} + +int32_t toggletag(const Arg *arg) { + uint32_t newtags; + Client *sel = focustop(server.selmon); + if (!sel) + return 0; + + if ((int32_t)arg->ui == INT_MIN && sel->tags != (~0 & TAGMASK)) { + newtags = ~0 & TAGMASK; + } else if ((int32_t)arg->ui == INT_MIN && sel->tags == (~0 & TAGMASK)) { + newtags = 1 << (sel->mon->pertag->curtag - 1); + } else { + newtags = sel->tags ^ (arg->ui & TAGMASK); + } + + if (newtags) { + sel->tags = newtags; + focusclient(focustop(server.selmon), 1); + arrange(server.selmon, false, false); + } + printstatus(); + return 0; +} + +int32_t toggleview(const Arg *arg) { + uint32_t newtagset; + uint32_t target; + + target = arg->ui == 0 ? ~0 & TAGMASK : arg->ui; + + newtagset = server.selmon ? server.selmon->tagset[server.selmon->seltags] ^ + (target & TAGMASK) + : 0; + + if (newtagset) { + server.selmon->tagset[server.selmon->seltags] = newtagset; + focusclient(focustop(server.selmon), 1); + arrange(server.selmon, false, false); + } + printstatus(); + return 0; +} + +int32_t viewtoleft(const Arg *arg) { + uint32_t target = server.selmon->tagset[server.selmon->seltags]; + + if (server.selmon->isoverview || server.selmon->pertag->curtag == 0) { + return 0; + } + + target >>= 1; + + if (target == 0) { + return 0; + } + + if (!server.selmon || + (target) == server.selmon->tagset[server.selmon->seltags]) + return 0; + + view(&(Arg){.ui = target & TAGMASK, .i = arg->i}, true); + return 0; +} + +int32_t viewtoright(const Arg *arg) { + if (server.selmon->isoverview || server.selmon->pertag->curtag == 0) { + return 0; + } + uint32_t target = server.selmon->tagset[server.selmon->seltags]; + target <<= 1; + + if (!server.selmon || + (target) == server.selmon->tagset[server.selmon->seltags]) + return 0; + if (!(target & TAGMASK)) { + return 0; + } + + view(&(Arg){.ui = target & TAGMASK, .i = arg->i}, true); + return 0; +} + +int32_t viewtoleft_have_client(const Arg *arg) { + uint32_t n; + uint32_t current = + get_tags_first_tag_num(server.selmon->tagset[server.selmon->seltags]); + bool found = false; + + if (server.selmon->isoverview) { + return 0; + } + + if (current <= 1) + return 0; + + for (n = current - 1; n >= 1; n--) { + if (get_tag_status(n, server.selmon)) { + found = true; + break; + } + } + + if (found) + view(&(Arg){.ui = (1 << (n - 1)) & TAGMASK, .i = arg->i}, true); + return 0; +} + +int32_t viewtoright_have_client(const Arg *arg) { + uint32_t n; + uint32_t current = + get_tags_first_tag_num(server.selmon->tagset[server.selmon->seltags]); + bool found = false; + + if (server.selmon->isoverview) { + return 0; + } + + if (current >= LENGTH(tags)) + return 0; + + for (n = current + 1; n <= LENGTH(tags); n++) { + if (get_tag_status(n, server.selmon)) { + found = true; + break; + } + } + + if (found) + view(&(Arg){.ui = (1 << (n - 1)) & TAGMASK, .i = arg->i}, true); + return 0; +} + +int32_t viewcrossmon(const Arg *arg) { + focusmon(&(Arg){.v = arg->v, .i = UNDIR}); + view_in_mon(arg, true, server.selmon, true); + return 0; +} + +int32_t tagcrossmon(const Arg *arg) { + if (!server.selmon || !server.selmon->sel) + return 0; + + if (regex_match(server.selmon->wlr_output->name, arg->v)) { + tag_client(arg, server.selmon->sel); + return 0; + } + + tagmon(&(Arg){.ui = arg->ui, .i = UNDIR, .v = arg->v}); + return 0; +} + +int32_t comboview(const Arg *arg) { + uint32_t newtags = arg->ui & TAGMASK; + + if (!newtags || !server.selmon) + return 0; + + if (server.tag_combo) { + server.selmon->tagset[server.selmon->seltags] |= newtags; + focusclient(focustop(server.selmon), 1); + arrange(server.selmon, false, false); + } else { + server.tag_combo = true; + view(&(Arg){.ui = newtags}, false); + } + + printstatus(); + return 0; +} + +int32_t zoom(const Arg *arg) { + Client *c = NULL, *sel = focustop(server.selmon); + + if (!sel || !server.selmon || + !server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] + ->arrange || + sel->isfloating) + return 0; + + /* Search for the first tiled window that is not sel, marking sel as + * NULL if we pass it along the way */ + wl_list_for_each(c, &server.clients, + link) if (VISIBLEON(c, server.selmon) && !c->isfloating) { + if (c != sel) + break; + sel = NULL; + } + + /* Return if no other tiled window was found */ + if (&c->link == &server.clients) + return 0; + + /* If we passed sel, move c to the front; otherwise, move sel to the + * front */ + if (!sel) + sel = c; + wl_list_remove(&sel->link); + wl_list_insert(&server.clients, &sel->link); + + focusclient(sel, 1); + arrange(server.selmon, false, false); + return 0; +} + +int32_t setoption(const Arg *arg) { + parse_option(&config, arg->v, arg->v2); + override_config(); + reset_option(); + return 0; +} + +int32_t minimized(const Arg *arg) { + + if (server.selmon && server.selmon->isoverview) + return 0; + + if (server.selmon->sel && !server.selmon->sel->isminimized) { + set_minimized(server.selmon->sel); + } + return 0; +} + +int32_t toggleoverview(const Arg *arg) { + Client *c = NULL; + + if (server.selmon->isoverview && ov_tab_mode && arg->i != 1 && + server.selmon->sel) { + focusstack(&(Arg){.i = 1}); + return 0; + } + + server.selmon->isoverview ^= 1; + uint32_t target; + uint32_t visible_client_number = 0; + + if (server.selmon->isoverview) { + wl_list_for_each(c, &server.clients, + link) if (c && c->mon == server.selmon && + !client_is_unmanaged(c) && + !client_is_x11_popup(c) && !c->isminimized && + !c->isunglobal) { + visible_client_number++; + } + if (visible_client_number > 0) { + target = ~0 & TAGMASK; + } else { + server.selmon->isoverview ^= 1; + return 0; + } + } else if (!server.selmon->isoverview && server.selmon->sel) { + target = get_tags_first_tag(server.selmon->sel->tags); + } else if (!server.selmon->isoverview && !server.selmon->sel) { + target = (1 << (server.selmon->pertag->prevtag - 1)); + view(&(Arg){.ui = target}, false); + refresh_monitors_workspaces_status(server.selmon); + return 0; + } + + // 正常视图到overview,退出所有窗口的浮动和全屏状态参与平铺, + // overview到正常视图,还原之前退出的浮动和全屏窗口状态 + if (server.selmon->isoverview) { + wl_list_for_each(c, &server.clients, link) { + if (c && c->mon == server.selmon && !client_is_unmanaged(c) && + !client_is_x11_popup(c) && !c->isunglobal) + overview_backup(c); + } + } else { + wl_list_for_each(c, &server.clients, link) { + if (c && c->mon == server.selmon && !c->iskilling && + !client_is_unmanaged(c) && !c->isunglobal && + !client_is_x11_popup(c) && client_surface(c)->mapped) + overview_restore(c, &(Arg){.ui = target}); + } + } + + view(&(Arg){.ui = target}, false); + + refresh_monitors_workspaces_status(server.selmon); + return 0; +} + +int32_t disable_monitor(const Arg *arg) { + Monitor *m = NULL; + struct wlr_output_state state = {0}; + wl_list_for_each(m, &server.mons, link) { + if (regex_match(arg->v, m->wlr_output->name)) { + wlr_output_state_set_enabled(&state, false); + wlr_output_commit_state(m->wlr_output, &state); + m->asleep = 1; + updatemons(NULL, NULL); + break; + } + } + return 0; +} + +int32_t enable_monitor(const Arg *arg) { + Monitor *m = NULL; + struct wlr_output_state state = {0}; + wl_list_for_each(m, &server.mons, link) { + if (regex_match(arg->v, m->wlr_output->name)) { + wlr_output_state_set_enabled(&state, true); + wlr_output_commit_state(m->wlr_output, &state); + m->asleep = 0; + updatemons(NULL, NULL); + break; + } + } + return 0; +} + +int32_t toggle_monitor(const Arg *arg) { + Monitor *m = NULL; + struct wlr_output_state state = {0}; + wl_list_for_each(m, &server.mons, link) { + if (regex_match(arg->v, m->wlr_output->name)) { + wlr_output_state_set_enabled(&state, !m->wlr_output->enabled); + wlr_output_commit_state(m->wlr_output, &state); + m->asleep = !m->wlr_output->enabled; + updatemons(NULL, NULL); + break; + } + } + return 0; +} + +int32_t scroller_stack(const Arg *arg) { + Client *c = server.selmon->sel; + Client *stack_head = NULL; + Client *source_stack_head = NULL; + if (!c || !c->mon || c->isfloating || !is_scroller_layout(server.selmon)) + return 0; + + if (c && (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal)) + return 0; + + bool is_horizontal_layout = + c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true + : false; + + Client *target_client = find_client_by_direction(c, arg, false, true); + + if (target_client && (!client_only_in_one_tag(target_client) || + target_client->isglobal || target_client->isunglobal)) + return 0; + + if (target_client) { + stack_head = get_scroll_stack_head(target_client); + } + + if (c) { + source_stack_head = get_scroll_stack_head(c); + } + + if (stack_head == source_stack_head) { + return 0; + } + + if (c->isfullscreen) { + setfullscreen(c, 0); + } + + if (c->ismaximizescreen) { + setmaximizescreen(c, 0); + } + + if (c->prev_in_stack) { + if ((is_horizontal_layout && arg->i == LEFT) || + (!is_horizontal_layout && arg->i == UP)) { + exit_scroller_stack(c); + wl_list_remove(&c->link); + wl_list_insert(source_stack_head->link.prev, &c->link); + arrange(server.selmon, false, false); + + } else if ((is_horizontal_layout && arg->i == RIGHT) || + (!is_horizontal_layout && arg->i == DOWN)) { + exit_scroller_stack(c); + wl_list_remove(&c->link); + wl_list_insert(&source_stack_head->link, &c->link); + arrange(server.selmon, false, false); + } + return 0; + } else if (c->next_in_stack) { + Client *next_in_stack = c->next_in_stack; + if ((is_horizontal_layout && arg->i == LEFT) || + (!is_horizontal_layout && arg->i == UP)) { + exit_scroller_stack(c); + wl_list_remove(&c->link); + wl_list_insert(next_in_stack->link.prev, &c->link); + arrange(server.selmon, false, false); + } else if ((is_horizontal_layout && arg->i == RIGHT) || + (!is_horizontal_layout && arg->i == DOWN)) { + exit_scroller_stack(c); + wl_list_remove(&c->link); + wl_list_insert(&next_in_stack->link, &c->link); + arrange(server.selmon, false, false); + } + return 0; + } + + if (!target_client || target_client->mon != c->mon) { + return 0; + } + + exit_scroller_stack(c); + + // Find the tail of target_client's stack + Client *stack_tail = target_client; + while (stack_tail->next_in_stack) { + stack_tail = stack_tail->next_in_stack; + } + + // Add c to the stack + stack_tail->next_in_stack = c; + c->prev_in_stack = stack_tail; + c->next_in_stack = NULL; + + if (stack_head->ismaximizescreen) { + setmaximizescreen(stack_head, 0); + } + + if (stack_head->isfullscreen) { + setfullscreen(stack_head, 0); + } + + arrange(server.selmon, false, false); + return 0; +} \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 8818baa..22ef612 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1,1784 +1,72 @@ -int32_t bind_to_view(const Arg *arg) { - - uint32_t target = arg->ui; - - if (view_current_to_back && server.selmon->pertag->curtag && - (target & TAGMASK) == (server.selmon->tagset[server.selmon->seltags])) { - if (server.selmon->pertag->prevtag) - target = 1 << (server.selmon->pertag->prevtag - 1); - else - target = 0; - } - - if (!view_current_to_back && - (target & TAGMASK) == (server.selmon->tagset[server.selmon->seltags])) { - return 0; - } - - if ((int32_t)target == INT_MIN && server.selmon->pertag->curtag == 0) { - if (view_current_to_back && server.selmon->pertag->prevtag) - target = 1 << (server.selmon->pertag->prevtag - 1); - else - target = 0; - } - - if (target == 0 || (int32_t)target == INT_MIN) { - view(&(Arg){.ui = ~0 & TAGMASK, .i = arg->i}, false); - } else { - view(&(Arg){.ui = target, .i = arg->i}, true); - } - return 0; -} - -int32_t chvt(const Arg *arg) { - struct timespec ts; - - // prevent the animation to rquest the new frame - server.allow_frame_scheduling = false; - - // backup current tag and monitor name - if (server.selmon) { - server.chvt_backup_tag = server.selmon->pertag->curtag; - strncpy(server.chvt_backup_selmon, server.selmon->wlr_output->name, - sizeof(server.chvt_backup_selmon) - 1); - } - - wlr_session_change_vt(server.session, arg->ui); - - // wait for DRM device to stabilize and ensure the server.session state is - // inactive - ts.tv_sec = 0; - ts.tv_nsec = 100000000; // 200ms - nanosleep(&ts, NULL); - - // allow frame scheduling, - // because server.session state is now inactive, rendermon will not enter - server.allow_frame_scheduling = true; - return 1; -} - -int32_t create_virtual_output(const Arg *arg) { - - if (!wlr_backend_is_multi(server.backend)) { - wlr_log(WLR_ERROR, "Expected a multi server.backend"); - return 0; - } - - bool done = false; - wlr_multi_for_each_backend(server.backend, create_output, &done); - - if (!done) { - wlr_log(WLR_ERROR, "Failed to create virtual output"); - return 0; - } - - wlr_log(WLR_INFO, "Virtual output created"); - return 0; -} - -int32_t destroy_all_virtual_output(const Arg *arg) { - - if (!wlr_backend_is_multi(server.backend)) { - wlr_log(WLR_ERROR, "Expected a multi server.backend"); - return 0; - } - - Monitor *m, *tmp; - wl_list_for_each_safe(m, tmp, &server.mons, link) { - if (wlr_output_is_headless(m->wlr_output)) { - // if(server.selmon == m) - // server.selmon = NULL; - wlr_output_destroy(m->wlr_output); - wlr_log(WLR_INFO, "Virtual output destroyed"); - } - } - return 0; -} - -int32_t defaultgaps(const Arg *arg) { - setgaps(gappoh, gappov, gappih, gappiv); - return 0; -} - -int32_t exchange_client(const Arg *arg) { - Client *c = server.selmon->sel; - if (!c || c->isfloating) - return 0; - - if ((c->isfullscreen || c->ismaximizescreen) && !is_scroller_layout(c->mon)) - return 0; - - exchange_two_client(c, direction_select(arg)); - return 0; -} - -int32_t exchange_stack_client(const Arg *arg) { - Client *c = server.selmon->sel; - Client *tc = NULL; - if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen) - return 0; - if (arg->i == NEXT) { - tc = get_next_stack_client(c, false); - } else { - tc = get_next_stack_client(c, true); - } - if (tc) - exchange_two_client(c, tc); - return 0; -} - -int32_t focusdir(const Arg *arg) { - Client *c = NULL; - c = direction_select(arg); - c = get_focused_stack_client(c); - if (c) { - focusclient(c, 1); - if (warpcursor) - warp_cursor(c); - } else { - if (config.focus_cross_tag) { - if (arg->i == LEFT || arg->i == UP) - viewtoleft_have_client(&(Arg){0}); - if (arg->i == RIGHT || arg->i == DOWN) - viewtoright_have_client(&(Arg){0}); - } else if (config.focus_cross_monitor) { - focusmon(arg); - } - } - return 0; -} - -int32_t focuslast(const Arg *arg) { - - Client *c = NULL; - Client *tc = NULL; - bool begin = false; - uint32_t target = 0; - - wl_list_for_each(c, &server.fstack, flink) { - if (c->iskilling || c->isminimized || c->isunglobal || - !client_surface(c)->mapped || client_is_unmanaged(c) || - client_is_x11_popup(c)) - continue; - - if (server.selmon && !server.selmon->sel) { - tc = c; - break; - } - - if (server.selmon && c == server.selmon->sel && !begin) { - begin = true; - continue; - } - - if (begin) { - tc = c; - break; - } - } - - if (!tc || !client_surface(tc)->mapped) - return 0; - - if ((int32_t)tc->tags > 0) { - focusclient(tc, 1); - target = get_tags_first_tag(tc->tags); - view(&(Arg){.ui = target}, true); - } - return 0; -} - -int32_t toggle_trackpad_enable(const Arg *arg) { - disable_trackpad = !disable_trackpad; - return 0; -} - -int32_t focusmon(const Arg *arg) { - Client *c = NULL; - Monitor *m = NULL; - Monitor *tm = NULL; - - if (arg->i != UNDIR) { - tm = dirtomon(arg->i); - } else if (arg->v) { - wl_list_for_each(m, &server.mons, link) { - if (!m->wlr_output->enabled) { - continue; - } - if (regex_match(arg->v, m->wlr_output->name)) { - tm = m; - break; - } - } - } else { - return 0; - } - - if (!tm || !tm->wlr_output->enabled || tm == server.selmon) - return 0; - - server.selmon = tm; - if (warpcursor) { - warp_cursor_to_selmon(server.selmon); - } - c = focustop(server.selmon); - if (!c) { - server.selmon->sel = NULL; - wlr_seat_pointer_notify_clear_focus(server.seat); - wlr_seat_keyboard_notify_clear_focus(server.seat); - focusclient(NULL, 0); - } else - focusclient(c, 1); - - return 0; -} - -int32_t focusstack(const Arg *arg) { - /* Focus the next or previous client (in tiling order) on server.selmon */ - Client *sel = focustop(server.selmon); - Client *tc = NULL; - - if (!sel) - return 0; - if (arg->i == NEXT) { - tc = get_next_stack_client(sel, false); - } else { - tc = get_next_stack_client(sel, true); - } - /* If only one client is visible on server.selmon , then c == sel */ - - if (!tc) - return 0; - - focusclient(tc, 1); - if (warpcursor) - warp_cursor(tc); - return 0; -} - -int32_t incnmaster(const Arg *arg) { - if (!arg || !server.selmon) - return 0; - server.selmon->pertag->nmasters[server.selmon->pertag->curtag] = MAX( - server.selmon->pertag->nmasters[server.selmon->pertag->curtag] + arg->i, - 0); - arrange(server.selmon, false, false); - return 0; -} - -int32_t incgaps(const Arg *arg) { - setgaps(server.selmon->gappoh + arg->i, server.selmon->gappov + arg->i, - server.selmon->gappih + arg->i, server.selmon->gappiv + arg->i); - return 0; -} - -int32_t incigaps(const Arg *arg) { - setgaps(server.selmon->gappoh, server.selmon->gappov, - server.selmon->gappih + arg->i, server.selmon->gappiv + arg->i); - return 0; -} - -int32_t incogaps(const Arg *arg) { - setgaps(server.selmon->gappoh + arg->i, server.selmon->gappov + arg->i, - server.selmon->gappih, server.selmon->gappiv); - return 0; -} - -int32_t incihgaps(const Arg *arg) { - setgaps(server.selmon->gappoh, server.selmon->gappov, - server.selmon->gappih + arg->i, server.selmon->gappiv); - return 0; -} - -int32_t incivgaps(const Arg *arg) { - setgaps(server.selmon->gappoh, server.selmon->gappov, server.selmon->gappih, - server.selmon->gappiv + arg->i); - return 0; -} - -int32_t incohgaps(const Arg *arg) { - setgaps(server.selmon->gappoh + arg->i, server.selmon->gappov, - server.selmon->gappih, server.selmon->gappiv); - return 0; -} - -int32_t incovgaps(const Arg *arg) { - setgaps(server.selmon->gappoh, server.selmon->gappov + arg->i, - server.selmon->gappih, server.selmon->gappiv); - return 0; -} - -int32_t setmfact(const Arg *arg) { - float f; - Client *c = NULL; - - if (!arg || !server.selmon || - !server.selmon->pertag->ltidxs[server.selmon->pertag->curtag]->arrange) - return 0; - f = arg->f < 1.0 - ? arg->f + - server.selmon->pertag->mfacts[server.selmon->pertag->curtag] - : arg->f - 1.0; - if (f < 0.1 || f > 0.9) - return 0; - - server.selmon->pertag->mfacts[server.selmon->pertag->curtag] = f; - wl_list_for_each(c, &server.clients, link) { - if (VISIBLEON(c, server.selmon) && ISTILED(c)) { - c->master_mfact_per = f; - } - } - arrange(server.selmon, false, false); - return 0; -} - -int32_t killclient(const Arg *arg) { - Client *c = NULL; - c = server.selmon->sel; - if (c) { - pending_kill_client(c); - } - return 0; -} - -int32_t moveresize(const Arg *arg) { - const char *cursors[] = {"nw-resize", "ne-resize", "sw-resize", - "se-resize"}; - - if (server.cursor_mode != CurNormal && server.cursor_mode != CurPressed) - return 0; - xytonode(server.cursor->x, server.cursor->y, NULL, &server.grabc, NULL, - NULL, NULL); - if (!server.grabc || client_is_unmanaged(server.grabc) || - server.grabc->isfullscreen || server.grabc->ismaximizescreen) { - server.grabc = NULL; - return 0; - } - /* Float the window and tell motionnotify to grab it */ - if (server.grabc->isfloating == 0 && arg->ui == CurMove) { - server.grabc->drag_to_tile = true; - setfloating(server.grabc, 1); - } - - switch (server.cursor_mode = arg->ui) { - case CurMove: - - server.grabcx = server.cursor->x - server.grabc->geom.x; - server.grabcy = server.cursor->y - server.grabc->geom.y; - wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, "grab"); - break; - case CurResize: - /* Doesn't work for X11 output - the next absolute motion event - * returns the server.cursor to where it started */ - if (server.grabc->isfloating) { - server.rzcorner = drag_corner; - server.grabcx = (int)round(server.cursor->x); - server.grabcy = (int)round(server.cursor->y); - if (server.rzcorner == 4) - /* identify the closest corner index */ - server.rzcorner = - (server.grabcx - server.grabc->geom.x < - server.grabc->geom.x + server.grabc->geom.width - - server.grabcx - ? 0 - : 1) + - (server.grabcy - server.grabc->geom.y < - server.grabc->geom.y + server.grabc->geom.height - - server.grabcy - ? 0 - : 2); - - if (drag_warp_cursor) { - server.grabcx = - server.rzcorner & 1 - ? server.grabc->geom.x + server.grabc->geom.width - : server.grabc->geom.x; - server.grabcy = - server.rzcorner & 2 - ? server.grabc->geom.y + server.grabc->geom.height - : server.grabc->geom.y; - wlr_cursor_warp_closest(server.cursor, NULL, server.grabcx, - server.grabcy); - } - - wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, - cursors[server.rzcorner]); - } else { - wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, "grab"); - } - break; - } - return 0; -} - -int32_t movewin(const Arg *arg) { - Client *c = NULL; - c = server.selmon->sel; - if (!c || c->isfullscreen) - return 0; - if (!c->isfloating) - togglefloating(NULL); - - switch (arg->ui) { - case NUM_TYPE_MINUS: - c->geom.x -= arg->i; - break; - case NUM_TYPE_PLUS: - c->geom.x += arg->i; - break; - default: - c->geom.x = arg->i; - break; - } - - switch (arg->ui2) { - case NUM_TYPE_MINUS: - c->geom.y -= arg->i2; - break; - case NUM_TYPE_PLUS: - c->geom.y += arg->i2; - break; - default: - c->geom.y = arg->i2; - break; - } - - c->iscustomsize = 1; - c->float_geom = c->geom; - resize(c, c->geom, 0); - return 0; -} - -int32_t quit(const Arg *arg) { - wl_display_terminate(server.dpy); - return 0; -} - -int32_t resizewin(const Arg *arg) { - Client *c = NULL; - c = server.selmon->sel; - int32_t offsetx = 0, offsety = 0; - - if (!c || c->isfullscreen || c->ismaximizescreen) - return 0; - - int32_t animations_state_backup = animations; - if (!c->isfloating) - animations = 0; - - if (ISTILED(c)) { - switch (arg->ui) { - case NUM_TYPE_MINUS: - offsetx = -arg->i; - break; - case NUM_TYPE_PLUS: - offsetx = arg->i; - break; - default: - offsetx = arg->i; - break; - } - - switch (arg->ui2) { - case NUM_TYPE_MINUS: - offsety = -arg->i2; - break; - case NUM_TYPE_PLUS: - offsety = arg->i2; - break; - default: - offsety = arg->i2; - break; - } - resize_tile_client(c, false, offsetx, offsety, 0); - animations = animations_state_backup; - return 0; - } - - switch (arg->ui) { - case NUM_TYPE_MINUS: - c->geom.width -= arg->i; - break; - case NUM_TYPE_PLUS: - c->geom.width += arg->i; - break; - default: - c->geom.width = arg->i; - break; - } - - switch (arg->ui2) { - case NUM_TYPE_MINUS: - c->geom.height -= arg->i2; - break; - case NUM_TYPE_PLUS: - c->geom.height += arg->i2; - break; - default: - c->geom.height = arg->i2; - break; - } - - c->iscustomsize = 1; - c->float_geom = c->geom; - resize(c, c->geom, 0); - animations = animations_state_backup; - return 0; -} - -int32_t restore_minimized(const Arg *arg) { - Client *c = NULL; - - if (server.selmon && server.selmon->isoverview) - return 0; - - if (server.selmon && server.selmon->sel && - server.selmon->sel->is_in_scratchpad && - server.selmon->sel->is_scratchpad_show) { - server.selmon->sel->isminimized = 0; - server.selmon->sel->is_scratchpad_show = 0; - server.selmon->sel->is_in_scratchpad = 0; - server.selmon->sel->isnamedscratchpad = 0; - setborder_color(server.selmon->sel); - return 0; - } - - wl_list_for_each(c, &server.clients, link) { - if (c->isminimized && !c->isnamedscratchpad) { - c->is_scratchpad_show = 0; - c->is_in_scratchpad = 0; - c->isnamedscratchpad = 0; - show_hide_client(c); - setborder_color(c); - arrange(c->mon, false, false); - focusclient(c, 0); - warp_cursor(c); - return 0; - } - } - return 0; -} - -int32_t setlayout(const Arg *arg) { - int32_t jk; - - for (jk = 0; jk < LENGTH(layouts); jk++) { - if (strcmp(layouts[jk].name, arg->v) == 0) { - server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] = - &layouts[jk]; - clear_fullscreen_and_maximized_state(server.selmon); - arrange(server.selmon, false, false); - printstatus(); - return 0; - } - } - return 0; -} - -int32_t setkeymode(const Arg *arg) { - free(server.keymode.name); - server.keymode.name = strdup(arg->v); - if (strcmp(server.keymode.name, "default") == 0) { - server.keymode.is_default = true; - } else { - server.keymode.is_default = false; - } - if (strcmp(server.keymode.name, "common") == 0) { - server.keymode.is_common = true; - } else { - server.keymode.is_common = false; - } - printstatus(); - return 1; -} - -int32_t set_proportion(const Arg *arg) { - - if (server.selmon->isoverview || !is_scroller_layout(server.selmon)) - return 0; - - if (server.selmon->visible_tiling_clients == 1 && - !scroller_ignore_proportion_single) - return 0; - - Client *tc = server.selmon->sel; - - if (tc) { - tc = get_scroll_stack_head(tc); - uint32_t max_client_width = - server.selmon->w.width - 2 * scroller_structs - gappih; - tc->scroller_proportion = arg->f; - tc->geom.width = max_client_width * arg->f; - arrange(server.selmon, false, false); - } - return 0; -} - -int32_t smartmovewin(const Arg *arg) { - Client *c = NULL, *tc = NULL; - int32_t nx, ny; - int32_t buttom, top, left, right, tar; - c = server.selmon->sel; - if (!c || c->isfullscreen) - return 0; - if (!c->isfloating) - setfloating(server.selmon->sel, true); - nx = c->geom.x; - ny = c->geom.y; - - switch (arg->i) { - case UP: - tar = -99999; - top = c->geom.y; - ny -= c->mon->w.height / 4; - - wl_list_for_each(tc, &server.clients, link) { - if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) - continue; - if (c->geom.x + c->geom.width < tc->geom.x || - c->geom.x > tc->geom.x + tc->geom.width) - continue; - buttom = tc->geom.y + tc->geom.height + gappiv; - if (top > buttom && ny < buttom) { - tar = MAX(tar, buttom); - }; - } - - ny = tar == -99999 ? ny : tar; - ny = MAX(ny, c->mon->w.y + c->mon->gappov); - break; - case DOWN: - tar = 99999; - buttom = c->geom.y + c->geom.height; - ny += c->mon->w.height / 4; - - wl_list_for_each(tc, &server.clients, link) { - if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) - continue; - if (c->geom.x + c->geom.width < tc->geom.x || - c->geom.x > tc->geom.x + tc->geom.width) - continue; - top = tc->geom.y - gappiv; - if (buttom < top && (ny + c->geom.height) > top) { - tar = MIN(tar, top - c->geom.height); - }; - } - ny = tar == 99999 ? ny : tar; - ny = MIN(ny, c->mon->w.y + c->mon->w.height - c->geom.height - - c->mon->gappov); - break; - case LEFT: - tar = -99999; - left = c->geom.x; - nx -= c->mon->w.width / 6; - - wl_list_for_each(tc, &server.clients, link) { - if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) - continue; - if (c->geom.y + c->geom.height < tc->geom.y || - c->geom.y > tc->geom.y + tc->geom.height) - continue; - right = tc->geom.x + tc->geom.width + gappih; - if (left > right && nx < right) { - tar = MAX(tar, right); - }; - } - - nx = tar == -99999 ? nx : tar; - nx = MAX(nx, c->mon->w.x + c->mon->gappoh); - break; - case RIGHT: - tar = 99999; - right = c->geom.x + c->geom.width; - nx += c->mon->w.width / 6; - wl_list_for_each(tc, &server.clients, link) { - if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) - continue; - if (c->geom.y + c->geom.height < tc->geom.y || - c->geom.y > tc->geom.y + tc->geom.height) - continue; - left = tc->geom.x - gappih; - if (right < left && (nx + c->geom.width) > left) { - tar = MIN(tar, left - c->geom.width); - }; - } - nx = tar == 99999 ? nx : tar; - nx = MIN(nx, c->mon->w.x + c->mon->w.width - c->geom.width - - c->mon->gappoh); - break; - } - - c->float_geom = (struct wlr_box){ - .x = nx, .y = ny, .width = c->geom.width, .height = c->geom.height}; - c->iscustomsize = 1; - resize(c, c->float_geom, 1); - return 0; -} - -int32_t smartresizewin(const Arg *arg) { - Client *c = NULL, *tc = NULL; - int32_t nw, nh; - int32_t buttom, top, left, right, tar; - c = server.selmon->sel; - if (!c || c->isfullscreen) - return 0; - if (!c->isfloating) - setfloating(c, true); - nw = c->geom.width; - nh = c->geom.height; - - switch (arg->i) { - case UP: - nh -= server.selmon->w.height / 8; - nh = MAX(nh, server.selmon->w.height / 10); - break; - case DOWN: - tar = -99999; - buttom = c->geom.y + c->geom.height; - nh += server.selmon->w.height / 8; - - wl_list_for_each(tc, &server.clients, link) { - if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) - continue; - if (c->geom.x + c->geom.width < tc->geom.x || - c->geom.x > tc->geom.x + tc->geom.width) - continue; - top = tc->geom.y - gappiv; - if (buttom < top && (nh + c->geom.y) > top) { - tar = MAX(tar, top - c->geom.y); - }; - } - nh = tar == -99999 ? nh : tar; - if (c->geom.y + nh + gappov > - server.selmon->w.y + server.selmon->w.height) - nh = server.selmon->w.y + server.selmon->w.height - c->geom.y - - gappov; - break; - case LEFT: - nw -= server.selmon->w.width / 16; - nw = MAX(nw, server.selmon->w.width / 10); - break; - case RIGHT: - tar = 99999; - right = c->geom.x + c->geom.width; - nw += server.selmon->w.width / 16; - wl_list_for_each(tc, &server.clients, link) { - if (!VISIBLEON(tc, server.selmon) || !tc->isfloating || tc == c) - continue; - if (c->geom.y + c->geom.height < tc->geom.y || - c->geom.y > tc->geom.y + tc->geom.height) - continue; - left = tc->geom.x - gappih; - if (right < left && (nw + c->geom.x) > left) { - tar = MIN(tar, left - c->geom.x); - }; - } - - nw = tar == 99999 ? nw : tar; - if (c->geom.x + nw + gappoh > - server.selmon->w.x + server.selmon->w.width) - nw = server.selmon->w.x + server.selmon->w.width - c->geom.x - - gappoh; - break; - } - - c->float_geom = (struct wlr_box){ - .x = c->geom.x, .y = c->geom.y, .width = nw, .height = nh}; - c->iscustomsize = 1; - resize(c, c->float_geom, 1); - return 0; -} - -int32_t centerwin(const Arg *arg) { - Client *c = NULL; - c = server.selmon->sel; - - if (!c || c->isfullscreen || c->ismaximizescreen) - return 0; - - if (c->isfloating) { - c->float_geom = setclient_coordinate_center(c, c->mon, c->geom, 0, 0); - c->iscustomsize = 1; - resize(c, c->float_geom, 1); - return 0; - } - - if (!is_scroller_layout(server.selmon)) - return 0; - - Client *stack_head = get_scroll_stack_head(c); - if (server.selmon->pertag->ltidxs[server.selmon->pertag->curtag]->id == - SCROLLER) { - stack_head->geom.x = - server.selmon->w.x + - (server.selmon->w.width - stack_head->geom.width) / 2; - } else { - stack_head->geom.y = - server.selmon->w.y + - (server.selmon->w.height - stack_head->geom.height) / 2; - } - - arrange(server.selmon, false, false); - return 0; -} - -int32_t spawn_shell(const Arg *arg) { - if (!arg->v) - return 0; - - if (fork() == 0) { - // 1. 忽略可能导致 coredump 的信号 - signal(SIGSEGV, SIG_IGN); - signal(SIGABRT, SIG_IGN); - signal(SIGILL, SIG_IGN); - - dup2(STDERR_FILENO, STDOUT_FILENO); - setsid(); - - execlp("sh", "sh", "-c", arg->v, (char *)NULL); - - // fallback to bash - execlp("bash", "bash", "-c", arg->v, (char *)NULL); - - // if execlp fails, we should not reach here - wlr_log(WLR_ERROR, - "mango: failed to execute command '%s' with shell: %s\n", - arg->v, strerror(errno)); - _exit(EXIT_FAILURE); - } - return 0; -} - -int32_t spawn(const Arg *arg) { - - if (!arg->v) - return 0; - - if (fork() == 0) { - // 1. 忽略可能导致 coredump 的信号 - signal(SIGSEGV, SIG_IGN); - signal(SIGABRT, SIG_IGN); - signal(SIGILL, SIG_IGN); - - dup2(STDERR_FILENO, STDOUT_FILENO); - setsid(); - - // 2. 解析参数 - char *argv[64]; - int32_t argc = 0; - char *token = strtok((char *)arg->v, " "); - while (token != NULL && argc < 63) { - wordexp_t p; - if (wordexp(token, &p, 0) == 0) { - argv[argc++] = p.we_wordv[0]; - } else { - argv[argc++] = token; - } - token = strtok(NULL, " "); - } - argv[argc] = NULL; - - // 3. 执行命令 - execvp(argv[0], argv); - - // 4. execvp 失败时:打印错误并直接退出(避免 coredump) - wlr_log(WLR_ERROR, "mango: execvp '%s' failed: %s\n", argv[0], - strerror(errno)); - _exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作 - } - return 0; -} - -int32_t spawn_on_empty(const Arg *arg) { - bool is_empty = true; - Client *c = NULL; - - wl_list_for_each(c, &server.clients, link) { - if (arg->ui & c->tags && c->mon == server.selmon) { - is_empty = false; - break; - } - } - if (!is_empty) { - view(arg, true); - return 0; - } else { - view(arg, true); - spawn_shell(arg); - } - return 0; -} - -int32_t switch_keyboard_layout(const Arg *arg) { - if (!server.kb_group || !server.kb_group->wlr_group || !server.seat) { - wlr_log(WLR_ERROR, "Invalid keyboard group or server.seat "); - return 0; - } - - struct wlr_keyboard *keyboard = &server.kb_group->wlr_group->keyboard; - if (!keyboard || !keyboard->keymap) { - wlr_log(WLR_ERROR, "Invalid keyboard or keymap"); - return 0; - } - - // 1. 获取当前布局和计算下一个布局 - xkb_layout_index_t current = xkb_state_serialize_layout( - keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); - const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap); - if (num_layouts < 2) { - wlr_log(WLR_INFO, "Only one layout available"); - return 0; - } - xkb_layout_index_t next = (current + 1) % num_layouts; - - // 6. 应用新 keymap - uint32_t depressed = keyboard->modifiers.depressed; - uint32_t latched = keyboard->modifiers.latched; - // uint32_t locked = keyboard->modifiers.locked; - - wlr_keyboard_set_keymap(keyboard, keyboard->keymap); - wlr_keyboard_notify_modifiers(keyboard, depressed, latched, server.locked, - next); - keyboard->modifiers.group = 0; - - // 7. 更新 server.seat - wlr_seat_set_keyboard(server.seat, keyboard); - wlr_seat_keyboard_notify_modifiers(server.seat, &keyboard->modifiers); - - InputDevice *id; - wl_list_for_each(id, &server.inputdevices, link) { - if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { - continue; - } - - struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data; - - wlr_keyboard_set_keymap(tkb, keyboard->keymap); - wlr_keyboard_notify_modifiers(tkb, depressed, latched, server.locked, - next); - tkb->modifiers.group = 0; - - // 7. 更新 server.seat - wlr_seat_set_keyboard(server.seat, tkb); - wlr_seat_keyboard_notify_modifiers(server.seat, &tkb->modifiers); - } - - printstatus(); - return 0; -} - -int32_t switch_layout(const Arg *arg) { - - int32_t jk, ji; - char *target_layout_name = NULL; - uint32_t len; - - if (config.circle_layout_count != 0) { - for (jk = 0; jk < config.circle_layout_count; jk++) { - - len = MAX(strlen(config.circle_layout[jk]), - strlen(server.selmon->pertag - ->ltidxs[server.selmon->pertag->curtag] - ->name)); - - if (strncmp( - config.circle_layout[jk], - server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] - ->name, - len) == 0) { - target_layout_name = jk == config.circle_layout_count - 1 - ? config.circle_layout[0] - : config.circle_layout[jk + 1]; - break; - } - } - - if (!target_layout_name) { - target_layout_name = config.circle_layout[0]; - } - - for (ji = 0; ji < LENGTH(layouts); ji++) { - len = MAX(strlen(layouts[ji].name), strlen(target_layout_name)); - if (strncmp(layouts[ji].name, target_layout_name, len) == 0) { - server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] = - &layouts[ji]; - - break; - } - } - clear_fullscreen_and_maximized_state(server.selmon); - arrange(server.selmon, false, false); - printstatus(); - return 0; - } - - for (jk = 0; jk < LENGTH(layouts); jk++) { - if (strcmp(layouts[jk].name, - server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] - ->name) == 0) { - server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] = - jk == LENGTH(layouts) - 1 ? &layouts[0] : &layouts[jk + 1]; - clear_fullscreen_and_maximized_state(server.selmon); - arrange(server.selmon, false, false); - printstatus(); - return 0; - } - } - return 0; -} - -int32_t switch_proportion_preset(const Arg *arg) { - float target_proportion = 0; - - if (config.scroller_proportion_preset_count == 0) { - return 0; - } - - if (server.selmon->isoverview || !is_scroller_layout(server.selmon)) - return 0; - - if (server.selmon->visible_tiling_clients == 1 && - !scroller_ignore_proportion_single) - return 0; - - Client *tc = server.selmon->sel; - - if (tc) { - tc = get_scroll_stack_head(tc); - for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) { - if (config.scroller_proportion_preset[i] == - tc->scroller_proportion) { - if (i == config.scroller_proportion_preset_count - 1) { - target_proportion = config.scroller_proportion_preset[0]; - break; - } else { - target_proportion = - config.scroller_proportion_preset[i + 1]; - break; - } - } - } - - if (target_proportion == 0) { - target_proportion = config.scroller_proportion_preset[0]; - } - - uint32_t max_client_width = - server.selmon->w.width - 2 * scroller_structs - gappih; - tc->scroller_proportion = target_proportion; - tc->geom.width = max_client_width * target_proportion; - arrange(server.selmon, false, false); - } - return 0; -} - -int32_t tag(const Arg *arg) { - Client *target_client = server.selmon->sel; - tag_client(arg, target_client); - return 0; -} - -int32_t tagmon(const Arg *arg) { - Monitor *m = NULL, *cm = NULL; - Client *c = focustop(server.selmon); - - if (!c) - return 0; - - if (arg->i != UNDIR) { - m = dirtomon(arg->i); - } else if (arg->v) { - wl_list_for_each(cm, &server.mons, link) { - if (!cm->wlr_output->enabled) { - continue; - } - if (regex_match(arg->v, cm->wlr_output->name)) { - m = cm; - break; - } - } - } else { - return 0; - } - - if (!m || !m->wlr_output->enabled) - return 0; - - uint32_t newtags = arg->ui ? arg->ui : arg->i2 ? c->tags : 0; - uint32_t target; - - if (c->mon == m) { - view(&(Arg){.ui = newtags}, true); - return 0; - } - - if (c == server.selmon->sel) { - server.selmon->sel = NULL; - } - - setmon(c, m, newtags, true); - client_update_oldmonname_record(c, m); - - reset_foreign_tolevel(c); - - c->float_geom.width = (int32_t)(c->float_geom.width * c->mon->w.width / - server.selmon->w.width); - c->float_geom.height = (int32_t)(c->float_geom.height * c->mon->w.height / - server.selmon->w.height); - server.selmon = c->mon; - c->float_geom = setclient_coordinate_center(c, c->mon, c->float_geom, 0, 0); - - // 重新计算居中的坐标 - // 重新计算居中的坐标 - if (c->isfloating) { - c->geom = c->float_geom; - target = get_tags_first_tag(c->tags); - view(&(Arg){.ui = target}, true); - focusclient(c, 1); - resize(c, c->geom, 1); - } else { - server.selmon = c->mon; - target = get_tags_first_tag(c->tags); - view(&(Arg){.ui = target}, true); - focusclient(c, 1); - arrange(server.selmon, false, false); - } - if (warpcursor) { - warp_cursor_to_selmon(c->mon); - } - return 0; -} - -int32_t tagsilent(const Arg *arg) { - Client *fc = NULL; - Client *target_client = NULL; - - if (!server.selmon || !server.selmon->sel) - return 0; - - target_client = server.selmon->sel; - target_client->tags = arg->ui & TAGMASK; - wl_list_for_each(fc, &server.clients, link) { - if (fc && fc != target_client && target_client->tags & fc->tags && - ISFULLSCREEN(fc) && !target_client->isfloating) { - clear_fullscreen_flag(fc); - } - } - exit_scroller_stack(target_client); - focusclient(focustop(server.selmon), 1); - arrange(target_client->mon, false, false); - return 0; -} - -int32_t tagtoleft(const Arg *arg) { - if (server.selmon->sel != NULL && - __builtin_popcount(server.selmon->tagset[server.selmon->seltags] & - TAGMASK) == 1 && - server.selmon->tagset[server.selmon->seltags] > 1) { - tag(&(Arg){.ui = server.selmon->tagset[server.selmon->seltags] >> 1, - .i = arg->i}); - } - return 0; -} - -int32_t tagtoright(const Arg *arg) { - if (server.selmon->sel != NULL && - __builtin_popcount(server.selmon->tagset[server.selmon->seltags] & - TAGMASK) == 1 && - server.selmon->tagset[server.selmon->seltags] & (TAGMASK >> 1)) { - tag(&(Arg){.ui = server.selmon->tagset[server.selmon->seltags] << 1, - .i = arg->i}); - } - return 0; -} - -int32_t toggle_named_scratchpad(const Arg *arg) { - Client *target_client = NULL; - char *arg_id = arg->v; - char *arg_title = arg->v2; - - target_client = get_client_by_id_or_title(arg_id, arg_title); - - if (!target_client && arg->v3) { - Arg arg_spawn = {.v = arg->v3}; - spawn_shell(&arg_spawn); - return 0; - } - - target_client->isnamedscratchpad = 1; - - apply_named_scratchpad(target_client); - return 0; -} - -int32_t toggle_render_border(const Arg *arg) { - server.render_border = !server.render_border; - arrange(server.selmon, false, false); - return 0; -} - -int32_t toggle_scratchpad(const Arg *arg) { - Client *c = NULL; - bool hit = false; - Client *tmp = NULL; - - if (server.selmon && server.selmon->isoverview) - return 0; - - wl_list_for_each_safe(c, tmp, &server.clients, link) { - if (!scratchpad_cross_monitor && c->mon != server.selmon) { - continue; - } - - if (single_scratchpad && c->isnamedscratchpad && !c->isminimized) { - set_minimized(c); - continue; - } - - if (c->isnamedscratchpad) - continue; - - if (hit) - continue; - - hit = switch_scratchpad_client_state(c); - } - return 0; -} - -int32_t togglefakefullscreen(const Arg *arg) { - Client *sel = focustop(server.selmon); - if (sel) - setfakefullscreen(sel, !sel->isfakefullscreen); - return 0; -} - -int32_t togglefloating(const Arg *arg) { - Client *sel = focustop(server.selmon); - - if (server.selmon && server.selmon->isoverview) - return 0; - - if (!sel) - return 0; - - if ((sel->isfullscreen || sel->ismaximizescreen)) { - sel->isfloating = 1; - } else { - sel->isfloating = !sel->isfloating; - } - - setfloating(sel, sel->isfloating); - return 0; -} - -int32_t togglefullscreen(const Arg *arg) { - Client *sel = focustop(server.selmon); - if (!sel) - return 0; - - sel->is_scratchpad_show = 0; - sel->is_in_scratchpad = 0; - sel->isnamedscratchpad = 0; - - if (sel->isfullscreen) - setfullscreen(sel, 0); - else - setfullscreen(sel, 1); - return 0; -} - -int32_t toggleglobal(const Arg *arg) { - if (!server.selmon->sel) - return 0; - if (server.selmon->sel->is_in_scratchpad) { - server.selmon->sel->is_in_scratchpad = 0; - server.selmon->sel->is_scratchpad_show = 0; - server.selmon->sel->isnamedscratchpad = 0; - } - server.selmon->sel->isglobal ^= 1; - if (server.selmon->sel->isglobal && (server.selmon->sel->prev_in_stack || - server.selmon->sel->next_in_stack)) { - exit_scroller_stack(server.selmon->sel); - arrange(server.selmon, false, false); - } - setborder_color(server.selmon->sel); - return 0; -} - -int32_t togglegaps(const Arg *arg) { - server.enablegaps ^= 1; - arrange(server.selmon, false, false); - return 0; -} - -int32_t togglemaximizescreen(const Arg *arg) { - Client *sel = focustop(server.selmon); - if (!sel) - return 0; - - sel->is_scratchpad_show = 0; - sel->is_in_scratchpad = 0; - sel->isnamedscratchpad = 0; - - if (sel->ismaximizescreen) - setmaximizescreen(sel, 0); - else - setmaximizescreen(sel, 1); - - setborder_color(sel); - return 0; -} - -int32_t toggleoverlay(const Arg *arg) { - if (!server.selmon->sel || !server.selmon->sel->mon || - server.selmon->sel->isfullscreen) { - return 0; - } - - server.selmon->sel->isoverlay ^= 1; - - if (server.selmon->sel->isoverlay) { - wlr_scene_node_reparent(&server.selmon->sel->scene->node, - server.layers[LyrOverlay]); - wlr_scene_node_raise_to_top(&server.selmon->sel->scene->node); - } else if (client_should_overtop(server.selmon->sel) && - server.selmon->sel->isfloating) { - wlr_scene_node_reparent(&server.selmon->sel->scene->node, - server.layers[LyrTop]); - } else { - wlr_scene_node_reparent( - &server.selmon->sel->scene->node, - server.layers[server.selmon->sel->isfloating ? LyrTop : LyrTile]); - } - setborder_color(server.selmon->sel); - return 0; -} - -int32_t toggletag(const Arg *arg) { - uint32_t newtags; - Client *sel = focustop(server.selmon); - if (!sel) - return 0; - - if ((int32_t)arg->ui == INT_MIN && sel->tags != (~0 & TAGMASK)) { - newtags = ~0 & TAGMASK; - } else if ((int32_t)arg->ui == INT_MIN && sel->tags == (~0 & TAGMASK)) { - newtags = 1 << (sel->mon->pertag->curtag - 1); - } else { - newtags = sel->tags ^ (arg->ui & TAGMASK); - } - - if (newtags) { - sel->tags = newtags; - focusclient(focustop(server.selmon), 1); - arrange(server.selmon, false, false); - } - printstatus(); - return 0; -} - -int32_t toggleview(const Arg *arg) { - uint32_t newtagset; - uint32_t target; - - target = arg->ui == 0 ? ~0 & TAGMASK : arg->ui; - - newtagset = server.selmon ? server.selmon->tagset[server.selmon->seltags] ^ - (target & TAGMASK) - : 0; - - if (newtagset) { - server.selmon->tagset[server.selmon->seltags] = newtagset; - focusclient(focustop(server.selmon), 1); - arrange(server.selmon, false, false); - } - printstatus(); - return 0; -} - -int32_t viewtoleft(const Arg *arg) { - uint32_t target = server.selmon->tagset[server.selmon->seltags]; - - if (server.selmon->isoverview || server.selmon->pertag->curtag == 0) { - return 0; - } - - target >>= 1; - - if (target == 0) { - return 0; - } - - if (!server.selmon || - (target) == server.selmon->tagset[server.selmon->seltags]) - return 0; - - view(&(Arg){.ui = target & TAGMASK, .i = arg->i}, true); - return 0; -} - -int32_t viewtoright(const Arg *arg) { - if (server.selmon->isoverview || server.selmon->pertag->curtag == 0) { - return 0; - } - uint32_t target = server.selmon->tagset[server.selmon->seltags]; - target <<= 1; - - if (!server.selmon || - (target) == server.selmon->tagset[server.selmon->seltags]) - return 0; - if (!(target & TAGMASK)) { - return 0; - } - - view(&(Arg){.ui = target & TAGMASK, .i = arg->i}, true); - return 0; -} - -int32_t viewtoleft_have_client(const Arg *arg) { - uint32_t n; - uint32_t current = - get_tags_first_tag_num(server.selmon->tagset[server.selmon->seltags]); - bool found = false; - - if (server.selmon->isoverview) { - return 0; - } - - if (current <= 1) - return 0; - - for (n = current - 1; n >= 1; n--) { - if (get_tag_status(n, server.selmon)) { - found = true; - break; - } - } - - if (found) - view(&(Arg){.ui = (1 << (n - 1)) & TAGMASK, .i = arg->i}, true); - return 0; -} - -int32_t viewtoright_have_client(const Arg *arg) { - uint32_t n; - uint32_t current = - get_tags_first_tag_num(server.selmon->tagset[server.selmon->seltags]); - bool found = false; - - if (server.selmon->isoverview) { - return 0; - } - - if (current >= LENGTH(tags)) - return 0; - - for (n = current + 1; n <= LENGTH(tags); n++) { - if (get_tag_status(n, server.selmon)) { - found = true; - break; - } - } - - if (found) - view(&(Arg){.ui = (1 << (n - 1)) & TAGMASK, .i = arg->i}, true); - return 0; -} - -int32_t viewcrossmon(const Arg *arg) { - focusmon(&(Arg){.v = arg->v, .i = UNDIR}); - view_in_mon(arg, true, server.selmon, true); - return 0; -} - -int32_t tagcrossmon(const Arg *arg) { - if (!server.selmon || !server.selmon->sel) - return 0; - - if (regex_match(server.selmon->wlr_output->name, arg->v)) { - tag_client(arg, server.selmon->sel); - return 0; - } - - tagmon(&(Arg){.ui = arg->ui, .i = UNDIR, .v = arg->v}); - return 0; -} - -int32_t comboview(const Arg *arg) { - uint32_t newtags = arg->ui & TAGMASK; - - if (!newtags || !server.selmon) - return 0; - - if (server.tag_combo) { - server.selmon->tagset[server.selmon->seltags] |= newtags; - focusclient(focustop(server.selmon), 1); - arrange(server.selmon, false, false); - } else { - server.tag_combo = true; - view(&(Arg){.ui = newtags}, false); - } - - printstatus(); - return 0; -} - -int32_t zoom(const Arg *arg) { - Client *c = NULL, *sel = focustop(server.selmon); - - if (!sel || !server.selmon || - !server.selmon->pertag->ltidxs[server.selmon->pertag->curtag] - ->arrange || - sel->isfloating) - return 0; - - /* Search for the first tiled window that is not sel, marking sel as - * NULL if we pass it along the way */ - wl_list_for_each(c, &server.clients, - link) if (VISIBLEON(c, server.selmon) && !c->isfloating) { - if (c != sel) - break; - sel = NULL; - } - - /* Return if no other tiled window was found */ - if (&c->link == &server.clients) - return 0; - - /* If we passed sel, move c to the front; otherwise, move sel to the - * front */ - if (!sel) - sel = c; - wl_list_remove(&sel->link); - wl_list_insert(&server.clients, &sel->link); - - focusclient(sel, 1); - arrange(server.selmon, false, false); - return 0; -} - -int32_t setoption(const Arg *arg) { - parse_option(&config, arg->v, arg->v2); - override_config(); - reset_option(); - return 0; -} - -int32_t minimized(const Arg *arg) { - - if (server.selmon && server.selmon->isoverview) - return 0; - - if (server.selmon->sel && !server.selmon->sel->isminimized) { - set_minimized(server.selmon->sel); - } - return 0; -} - -int32_t toggleoverview(const Arg *arg) { - Client *c = NULL; - - if (server.selmon->isoverview && ov_tab_mode && arg->i != 1 && - server.selmon->sel) { - focusstack(&(Arg){.i = 1}); - return 0; - } - - server.selmon->isoverview ^= 1; - uint32_t target; - uint32_t visible_client_number = 0; - - if (server.selmon->isoverview) { - wl_list_for_each(c, &server.clients, - link) if (c && c->mon == server.selmon && - !client_is_unmanaged(c) && - !client_is_x11_popup(c) && !c->isminimized && - !c->isunglobal) { - visible_client_number++; - } - if (visible_client_number > 0) { - target = ~0 & TAGMASK; - } else { - server.selmon->isoverview ^= 1; - return 0; - } - } else if (!server.selmon->isoverview && server.selmon->sel) { - target = get_tags_first_tag(server.selmon->sel->tags); - } else if (!server.selmon->isoverview && !server.selmon->sel) { - target = (1 << (server.selmon->pertag->prevtag - 1)); - view(&(Arg){.ui = target}, false); - refresh_monitors_workspaces_status(server.selmon); - return 0; - } - - // 正常视图到overview,退出所有窗口的浮动和全屏状态参与平铺, - // overview到正常视图,还原之前退出的浮动和全屏窗口状态 - if (server.selmon->isoverview) { - wl_list_for_each(c, &server.clients, link) { - if (c && c->mon == server.selmon && !client_is_unmanaged(c) && - !client_is_x11_popup(c) && !c->isunglobal) - overview_backup(c); - } - } else { - wl_list_for_each(c, &server.clients, link) { - if (c && c->mon == server.selmon && !c->iskilling && - !client_is_unmanaged(c) && !c->isunglobal && - !client_is_x11_popup(c) && client_surface(c)->mapped) - overview_restore(c, &(Arg){.ui = target}); - } - } - - view(&(Arg){.ui = target}, false); - - refresh_monitors_workspaces_status(server.selmon); - return 0; -} - -int32_t disable_monitor(const Arg *arg) { - Monitor *m = NULL; - struct wlr_output_state state = {0}; - wl_list_for_each(m, &server.mons, link) { - if (regex_match(arg->v, m->wlr_output->name)) { - wlr_output_state_set_enabled(&state, false); - wlr_output_commit_state(m->wlr_output, &state); - m->asleep = 1; - updatemons(NULL, NULL); - break; - } - } - return 0; -} - -int32_t enable_monitor(const Arg *arg) { - Monitor *m = NULL; - struct wlr_output_state state = {0}; - wl_list_for_each(m, &server.mons, link) { - if (regex_match(arg->v, m->wlr_output->name)) { - wlr_output_state_set_enabled(&state, true); - wlr_output_commit_state(m->wlr_output, &state); - m->asleep = 0; - updatemons(NULL, NULL); - break; - } - } - return 0; -} - -int32_t toggle_monitor(const Arg *arg) { - Monitor *m = NULL; - struct wlr_output_state state = {0}; - wl_list_for_each(m, &server.mons, link) { - if (regex_match(arg->v, m->wlr_output->name)) { - wlr_output_state_set_enabled(&state, !m->wlr_output->enabled); - wlr_output_commit_state(m->wlr_output, &state); - m->asleep = !m->wlr_output->enabled; - updatemons(NULL, NULL); - break; - } - } - return 0; -} - -int32_t scroller_stack(const Arg *arg) { - Client *c = server.selmon->sel; - Client *stack_head = NULL; - Client *source_stack_head = NULL; - if (!c || !c->mon || c->isfloating || !is_scroller_layout(server.selmon)) - return 0; - - if (c && (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal)) - return 0; - - bool is_horizontal_layout = - c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true - : false; - - Client *target_client = find_client_by_direction(c, arg, false, true); - - if (target_client && (!client_only_in_one_tag(target_client) || - target_client->isglobal || target_client->isunglobal)) - return 0; - - if (target_client) { - stack_head = get_scroll_stack_head(target_client); - } - - if (c) { - source_stack_head = get_scroll_stack_head(c); - } - - if (stack_head == source_stack_head) { - return 0; - } - - if (c->isfullscreen) { - setfullscreen(c, 0); - } - - if (c->ismaximizescreen) { - setmaximizescreen(c, 0); - } - - if (c->prev_in_stack) { - if ((is_horizontal_layout && arg->i == LEFT) || - (!is_horizontal_layout && arg->i == UP)) { - exit_scroller_stack(c); - wl_list_remove(&c->link); - wl_list_insert(source_stack_head->link.prev, &c->link); - arrange(server.selmon, false, false); - - } else if ((is_horizontal_layout && arg->i == RIGHT) || - (!is_horizontal_layout && arg->i == DOWN)) { - exit_scroller_stack(c); - wl_list_remove(&c->link); - wl_list_insert(&source_stack_head->link, &c->link); - arrange(server.selmon, false, false); - } - return 0; - } else if (c->next_in_stack) { - Client *next_in_stack = c->next_in_stack; - if ((is_horizontal_layout && arg->i == LEFT) || - (!is_horizontal_layout && arg->i == UP)) { - exit_scroller_stack(c); - wl_list_remove(&c->link); - wl_list_insert(next_in_stack->link.prev, &c->link); - arrange(server.selmon, false, false); - } else if ((is_horizontal_layout && arg->i == RIGHT) || - (!is_horizontal_layout && arg->i == DOWN)) { - exit_scroller_stack(c); - wl_list_remove(&c->link); - wl_list_insert(&next_in_stack->link, &c->link); - arrange(server.selmon, false, false); - } - return 0; - } - - if (!target_client || target_client->mon != c->mon) { - return 0; - } - - exit_scroller_stack(c); - - // Find the tail of target_client's stack - Client *stack_tail = target_client; - while (stack_tail->next_in_stack) { - stack_tail = stack_tail->next_in_stack; - } - - // Add c to the stack - stack_tail->next_in_stack = c; - c->prev_in_stack = stack_tail; - c->next_in_stack = NULL; - - if (stack_head->ismaximizescreen) { - setmaximizescreen(stack_head, 0); - } - - if (stack_head->isfullscreen) { - setfullscreen(stack_head, 0); - } - - arrange(server.selmon, false, false); - return 0; -} \ No newline at end of file +int32_t minimized(const Arg *arg); +int32_t restore_minimized(const Arg *arg); +int32_t toggle_scratchpad(const Arg *arg); +int32_t focusdir(const Arg *arg); +int32_t toggleoverview(const Arg *arg); +int32_t set_proportion(const Arg *arg); +int32_t switch_proportion_preset(const Arg *arg); +int32_t zoom(const Arg *arg); +int32_t tagsilent(const Arg *arg); +int32_t tagtoleft(const Arg *arg); +int32_t tagtoright(const Arg *arg); +int32_t tagcrossmon(const Arg *arg); +int32_t viewtoleft(const Arg *arg); +int32_t viewtoright(const Arg *arg); +int32_t viewtoleft_have_client(const Arg *arg); +int32_t viewtoright_have_client(const Arg *arg); +int32_t viewcrossmon(const Arg *arg); +int32_t togglefloating(const Arg *arg); +int32_t togglefullscreen(const Arg *arg); +int32_t togglemaximizescreen(const Arg *arg); +int32_t togglegaps(const Arg *arg); +int32_t tagmon(const Arg *arg); +int32_t spawn(const Arg *arg); +int32_t spawn_shell(const Arg *arg); +int32_t spawn_on_empty(const Arg *arg); +int32_t setkeymode(const Arg *arg); +int32_t switch_keyboard_layout(const Arg *arg); +int32_t setlayout(const Arg *arg); +int32_t switch_layout(const Arg *arg); +int32_t setmfact(const Arg *arg); +int32_t quit(const Arg *arg); +int32_t moveresize(const Arg *arg); +int32_t exchange_client(const Arg *arg); +int32_t exchange_stack_client(const Arg *arg); +int32_t killclient(const Arg *arg); +int32_t toggleglobal(const Arg *arg); +int32_t incnmaster(const Arg *arg); +int32_t focusmon(const Arg *arg); +int32_t focusstack(const Arg *arg); +int32_t chvt(const Arg *arg); +int32_t reload_config(const Arg *arg); +int32_t smartmovewin(const Arg *arg); +int32_t smartresizewin(const Arg *arg); +int32_t centerwin(const Arg *arg); +int32_t bind_to_view(const Arg *arg); +int32_t toggletag(const Arg *arg); +int32_t toggleview(const Arg *arg); +int32_t tag(const Arg *arg); +int32_t comboview(const Arg *arg); +int32_t incgaps(const Arg *arg); +int32_t incigaps(const Arg *arg); +int32_t incihgaps(const Arg *arg); +int32_t incivgaps(const Arg *arg); +int32_t incogaps(const Arg *arg); +int32_t incohgaps(const Arg *arg); +int32_t incovgaps(const Arg *arg); +int32_t defaultgaps(const Arg *arg); +int32_t togglefakefullscreen(const Arg *arg); +int32_t toggleoverlay(const Arg *arg); +int32_t movewin(const Arg *arg); +int32_t resizewin(const Arg *arg); +int32_t toggle_named_scratchpad(const Arg *arg); +int32_t toggle_render_border(const Arg *arg); +int32_t create_virtual_output(const Arg *arg); +int32_t destroy_all_virtual_output(const Arg *arg); +int32_t focuslast(const Arg *arg); +int32_t toggle_trackpad_enable(const Arg *arg); +int32_t setoption(const Arg *arg); +int32_t disable_monitor(const Arg *arg); +int32_t enable_monitor(const Arg *arg); +int32_t toggle_monitor(const Arg *arg); +int32_t scroller_stack(const Arg *arg); \ No newline at end of file diff --git a/src/ext-protocol/all.h b/src/ext-protocol/all.h deleted file mode 100644 index bc690fe..0000000 --- a/src/ext-protocol/all.h +++ /dev/null @@ -1,5 +0,0 @@ -#include "dwl-ipc.h" -#include "ext-workspace.h" -#include "foreign-toplevel.h" -#include "tearing.h" -#include "text-input.h" \ No newline at end of file diff --git a/src/ext-protocol/dwl-ipc.h b/src/ext-protocol/dwl-ipc.h index a66f074..b5f1fd6 100644 --- a/src/ext-protocol/dwl-ipc.h +++ b/src/ext-protocol/dwl-ipc.h @@ -1,41 +1,41 @@ #include "dwl-ipc-unstable-v2-protocol.h" -static void dwl_ipc_manager_bind(struct wl_client *client, void *data, +void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); -static void dwl_ipc_manager_destroy(struct wl_resource *resource); -static void dwl_ipc_manager_get_output(struct wl_client *client, +void dwl_ipc_manager_destroy(struct wl_resource *resource); +void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output); -static void dwl_ipc_manager_release(struct wl_client *client, +void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); -static void dwl_ipc_output_destroy(struct wl_resource *resource); -static void dwl_ipc_output_printstatus(Monitor *monitor); -static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); -static void dwl_ipc_output_set_client_tags(struct wl_client *client, +void dwl_ipc_output_destroy(struct wl_resource *resource); +void dwl_ipc_output_printstatus(Monitor *monitor); +void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); +void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags); -static void dwl_ipc_output_set_layout(struct wl_client *client, +void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index); -static void dwl_ipc_output_set_tags(struct wl_client *client, +void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset); -static void dwl_ipc_output_quit(struct wl_client *client, +void dwl_ipc_output_quit(struct wl_client *client, struct wl_resource *resource); -static void dwl_ipc_output_dispatch(struct wl_client *client, +void dwl_ipc_output_dispatch(struct wl_client *client, struct wl_resource *resource, const char *dispatch, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5); -static void dwl_ipc_output_release(struct wl_client *client, +void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource); /* global event handlers */ -static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = { +struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = { .release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; -static struct zdwl_ipc_output_v2_interface dwl_output_implementation = { +struct zdwl_ipc_output_v2_interface dwl_output_implementation = { .release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .quit = dwl_ipc_output_quit, @@ -57,7 +57,7 @@ void dwl_ipc_manager_bind(struct wl_client *client, void *data, zdwl_ipc_manager_v2_send_tags(manager_resource, LENGTH(tags)); - for (uint32_t i = 0; i < LENGTH(layouts); i++) + for (uint32_t i = 0; i < layouts_len; i++) { zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); } @@ -93,7 +93,7 @@ void dwl_ipc_manager_release(struct wl_client *client, wl_resource_destroy(resource); } -static void dwl_ipc_output_destroy(struct wl_resource *resource) { +void dwl_ipc_output_destroy(struct wl_resource *resource) { DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); wl_list_remove(&ipc_output->link); free(ipc_output); @@ -254,7 +254,7 @@ void dwl_ipc_output_set_layout(struct wl_client *client, return; monitor = ipc_output->mon; - if (index >= LENGTH(layouts)) + if (index >= layouts_len) index = 0; monitor->pertag->ltidxs[monitor->pertag->curtag] = &layouts[index]; diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h index f9d8238..ad9ac02 100644 --- a/src/ext-protocol/ext-workspace.h +++ b/src/ext-protocol/ext-workspace.h @@ -43,7 +43,7 @@ void toggle_workspace(struct workspace *target) { } } -static void handle_ext_workspace_activate(struct wl_listener *listener, +void handle_ext_workspace_activate(struct wl_listener *listener, void *data) { struct workspace *workspace = wl_container_of(listener, workspace, activate); @@ -56,7 +56,7 @@ static void handle_ext_workspace_activate(struct wl_listener *listener, wlr_log(WLR_INFO, "ext activating workspace %d", workspace->tag); } -static void handle_ext_workspace_deactivate(struct wl_listener *listener, +void handle_ext_workspace_deactivate(struct wl_listener *listener, void *data) { struct workspace *workspace = wl_container_of(listener, workspace, deactivate); @@ -69,8 +69,8 @@ static void handle_ext_workspace_deactivate(struct wl_listener *listener, wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag); } -static const char *get_name_from_tag(uint32_t tag) { - static const char *names[] = {"overview", "1", "2", "3", "4", +const char *get_name_from_tag(uint32_t tag) { + const char *names[] = {"overview", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; return (tag < sizeof(names) / sizeof(names[0])) ? names[tag] : NULL; } @@ -92,7 +92,7 @@ void cleanup_workspaces_by_monitor(Monitor *m) { } } -static void remove_workspace_by_tag(uint32_t tag, Monitor *m) { +void remove_workspace_by_tag(uint32_t tag, Monitor *m) { struct workspace *workspace, *tmp; wl_list_for_each_safe(workspace, tmp, &workspaces, link) { if (workspace->tag == tag && workspace->m == m) { @@ -102,7 +102,7 @@ static void remove_workspace_by_tag(uint32_t tag, Monitor *m) { } } -static void add_workspace_by_tag(int32_t tag, Monitor *m) { +void add_workspace_by_tag(int32_t tag, Monitor *m) { const char *name = get_name_from_tag(tag); struct workspace *workspace = ecalloc(1, sizeof(*workspace)); diff --git a/src/ext-protocol/foreign-toplevel.h b/src/ext-protocol/foreign-toplevel.h index 89f3839..8af39a3 100644 --- a/src/ext-protocol/foreign-toplevel.h +++ b/src/ext-protocol/foreign-toplevel.h @@ -1,6 +1,6 @@ #include -static struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; +struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; void handle_foreign_activate_request(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, foreign_activate_request); diff --git a/src/ext-protocol/tearing.h b/src/ext-protocol/tearing.h index 296d489..0cc03fa 100644 --- a/src/ext-protocol/tearing.h +++ b/src/ext-protocol/tearing.h @@ -9,7 +9,7 @@ struct tearing_controller { struct wlr_tearing_control_manager_v1 *tearing_control; struct wl_listener tearing_new_object; -static void handle_controller_set_hint(struct wl_listener *listener, +void handle_controller_set_hint(struct wl_listener *listener, void *data) { struct tearing_controller *controller = wl_container_of(listener, controller, set_hint); @@ -29,7 +29,7 @@ static void handle_controller_set_hint(struct wl_listener *listener, } } -static void handle_controller_destroy(struct wl_listener *listener, +void handle_controller_destroy(struct wl_listener *listener, void *data) { struct tearing_controller *controller = wl_container_of(listener, controller, destroy); diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h index 0e53e47..fb63f77 100644 --- a/src/ext-protocol/text-input.h +++ b/src/ext-protocol/text-input.h @@ -88,7 +88,7 @@ Monitor *output_nearest_to(int32_t lx, int32_t ly) { bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; } -static bool +bool is_keyboard_emulated_by_input_method(struct wlr_keyboard *keyboard, struct wlr_input_method_v2 *input_method) { struct wlr_virtual_keyboard_v1 *virtual_keyboard; @@ -103,7 +103,7 @@ is_keyboard_emulated_by_input_method(struct wlr_keyboard *keyboard, wl_resource_get_client(input_method->resource); } -static struct wlr_input_method_keyboard_grab_v2 * +struct wlr_input_method_keyboard_grab_v2 * get_keyboard_grab(KeyboardGroup *keyboard) { struct wlr_input_method_v2 *input_method = dwl_input_method_relay->input_method; @@ -157,7 +157,7 @@ bool dwl_im_keyboard_grab_forward_key(KeyboardGroup *keyboard, } } -static struct text_input * +struct text_input * get_active_text_input(struct dwl_input_method_relay *relay) { struct text_input *text_input; @@ -173,7 +173,7 @@ get_active_text_input(struct dwl_input_method_relay *relay) { return NULL; } -static void update_active_text_input(struct dwl_input_method_relay *relay) { +void update_active_text_input(struct dwl_input_method_relay *relay) { struct text_input *active_text_input = get_active_text_input(relay); if (relay->input_method && relay->active_text_input != active_text_input) { @@ -188,7 +188,7 @@ static void update_active_text_input(struct dwl_input_method_relay *relay) { relay->active_text_input = active_text_input; } -static void +void update_text_inputs_focused_surface(struct dwl_input_method_relay *relay) { struct text_input *text_input; wl_list_for_each(text_input, &relay->text_inputs, link) { @@ -215,7 +215,7 @@ update_text_inputs_focused_surface(struct dwl_input_method_relay *relay) { } } -static void update_popup_position(struct dwl_input_method_popup *popup) { +void update_popup_position(struct dwl_input_method_popup *popup) { struct dwl_input_method_relay *relay = popup->relay; struct text_input *text_input = relay->active_text_input; struct wlr_box cursor_rect; @@ -291,14 +291,14 @@ static void update_popup_position(struct dwl_input_method_popup *popup) { }); } -static void update_popups_position(struct dwl_input_method_relay *relay) { +void update_popups_position(struct dwl_input_method_relay *relay) { struct dwl_input_method_popup *popup; wl_list_for_each(popup, &relay->popups, link) { update_popup_position(popup); } } -static void handle_input_method_commit(struct wl_listener *listener, +void handle_input_method_commit(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, input_method_commit); @@ -330,7 +330,7 @@ static void handle_input_method_commit(struct wl_listener *listener, wlr_text_input_v3_send_done(text_input->input); } -static void handle_keyboard_grab_destroy(struct wl_listener *listener, +void handle_keyboard_grab_destroy(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, keyboard_grab_destroy); @@ -343,7 +343,7 @@ static void handle_keyboard_grab_destroy(struct wl_listener *listener, } } -static void handle_input_method_grab_keyboard(struct wl_listener *listener, +void handle_input_method_grab_keyboard(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, input_method_grab_keyboard); @@ -362,7 +362,7 @@ static void handle_input_method_grab_keyboard(struct wl_listener *listener, &relay->keyboard_grab_destroy); } -static void handle_input_method_destroy(struct wl_listener *listener, +void handle_input_method_destroy(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, input_method_destroy); @@ -377,7 +377,7 @@ static void handle_input_method_destroy(struct wl_listener *listener, update_active_text_input(relay); } -static void handle_popup_surface_destroy(struct wl_listener *listener, +void handle_popup_surface_destroy(struct wl_listener *listener, void *data) { struct dwl_input_method_popup *popup = wl_container_of(listener, popup, destroy); @@ -388,14 +388,14 @@ static void handle_popup_surface_destroy(struct wl_listener *listener, free(popup); } -static void handle_popup_surface_commit(struct wl_listener *listener, +void handle_popup_surface_commit(struct wl_listener *listener, void *data) { struct dwl_input_method_popup *popup = wl_container_of(listener, popup, commit); update_popup_position(popup); } -static void handle_input_method_new_popup_surface(struct wl_listener *listener, +void handle_input_method_new_popup_surface(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, input_method_new_popup_surface); @@ -422,7 +422,7 @@ static void handle_input_method_new_popup_surface(struct wl_listener *listener, update_popup_position(popup); } -static void handle_new_input_method(struct wl_listener *listener, void *data) { +void handle_new_input_method(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, new_input_method); struct wlr_input_method_v2 *input_method = data; @@ -459,7 +459,7 @@ static void handle_new_input_method(struct wl_listener *listener, void *data) { update_active_text_input(relay); } -static void send_state_to_input_method(struct dwl_input_method_relay *relay) { +void send_state_to_input_method(struct dwl_input_method_relay *relay) { struct wlr_input_method_v2 *input_method = relay->input_method; struct wlr_text_input_v3 *input = relay->active_text_input->input; @@ -481,7 +481,7 @@ static void send_state_to_input_method(struct dwl_input_method_relay *relay) { wlr_input_method_v2_send_done(input_method); } -static void handle_text_input_enable(struct wl_listener *listener, void *data) { +void handle_text_input_enable(struct wl_listener *listener, void *data) { struct text_input *text_input = wl_container_of(listener, text_input, enable); struct dwl_input_method_relay *relay = text_input->relay; @@ -494,7 +494,7 @@ static void handle_text_input_enable(struct wl_listener *listener, void *data) { wlr_text_input_v3_send_done(text_input->input); } -static void handle_text_input_disable(struct wl_listener *listener, +void handle_text_input_disable(struct wl_listener *listener, void *data) { struct text_input *text_input = wl_container_of(listener, text_input, disable); @@ -502,7 +502,7 @@ static void handle_text_input_disable(struct wl_listener *listener, update_active_text_input(text_input->relay); } -static void handle_text_input_commit(struct wl_listener *listener, void *data) { +void handle_text_input_commit(struct wl_listener *listener, void *data) { struct text_input *text_input = wl_container_of(listener, text_input, commit); struct dwl_input_method_relay *relay = text_input->relay; @@ -513,7 +513,7 @@ static void handle_text_input_commit(struct wl_listener *listener, void *data) { } } -static void handle_text_input_destroy(struct wl_listener *listener, +void handle_text_input_destroy(struct wl_listener *listener, void *data) { struct text_input *text_input = wl_container_of(listener, text_input, destroy); @@ -526,7 +526,7 @@ static void handle_text_input_destroy(struct wl_listener *listener, free(text_input); } -static void handle_new_text_input(struct wl_listener *listener, void *data) { +void handle_new_text_input(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, new_text_input); struct wlr_text_input_v3 *wlr_text_input = data; @@ -555,7 +555,7 @@ static void handle_new_text_input(struct wl_listener *listener, void *data) { update_text_inputs_focused_surface(relay); } -static void handle_focused_surface_destroy(struct wl_listener *listener, +void handle_focused_surface_destroy(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, focused_surface_destroy); diff --git a/src/ext-protocol/wlr_ext_workspace_v1.c b/src/ext-protocol/wlr_ext_workspace_v1.c index 2d781b3..90c39d2 100644 --- a/src/ext-protocol/wlr_ext_workspace_v1.c +++ b/src/ext-protocol/wlr_ext_workspace_v1.c @@ -71,39 +71,39 @@ struct wlr_ext_workspace_v1_resource { manager_resource_link; // wlr_ext_workspace_manager_v1_resource.workspace_resources }; -static const struct ext_workspace_group_handle_v1_interface group_impl; +const struct ext_workspace_group_handle_v1_interface group_impl; -static struct wlr_ext_workspace_group_v1_resource * +struct wlr_ext_workspace_group_v1_resource * group_resource_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of( resource, &ext_workspace_group_handle_v1_interface, &group_impl)); return wl_resource_get_user_data(resource); } -static const struct ext_workspace_handle_v1_interface workspace_impl; +const struct ext_workspace_handle_v1_interface workspace_impl; -static struct wlr_ext_workspace_v1_resource * +struct wlr_ext_workspace_v1_resource * workspace_resource_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &ext_workspace_handle_v1_interface, &workspace_impl)); return wl_resource_get_user_data(resource); } -static const struct ext_workspace_manager_v1_interface manager_impl; +const struct ext_workspace_manager_v1_interface manager_impl; -static struct wlr_ext_workspace_manager_v1_resource * +struct wlr_ext_workspace_manager_v1_resource * manager_resource_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of( resource, &ext_workspace_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } -static void workspace_handle_destroy(struct wl_client *client, +void workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } -static void workspace_handle_activate(struct wl_client *client, +void workspace_handle_activate(struct wl_client *client, struct wl_resource *workspace_resource) { struct wlr_ext_workspace_v1_resource *workspace_res = workspace_resource_from_resource(workspace_resource); @@ -121,7 +121,7 @@ static void workspace_handle_activate(struct wl_client *client, wl_list_insert(workspace_res->manager->requests.prev, &req->link); } -static void +void workspace_handle_deactivate(struct wl_client *client, struct wl_resource *workspace_resource) { struct wlr_ext_workspace_v1_resource *workspace_res = @@ -140,7 +140,7 @@ workspace_handle_deactivate(struct wl_client *client, wl_list_insert(workspace_res->manager->requests.prev, &req->link); } -static void workspace_handle_assign(struct wl_client *client, +void workspace_handle_assign(struct wl_client *client, struct wl_resource *workspace_resource, struct wl_resource *group_resource) { struct wlr_ext_workspace_v1_resource *workspace_res = @@ -162,7 +162,7 @@ static void workspace_handle_assign(struct wl_client *client, wl_list_insert(workspace_res->manager->requests.prev, &req->link); } -static void workspace_handle_remove(struct wl_client *client, +void workspace_handle_remove(struct wl_client *client, struct wl_resource *workspace_resource) { struct wlr_ext_workspace_v1_resource *workspace_res = workspace_resource_from_resource(workspace_resource); @@ -180,7 +180,7 @@ static void workspace_handle_remove(struct wl_client *client, wl_list_insert(workspace_res->manager->requests.prev, &req->link); } -static const struct ext_workspace_handle_v1_interface workspace_impl = { +const struct ext_workspace_handle_v1_interface workspace_impl = { .destroy = workspace_handle_destroy, .activate = workspace_handle_activate, .deactivate = workspace_handle_deactivate, @@ -188,7 +188,7 @@ static const struct ext_workspace_handle_v1_interface workspace_impl = { .remove = workspace_handle_remove, }; -static void group_handle_create_workspace(struct wl_client *client, +void group_handle_create_workspace(struct wl_client *client, struct wl_resource *group_resource, const char *name) { struct wlr_ext_workspace_group_v1_resource *group_res = @@ -213,17 +213,17 @@ static void group_handle_create_workspace(struct wl_client *client, wl_list_insert(group_res->manager->requests.prev, &req->link); } -static void group_handle_destroy(struct wl_client *client, +void group_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } -static const struct ext_workspace_group_handle_v1_interface group_impl = { +const struct ext_workspace_group_handle_v1_interface group_impl = { .create_workspace = group_handle_create_workspace, .destroy = group_handle_destroy, }; -static void destroy_workspace_resource( +void destroy_workspace_resource( struct wlr_ext_workspace_v1_resource *workspace_res) { wl_list_remove(&workspace_res->link); wl_list_remove(&workspace_res->manager_resource_link); @@ -231,7 +231,7 @@ static void destroy_workspace_resource( free(workspace_res); } -static void workspace_resource_destroy(struct wl_resource *resource) { +void workspace_resource_destroy(struct wl_resource *resource) { struct wlr_ext_workspace_v1_resource *workspace_res = workspace_resource_from_resource(resource); if (workspace_res) { @@ -239,7 +239,7 @@ static void workspace_resource_destroy(struct wl_resource *resource) { } } -static struct wlr_ext_workspace_v1_resource *create_workspace_resource( +struct wlr_ext_workspace_v1_resource *create_workspace_resource( struct wlr_ext_workspace_handle_v1 *workspace, struct wlr_ext_workspace_manager_v1_resource *manager_res) { struct wlr_ext_workspace_v1_resource *workspace_res = @@ -268,7 +268,7 @@ static struct wlr_ext_workspace_v1_resource *create_workspace_resource( return workspace_res; } -static void +void destroy_group_resource(struct wlr_ext_workspace_group_v1_resource *group_res) { wl_list_remove(&group_res->link); wl_list_remove(&group_res->manager_resource_link); @@ -276,7 +276,7 @@ destroy_group_resource(struct wlr_ext_workspace_group_v1_resource *group_res) { free(group_res); } -static void group_handle_resource_destroy(struct wl_resource *resource) { +void group_handle_resource_destroy(struct wl_resource *resource) { struct wlr_ext_workspace_group_v1_resource *group_res = group_resource_from_resource(resource); if (group_res) { @@ -284,7 +284,7 @@ static void group_handle_resource_destroy(struct wl_resource *resource) { } } -static struct wlr_ext_workspace_group_v1_resource *create_group_resource( +struct wlr_ext_workspace_group_v1_resource *create_group_resource( struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_ext_workspace_manager_v1_resource *manager_res) { struct wlr_ext_workspace_group_v1_resource *group_res = @@ -313,13 +313,13 @@ static struct wlr_ext_workspace_group_v1_resource *create_group_resource( return group_res; } -static void destroy_request(struct wlr_ext_workspace_v1_request *req) { +void destroy_request(struct wlr_ext_workspace_v1_request *req) { wl_list_remove(&req->link); free(req->name); free(req); } -static void manager_handle_commit(struct wl_client *client, +void manager_handle_commit(struct wl_client *client, struct wl_resource *resource) { struct wlr_ext_workspace_manager_v1_resource *manager_res = manager_resource_from_resource(resource); @@ -355,7 +355,7 @@ static void manager_handle_commit(struct wl_client *client, } } -static void handle_idle(void *data) { +void handle_idle(void *data) { struct wlr_ext_workspace_manager_v1 *manager = data; struct wlr_ext_workspace_manager_v1_resource *manager_res; @@ -365,7 +365,7 @@ static void handle_idle(void *data) { manager->idle_source = NULL; } -static void +void manager_schedule_done(struct wlr_ext_workspace_manager_v1 *manager) { if (!manager->idle_source) { manager->idle_source = @@ -373,7 +373,7 @@ manager_schedule_done(struct wlr_ext_workspace_manager_v1 *manager) { } } -static void +void workspace_send_details(struct wlr_ext_workspace_v1_resource *workspace_res) { struct wlr_ext_workspace_handle_v1 *workspace = workspace_res->workspace; struct wl_resource *resource = workspace_res->resource; @@ -393,18 +393,18 @@ workspace_send_details(struct wlr_ext_workspace_v1_resource *workspace_res) { manager_schedule_done(workspace->manager); } -static void manager_handle_stop(struct wl_client *client, +void manager_handle_stop(struct wl_client *client, struct wl_resource *resource) { ext_workspace_manager_v1_send_finished(resource); wl_resource_destroy(resource); } -static const struct ext_workspace_manager_v1_interface manager_impl = { +const struct ext_workspace_manager_v1_interface manager_impl = { .commit = manager_handle_commit, .stop = manager_handle_stop, }; -static void destroy_manager_resource( +void destroy_manager_resource( struct wlr_ext_workspace_manager_v1_resource *manager_res) { struct wlr_ext_workspace_v1_request *req, *tmp; wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { @@ -427,7 +427,7 @@ static void destroy_manager_resource( free(manager_res); } -static void manager_resource_destroy(struct wl_resource *resource) { +void manager_resource_destroy(struct wl_resource *resource) { struct wlr_ext_workspace_manager_v1_resource *manager_res = manager_resource_from_resource(resource); if (manager_res) { @@ -435,7 +435,7 @@ static void manager_resource_destroy(struct wl_resource *resource) { } } -static void +void group_send_details(struct wlr_ext_workspace_group_v1_resource *group_res) { struct wlr_ext_workspace_group_handle_v1 *group = group_res->group; struct wl_resource *resource = group_res->resource; @@ -458,7 +458,7 @@ group_send_details(struct wlr_ext_workspace_group_v1_resource *group_res) { manager_schedule_done(group->manager); } -static void manager_bind(struct wl_client *client, void *data, uint32_t version, +void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_ext_workspace_manager_v1 *manager = data; @@ -525,7 +525,7 @@ static void manager_bind(struct wl_client *client, void *data, uint32_t version, ext_workspace_manager_v1_send_done(manager_res->resource); } -static void manager_handle_display_destroy(struct wl_listener *listener, +void manager_handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_ext_workspace_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); @@ -623,7 +623,7 @@ wlr_ext_workspace_group_handle_v1_create( return group; } -static void +void workspace_send_group(struct wlr_ext_workspace_handle_v1 *workspace, struct wlr_ext_workspace_group_handle_v1 *group, bool enter) { @@ -648,7 +648,7 @@ workspace_send_group(struct wlr_ext_workspace_handle_v1 *workspace, manager_schedule_done(workspace->manager); } -static void +void destroy_group_output(struct wlr_ext_workspace_v1_group_output *group_output) { wl_list_remove(&group_output->output_bind.link); wl_list_remove(&group_output->output_destroy.link); @@ -656,7 +656,7 @@ destroy_group_output(struct wlr_ext_workspace_v1_group_output *group_output) { free(group_output); } -static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group, +void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output, bool enter) { struct wlr_ext_workspace_group_v1_resource *group_res; @@ -728,7 +728,7 @@ void wlr_ext_workspace_group_handle_v1_destroy( free(group); } -static void handle_output_bind(struct wl_listener *listener, void *data) { +void handle_output_bind(struct wl_listener *listener, void *data) { struct wlr_ext_workspace_v1_group_output *group_output = wl_container_of(listener, group_output, output_bind); struct wlr_output_event_bind *event = data; @@ -745,14 +745,14 @@ static void handle_output_bind(struct wl_listener *listener, void *data) { manager_schedule_done(group_output->group->manager); } -static void handle_output_destroy(struct wl_listener *listener, void *data) { +void handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_ext_workspace_v1_group_output *group_output = wl_container_of(listener, group_output, output_destroy); group_send_output(group_output->group, group_output->output, false); destroy_group_output(group_output); } -static struct wlr_ext_workspace_v1_group_output * +struct wlr_ext_workspace_v1_group_output * get_group_output(struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output) { struct wlr_ext_workspace_v1_group_output *group_output; @@ -929,7 +929,7 @@ void wlr_ext_workspace_handle_v1_set_name( manager_schedule_done(workspace->manager); } -static bool array_equal(struct wl_array *a, struct wl_array *b) { +bool array_equal(struct wl_array *a, struct wl_array *b) { return (a->size == b->size) && (a->size == 0 || memcmp(a->data, b->data, a->size) == 0); } @@ -956,7 +956,7 @@ void wlr_ext_workspace_handle_v1_set_coordinates( manager_schedule_done(workspace->manager); } -static void workspace_set_state(struct wlr_ext_workspace_handle_v1 *workspace, +void workspace_set_state(struct wlr_ext_workspace_handle_v1 *workspace, enum ext_workspace_handle_v1_state state, bool enabled) { uint32_t old_state = workspace->state; diff --git a/src/fetch/client.h b/src/fetch/client.h index bfe9b46..82ab247 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -126,7 +126,7 @@ setclient_coordinate_center(Client *c, Monitor *tm, struct wlr_box geom, return tempbox; } /* Helper: Check if rule matches client */ -static bool is_window_rule_matches(const ConfigWinRule *r, const char *appid, +bool is_window_rule_matches(const ConfigWinRule *r, const char *appid, const char *title) { return (r->title && regex_match(r->title, title) && !r->id) || (r->id && regex_match(r->id, appid) && !r->title) || diff --git a/src/layout/arrange.c b/src/layout/arrange.c new file mode 100644 index 0000000..6eaca59 --- /dev/null +++ b/src/layout/arrange.c @@ -0,0 +1,844 @@ +#include "../type.h" +#include "layout.h" +#include "../animation/tag.h" + +extern Server server; +extern Layout layouts[]; +extern Layout overviewlayout; + +void set_size_per(Monitor *m, Client *c) { + Client *fc = NULL; + bool found = false; + + if (!m || !c) + return; + + wl_list_for_each(fc, &server.clients, link) { + if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) { + c->master_mfact_per = fc->master_mfact_per; + c->master_inner_per = fc->master_inner_per; + c->stack_inner_per = fc->stack_inner_per; + found = true; + break; + } + } + + if (!found) { + c->master_mfact_per = m->pertag->mfacts[m->pertag->curtag]; + c->master_inner_per = 1.0f; + c->stack_inner_per = 1.0f; + } +} + +void resize_tile_master_horizontal(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, + int32_t type) { + Client *tc = NULL; + float delta_x, delta_y; + Client *next = NULL; + Client *prev = NULL; + Client *nextnext = NULL; + Client *prevprev = NULL; + struct wl_list *node; + bool begin_find_nextnext = false; + bool begin_find_prevprev = false; + + // 从当前节点的下一个开始遍历 + for (node = rec->link.next; node != &server.clients; + node = node->next) { + tc = wl_container_of(node, tc, link); + if (begin_find_nextnext && VISIBLEON(tc, rec->mon) && + ISTILED(tc)) { + nextnext = tc; + break; + } + + if (!begin_find_nextnext && VISIBLEON(tc, rec->mon) && + ISTILED(tc)) { // 根据你的实际字段名调整 + next = tc; + begin_find_nextnext = true; + continue; + } + } + + // 从当前节点的上一个开始遍历 + for (node = rec->link.prev; node != &server.clients; + node = node->prev) { + tc = wl_container_of(node, tc, link); + + if (begin_find_prevprev && VISIBLEON(tc, rec->mon) && + ISTILED(tc)) { + prevprev = tc; + break; + } + + if (!begin_find_prevprev && VISIBLEON(tc, rec->mon) && + ISTILED(tc)) { // 根据你的实际字段名调整 + prev = tc; + begin_find_prevprev = true; + continue; + } + } + + if (!server.start_drag_window && isdrag) { + server.drag_begin_cursorx = server.cursor->x; + server.drag_begin_cursory = server.cursor->y; + server.start_drag_window = true; + // 记录初始状态 + rec->old_master_mfact_per = rec->master_mfact_per; + rec->old_master_inner_per = rec->master_inner_per; + rec->old_stack_inner_per = rec->stack_inner_per; + rec->cursor_in_upper_half = + server.cursor->y < + rec->geom.y + rec->geom.height / 2; + rec->cursor_in_left_half = + server.cursor->x < + rec->geom.x + rec->geom.width / 2; + // 记录初始几何信息 + rec->drag_begin_geom = rec->geom; + } else { + // 计算相对于屏幕尺寸的比例变化 + if (isdrag) { + + offsetx = server.cursor->x - server.drag_begin_cursorx; + offsety = server.cursor->y - server.drag_begin_cursory; + } else { + rec->old_master_mfact_per = rec->master_mfact_per; + rec->old_master_inner_per = rec->master_inner_per; + rec->old_stack_inner_per = rec->stack_inner_per; + rec->drag_begin_geom = rec->geom; + rec->cursor_in_upper_half = true; + rec->cursor_in_left_half = false; + } + + if (rec->ismaster) { + delta_x = (float)(offsetx) * (rec->old_master_mfact_per) / + rec->drag_begin_geom.width; + delta_y = (float)(offsety) * (rec->old_master_inner_per) / + rec->drag_begin_geom.height; + } else { + delta_x = (float)(offsetx) * + (1 - rec->old_master_mfact_per) / + rec->drag_begin_geom.width; + delta_y = (float)(offsety) * (rec->old_stack_inner_per) / + rec->drag_begin_geom.height; + } + bool moving_up; + bool moving_down; + + if (!isdrag) { + moving_up = offsety < 0 ? true : false; + moving_down = offsety > 0 ? true : false; + } else { + moving_up = server.cursor->y < server.drag_begin_cursory; + moving_down = server.cursor->y > server.drag_begin_cursory; + } + + if (rec->ismaster && !prev) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } else if (rec->ismaster && next && !next->ismaster) { + if (moving_up) { + delta_y = fabsf(delta_y); + } else { + delta_y = -fabsf(delta_y); + } + } else if (!rec->ismaster && prev && prev->ismaster) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } else if (!rec->ismaster && !next) { + if (moving_up) { + delta_y = fabsf(delta_y); + } else { + delta_y = -fabsf(delta_y); + } + } else if (type == CENTER_TILE && !rec->ismaster && + !nextnext) { + if (moving_up) { + delta_y = fabsf(delta_y); + } else { + delta_y = -fabsf(delta_y); + } + } else if (type == CENTER_TILE && !rec->ismaster && prevprev && + prevprev->ismaster) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } else if ((rec->cursor_in_upper_half && moving_up) || + (!rec->cursor_in_upper_half && moving_down)) { + // 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度 + delta_y = fabsf(delta_y); + delta_y = delta_y * 2; + } else { + // 其他情况 → 减小高度 + delta_y = -fabsf(delta_y); + delta_y = delta_y * 2; + } + + if (!rec->ismaster && rec->isleftstack && + type == CENTER_TILE) { + delta_x = delta_x * -1.0f; + } + + if (rec->ismaster && type == CENTER_TILE && + rec->cursor_in_left_half) { + delta_x = delta_x * -1.0f; + } + + if (rec->ismaster && type == CENTER_TILE) { + delta_x = delta_x * 2; + } + + if (type == RIGHT_TILE) { + delta_x = delta_x * -1.0f; + } + + // 直接设置新的比例,基于初始值 + 变化量 + float new_master_mfact_per = + rec->old_master_mfact_per + delta_x; + float new_master_inner_per = + rec->old_master_inner_per + delta_y; + float new_stack_inner_per = rec->old_stack_inner_per + delta_y; + + // 应用限制,确保比例在合理范围内 + new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per)); + new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per)); + new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per)); + + // 应用到所有平铺窗口 + wl_list_for_each(tc, &server.clients, link) { + if (VISIBLEON(tc, rec->mon) && ISTILED(tc)) { + tc->master_mfact_per = new_master_mfact_per; + } + } + + rec->master_inner_per = new_master_inner_per; + rec->stack_inner_per = new_stack_inner_per; + + if (!isdrag) { + arrange(rec->mon, false, false); + return; + } + + if (server.last_apply_drag_time == 0 || + time - server.last_apply_drag_time > drag_tile_refresh_interval) { + arrange(rec->mon, false, false); + server.last_apply_drag_time = time; + } + } +} + +void resize_tile_master_vertical(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, int32_t type) { + Client *tc = NULL; + float delta_x, delta_y; + Client *next = NULL; + Client *prev = NULL; + struct wl_list *node; + + // 从当前节点的下一个开始遍历 + for (node = rec->link.next; node != &server.clients; + node = node->next) { + tc = wl_container_of(node, tc, link); + + if (VISIBLEON(tc, rec->mon) && + ISTILED(tc)) { // 根据你的实际字段名调整 + next = tc; + break; + } + } + + // 从当前节点的上一个开始遍历 + for (node = rec->link.prev; node != &server.clients; + node = node->prev) { + tc = wl_container_of(node, tc, link); + + if (VISIBLEON(tc, rec->mon) && + ISTILED(tc)) { // 根据你的实际字段名调整 + prev = tc; + break; + } + } + + if (!server.start_drag_window && isdrag) { + server.drag_begin_cursorx = server.cursor->x; + server.drag_begin_cursory = server.cursor->y; + server.start_drag_window = true; + + // 记录初始状态 + rec->old_master_mfact_per = rec->master_mfact_per; + rec->old_master_inner_per = rec->master_inner_per; + rec->old_stack_inner_per = rec->stack_inner_per; + rec->cursor_in_upper_half = + server.cursor->y < + rec->geom.y + rec->geom.height / 2; + rec->cursor_in_left_half = + server.cursor->x < + rec->geom.x + rec->geom.width / 2; + // 记录初始几何信息 + rec->drag_begin_geom = rec->geom; + } else { + // 计算相对于屏幕尺寸的比例变化 + // 计算相对于屏幕尺寸的比例变化 + if (isdrag) { + + offsetx = server.cursor->x - server.drag_begin_cursorx; + offsety = server.cursor->y - server.drag_begin_cursory; + } else { + rec->old_master_mfact_per = rec->master_mfact_per; + rec->old_master_inner_per = rec->master_inner_per; + rec->old_stack_inner_per = rec->stack_inner_per; + rec->drag_begin_geom = rec->geom; + rec->cursor_in_upper_half = true; + rec->cursor_in_left_half = false; + } + + if (rec->ismaster) { + // 垂直版本:左右移动调整高度比例,上下移动调整宽度比例 + delta_x = (float)(offsetx) * (rec->old_master_inner_per) / + rec->drag_begin_geom.width; + delta_y = (float)(offsety) * (rec->old_master_mfact_per) / + rec->drag_begin_geom.height; + } else { + delta_x = (float)(offsetx) * (rec->old_stack_inner_per) / + rec->drag_begin_geom.width; + delta_y = (float)(offsety) * + (1 - rec->old_master_mfact_per) / + rec->drag_begin_geom.height; + } + + bool moving_left; + bool moving_right; + + if (!isdrag) { + moving_left = offsetx < 0 ? true : false; + moving_right = offsetx > 0 ? true : false; + } else { + moving_left = server.cursor->x < server.drag_begin_cursorx; + moving_right = server.cursor->x > server.drag_begin_cursorx; + } + + // 调整主区域和栈区域的高度比例(垂直分割) + if (rec->ismaster && !prev) { + if (moving_left) { + delta_x = -fabsf(delta_x); // 向上移动减少主区域高度 + } else { + delta_x = fabsf(delta_x); // 向下移动增加主区域高度 + } + } else if (rec->ismaster && next && !next->ismaster) { + if (moving_left) { + delta_x = fabsf(delta_x); // 向上移动增加主区域高度 + } else { + delta_x = -fabsf(delta_x); // 向下移动减少主区域高度 + } + } else if (!rec->ismaster && prev && prev->ismaster) { + if (moving_left) { + delta_x = -fabsf(delta_x); // 向上移动减少栈区域高度 + } else { + delta_x = fabsf(delta_x); // 向下移动增加栈区域高度 + } + } else if (!rec->ismaster && !next) { + if (moving_left) { + delta_x = fabsf(delta_x); // 向上移动增加栈区域高度 + } else { + delta_x = -fabsf(delta_x); // 向下移动减少栈区域高度 + } + } else if ((rec->cursor_in_left_half && moving_left) || + (!rec->cursor_in_left_half && moving_right)) { + // 光标在窗口左侧且向左移动,或在窗口右侧且向右移动 → 增加宽度 + delta_x = fabsf(delta_x); + delta_x = delta_x * 2; + } else { + // 其他情况 → 减小宽度 + delta_x = -fabsf(delta_x); + delta_x = delta_x * 2; + } + + // 直接设置新的比例,基于初始值 + 变化量 + float new_master_mfact_per = rec->old_master_mfact_per + + delta_y; // 垂直:delta_y调整主区域高度 + float new_master_inner_per = rec->old_master_inner_per + + delta_x; // 垂直:delta_x调整主区域内部宽度 + float new_stack_inner_per = rec->old_stack_inner_per + + delta_x; // 垂直:delta_x调整栈区域内部宽度 + + // 应用限制,确保比例在合理范围内 + new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per)); + new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per)); + new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per)); + + // 应用到所有平铺窗口 + wl_list_for_each(tc, &server.clients, link) { + if (VISIBLEON(tc, rec->mon) && ISTILED(tc)) { + tc->master_mfact_per = new_master_mfact_per; + } + } + + rec->master_inner_per = new_master_inner_per; + rec->stack_inner_per = new_stack_inner_per; + + if (!isdrag) { + arrange(rec->mon, false, false); + return; + } + + if (server.last_apply_drag_time == 0 || + time - server.last_apply_drag_time > drag_tile_refresh_interval) { + arrange(rec->mon, false, false); + server.last_apply_drag_time = time; + } + } +} + +void resize_tile_scroller(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, bool isvertical) { + float delta_x, delta_y; + float new_scroller_proportion; + float new_stack_proportion; + Client *stack_head = get_scroll_stack_head(rec); + + if (rec && rec->mon->visible_tiling_clients == 1 && + !scroller_ignore_proportion_single) + return; + + if (!server.start_drag_window && isdrag) { + server.drag_begin_cursorx = server.cursor->x; + server.drag_begin_cursory = server.cursor->y; + server.start_drag_window = true; + + // 记录初始状态 + stack_head->old_scroller_pproportion = stack_head->scroller_proportion; + rec->old_stack_proportion = rec->stack_proportion; + + rec->cursor_in_left_half = + server.cursor->x < + rec->geom.x + rec->geom.width / 2; + rec->cursor_in_upper_half = + server.cursor->y < + rec->geom.y + rec->geom.height / 2; + // 记录初始几何信息 + rec->drag_begin_geom = rec->geom; + } else { + // 计算相对于屏幕尺寸的比例变化 + // 计算相对于屏幕尺寸的比例变化 + if (isdrag) { + + offsetx = server.cursor->x - server.drag_begin_cursorx; + offsety = server.cursor->y - server.drag_begin_cursory; + } else { + rec->old_master_mfact_per = rec->master_mfact_per; + rec->old_master_inner_per = rec->master_inner_per; + rec->old_stack_inner_per = rec->stack_inner_per; + rec->drag_begin_geom = rec->geom; + stack_head->old_scroller_pproportion = + stack_head->scroller_proportion; + rec->old_stack_proportion = rec->stack_proportion; + rec->cursor_in_upper_half = false; + rec->cursor_in_left_half = false; + } + + if (isvertical) { + delta_y = (float)(offsety) * + (stack_head->old_scroller_pproportion) / + rec->drag_begin_geom.height; + delta_x = (float)(offsetx) * (rec->old_stack_proportion) / + rec->drag_begin_geom.width; + } else { + delta_x = (float)(offsetx) * + (stack_head->old_scroller_pproportion) / + rec->drag_begin_geom.width; + delta_y = (float)(offsety) * (rec->old_stack_proportion) / + rec->drag_begin_geom.height; + } + + bool moving_up; + bool moving_down; + bool moving_left; + bool moving_right; + + if (!isdrag) { + moving_up = offsety < 0 ? true : false; + moving_down = offsety > 0 ? true : false; + moving_left = offsetx < 0 ? true : false; + moving_right = offsetx > 0 ? true : false; + } else { + moving_up = server.cursor->y < server.drag_begin_cursory; + moving_down = server.cursor->y > server.drag_begin_cursory; + moving_left = server.cursor->x < server.drag_begin_cursorx; + moving_right = server.cursor->x > server.drag_begin_cursorx; + } + + if ((rec->cursor_in_upper_half && moving_up) || + (!rec->cursor_in_upper_half && moving_down)) { + // 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度 + delta_y = fabsf(delta_y); + } else { + // 其他情况 → 减小高度 + delta_y = -fabsf(delta_y); + } + + if ((rec->cursor_in_left_half && moving_left) || + (!rec->cursor_in_left_half && moving_right)) { + delta_x = fabsf(delta_x); + } else { + delta_x = -fabsf(delta_x); + } + + if (isvertical) { + if (!rec->next_in_stack && rec->prev_in_stack && + !isdrag) { + delta_x = delta_x * -1.0f; + } + if (!rec->next_in_stack && rec->prev_in_stack && + isdrag) { + if (moving_right) { + delta_x = -fabsf(delta_x); + } else { + delta_x = fabsf(delta_x); + } + } + if (!rec->prev_in_stack && rec->next_in_stack && + isdrag) { + if (moving_left) { + delta_x = -fabsf(delta_x); + } else { + delta_x = fabsf(delta_x); + } + } + + if (isdrag) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } + + } else { + if (!rec->next_in_stack && rec->prev_in_stack && + !isdrag) { + delta_y = delta_y * -1.0f; + } + if (!rec->next_in_stack && rec->prev_in_stack && + isdrag) { + if (moving_down) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } + if (!rec->prev_in_stack && rec->next_in_stack && + isdrag) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } + + if (isdrag) { + if (moving_left) { + delta_x = -fabsf(delta_x); + } else { + delta_x = fabsf(delta_x); + } + } + } + + // 直接设置新的比例,基于初始值 + 变化量 + if (isvertical) { + new_scroller_proportion = + stack_head->old_scroller_pproportion + delta_y; + new_stack_proportion = rec->old_stack_proportion + delta_x; + + } else { + new_scroller_proportion = + stack_head->old_scroller_pproportion + delta_x; + new_stack_proportion = rec->old_stack_proportion + delta_y; + } + + // 应用限制,确保比例在合理范围内 + new_scroller_proportion = + fmaxf(0.1f, fminf(1.0f, new_scroller_proportion)); + new_stack_proportion = fmaxf(0.1f, fminf(1.0f, new_stack_proportion)); + + rec->stack_proportion = new_stack_proportion; + + stack_head->scroller_proportion = new_scroller_proportion; + + if (!isdrag) { + arrange(rec->mon, false, false); + return; + } + + if (server.last_apply_drag_time == 0 || + time - server.last_apply_drag_time > drag_tile_refresh_interval) { + arrange(rec->mon, false, false); + server.last_apply_drag_time = time; + } + } +} + +void resize_tile_client(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time) { + + if (!rec || rec->isfullscreen || + rec->ismaximizescreen) + return; + + if (rec->mon->isoverview) + return; + + const Layout *current_layout = + rec->mon->pertag->ltidxs[rec->mon->pertag->curtag]; + if (current_layout->id == TILE || current_layout->id == DECK || + current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE || + (current_layout->id == TGMIX && + rec->mon->visible_tiling_clients <= 3) + + ) { + resize_tile_master_horizontal(rec, isdrag, offsetx, offsety, + time, current_layout->id); + } else if (current_layout->id == VERTICAL_TILE || + current_layout->id == VERTICAL_DECK) { + resize_tile_master_vertical(rec, isdrag, offsetx, offsety, + time, current_layout->id); + } else if (current_layout->id == SCROLLER) { + resize_tile_scroller(rec, isdrag, offsetx, offsety, time, + false); + } else if (current_layout->id == VERTICAL_SCROLLER) { + resize_tile_scroller(rec, isdrag, offsetx, offsety, time, + true); + } +} + +void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num, + double total_left_stack_hight_percent, + double total_right_stack_hight_percent, + double total_stack_hight_percent, + double total_master_inner_percent, int32_t master_num, + int32_t stack_num) { + Client *c = NULL; + int32_t i = 0; + uint32_t stack_index = 0; + uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag]; + + if (m->pertag->ltidxs[m->pertag->curtag]->id != CENTER_TILE) { + + wl_list_for_each(c, &server.clients, link) { + if (VISIBLEON(c, m) && ISTILED(c)) { + if (total_master_inner_percent > 0.0 && i < nmasters) { + c->ismaster = true; + c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f; + c->master_inner_per = + c->master_inner_per / total_master_inner_percent; + } else { + c->ismaster = false; + c->master_inner_per = + master_num > 0 ? 1.0f / master_num : 1.0f; + c->stack_inner_per = + total_stack_hight_percent + ? c->stack_inner_per / total_stack_hight_percent + : 1.0f; + } + i++; + } + } + } else { + wl_list_for_each(c, &server.clients, link) { + if (VISIBLEON(c, m) && ISTILED(c)) { + if (total_master_inner_percent > 0.0 && i < nmasters) { + c->ismaster = true; + if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) { + c->stack_inner_per = + stack_num > 1 ? 1.0f / ((stack_num - 1) / 2) : 1.0f; + + } else { + c->stack_inner_per = + stack_num > 1 ? 2.0f / stack_num : 1.0f; + } + + c->master_inner_per = + c->master_inner_per / total_master_inner_percent; + } else { + stack_index = i - nmasters; + + c->ismaster = false; + c->master_inner_per = + master_num > 0 ? 1.0f / master_num : 1.0f; + if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) { + c->stack_inner_per = + total_right_stack_hight_percent + ? c->stack_inner_per / + total_right_stack_hight_percent + : 1.0f; + } else { + c->stack_inner_per = + total_left_stack_hight_percent + ? c->stack_inner_per / + total_left_stack_hight_percent + : 1.0f; + } + } + i++; + } + } + } +} + +void resize_floating_window(Client *rec) { + int cdx = (int)round(server.cursor->x) - server.grabcx; + int cdy = (int)round(server.cursor->y) - server.grabcy; + + cdx = !(server.rzcorner & 1) && + rec->geom.width - 2 * (int)rec->bw - cdx < 1 + ? 0 + : cdx; + cdy = + !(server.rzcorner & 2) && + rec->geom.height - 2 * (int)rec->bw - cdy < 1 + ? 0 + : cdy; + + const struct wlr_box box = { + .x = rec->geom.x + (server.rzcorner & 1 ? 0 : cdx), + .y = rec->geom.y + (server.rzcorner & 2 ? 0 : cdy), + .width = rec->geom.width + (server.rzcorner & 1 ? cdx : -cdx), + .height = + rec->geom.height + (server.rzcorner & 2 ? cdy : -cdy)}; + + rec->float_geom = box; + + resize(rec, box, 1); + server.grabcx += cdx; + server.grabcy += cdy; +} + +void // 17 +arrange(Monitor *m, bool want_animation, bool from_view) { + Client *c = NULL; + double total_stack_inner_percent = 0; + double total_master_inner_percent = 0; + double total_right_stack_hight_percent = 0; + double total_left_stack_hight_percent = 0; + int32_t i = 0; + int32_t nmasters = 0; + int32_t stack_index = 0; + int32_t master_num = 0; + int32_t stack_num = 0; + + if (!m) + return; + + if (!m->wlr_output->enabled) + return; + m->visible_clients = 0; + m->visible_tiling_clients = 0; + m->visible_scroll_tiling_clients = 0; + + wl_list_for_each(c, &server.clients, link) { + + if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) { + exit_scroller_stack(c); + } + + if (from_view && (c->isglobal || c->isunglobal)) { + set_size_per(m, c); + } + + if (c->mon == m && (c->isglobal || c->isunglobal)) { + c->tags = m->tagset[m->seltags]; + } + + if (from_view && m->sel == NULL && c->isglobal && VISIBLEON(c, m)) { + focusclient(c, 1); + } + + if (VISIBLEON(c, m)) { + if (from_view && !client_only_in_one_tag(c)) { + set_size_per(m, c); + } + + if (!c->isunglobal) + m->visible_clients++; + + if (ISTILED(c)) { + m->visible_tiling_clients++; + } + + if (ISSCROLLTILED(c) && !c->prev_in_stack) { + m->visible_scroll_tiling_clients++; + } + } + } + + nmasters = m->pertag->nmasters[m->pertag->curtag]; + + wl_list_for_each(c, &server.clients, link) { + if (c->iskilling) + continue; + + if (c->mon == m) { + if (VISIBLEON(c, m)) { + if (ISTILED(c)) { + + if (i < nmasters) { + master_num++; + total_master_inner_percent += c->master_inner_per; + } else { + stack_num++; + total_stack_inner_percent += c->stack_inner_per; + stack_index = i - nmasters; + if ((stack_index % 2) ^ + (m->visible_tiling_clients % 2 == 0)) { + c->isleftstack = false; + total_right_stack_hight_percent += + c->stack_inner_per; + } else { + c->isleftstack = true; + total_left_stack_hight_percent += + c->stack_inner_per; + } + } + + i++; + } + + set_arrange_visible(m, c, want_animation); + } else { + set_arrange_hidden(m, c, want_animation); + } + } + + if (c->mon == m && c->ismaximizescreen && !c->animation.tagouted && + !c->animation.tagouting && VISIBLEON(c, m)) { + reset_maximizescreen_size(c); + } + } + + reset_size_per_mon( + m, m->visible_tiling_clients, total_left_stack_hight_percent, + total_right_stack_hight_percent, total_stack_inner_percent, + total_master_inner_percent, master_num, stack_num); + + if (m->isoverview) { + overviewlayout.arrange(m); + } else { + m->pertag->ltidxs[m->pertag->curtag]->arrange(m); + } + + if (!server.start_drag_window) { + motionnotify(0, NULL, 0, 0, 0, 0); + checkidleinhibitor(NULL); + } + + printstatus(); +} diff --git a/src/layout/arrange.h b/src/layout/arrange.h index 5a8d318..eb7d148 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -1,836 +1,37 @@ -void set_size_per(Monitor *m, Client *c) { - Client *fc = NULL; - bool found = false; +#ifndef TILE_RESIZE_H +#define TILE_RESIZE_H - if (!m || !c) - return; +/* 平铺窗口大小比例设置 */ +void set_size_per(Monitor *m, Client *c); - wl_list_for_each(fc, &server.clients, link) { - if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) { - c->master_mfact_per = fc->master_mfact_per; - c->master_inner_per = fc->master_inner_per; - c->stack_inner_per = fc->stack_inner_per; - found = true; - break; - } - } +/* 水平布局平铺窗口调整 */ +void resize_tile_master_horizontal(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, int32_t type); - if (!found) { - c->master_mfact_per = m->pertag->mfacts[m->pertag->curtag]; - c->master_inner_per = 1.0f; - c->stack_inner_per = 1.0f; - } -} +/* 垂直布局平铺窗口调整 */ +void resize_tile_master_vertical(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, int32_t type); -void resize_tile_master_horizontal(Client *rec, bool isdrag, int32_t offsetx, - int32_t offsety, uint32_t time, - int32_t type) { - Client *tc = NULL; - float delta_x, delta_y; - Client *next = NULL; - Client *prev = NULL; - Client *nextnext = NULL; - Client *prevprev = NULL; - struct wl_list *node; - bool begin_find_nextnext = false; - bool begin_find_prevprev = false; +/* 滚动布局平铺窗口调整 */ +void resize_tile_scroller(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, bool isvertical); - // 从当前节点的下一个开始遍历 - for (node = rec->link.next; node != &server.clients; - node = node->next) { - tc = wl_container_of(node, tc, link); - if (begin_find_nextnext && VISIBLEON(tc, rec->mon) && - ISTILED(tc)) { - nextnext = tc; - break; - } - - if (!begin_find_nextnext && VISIBLEON(tc, rec->mon) && - ISTILED(tc)) { // 根据你的实际字段名调整 - next = tc; - begin_find_nextnext = true; - continue; - } - } - - // 从当前节点的上一个开始遍历 - for (node = rec->link.prev; node != &server.clients; - node = node->prev) { - tc = wl_container_of(node, tc, link); - - if (begin_find_prevprev && VISIBLEON(tc, rec->mon) && - ISTILED(tc)) { - prevprev = tc; - break; - } - - if (!begin_find_prevprev && VISIBLEON(tc, rec->mon) && - ISTILED(tc)) { // 根据你的实际字段名调整 - prev = tc; - begin_find_prevprev = true; - continue; - } - } - - if (!server.start_drag_window && isdrag) { - server.drag_begin_cursorx = server.cursor->x; - server.drag_begin_cursory = server.cursor->y; - server.start_drag_window = true; - // 记录初始状态 - rec->old_master_mfact_per = rec->master_mfact_per; - rec->old_master_inner_per = rec->master_inner_per; - rec->old_stack_inner_per = rec->stack_inner_per; - rec->cursor_in_upper_half = - server.cursor->y < - rec->geom.y + rec->geom.height / 2; - rec->cursor_in_left_half = - server.cursor->x < - rec->geom.x + rec->geom.width / 2; - // 记录初始几何信息 - rec->drag_begin_geom = rec->geom; - } else { - // 计算相对于屏幕尺寸的比例变化 - if (isdrag) { - - offsetx = server.cursor->x - server.drag_begin_cursorx; - offsety = server.cursor->y - server.drag_begin_cursory; - } else { - rec->old_master_mfact_per = rec->master_mfact_per; - rec->old_master_inner_per = rec->master_inner_per; - rec->old_stack_inner_per = rec->stack_inner_per; - rec->drag_begin_geom = rec->geom; - rec->cursor_in_upper_half = true; - rec->cursor_in_left_half = false; - } - - if (rec->ismaster) { - delta_x = (float)(offsetx) * (rec->old_master_mfact_per) / - rec->drag_begin_geom.width; - delta_y = (float)(offsety) * (rec->old_master_inner_per) / - rec->drag_begin_geom.height; - } else { - delta_x = (float)(offsetx) * - (1 - rec->old_master_mfact_per) / - rec->drag_begin_geom.width; - delta_y = (float)(offsety) * (rec->old_stack_inner_per) / - rec->drag_begin_geom.height; - } - bool moving_up; - bool moving_down; - - if (!isdrag) { - moving_up = offsety < 0 ? true : false; - moving_down = offsety > 0 ? true : false; - } else { - moving_up = server.cursor->y < server.drag_begin_cursory; - moving_down = server.cursor->y > server.drag_begin_cursory; - } - - if (rec->ismaster && !prev) { - if (moving_up) { - delta_y = -fabsf(delta_y); - } else { - delta_y = fabsf(delta_y); - } - } else if (rec->ismaster && next && !next->ismaster) { - if (moving_up) { - delta_y = fabsf(delta_y); - } else { - delta_y = -fabsf(delta_y); - } - } else if (!rec->ismaster && prev && prev->ismaster) { - if (moving_up) { - delta_y = -fabsf(delta_y); - } else { - delta_y = fabsf(delta_y); - } - } else if (!rec->ismaster && !next) { - if (moving_up) { - delta_y = fabsf(delta_y); - } else { - delta_y = -fabsf(delta_y); - } - } else if (type == CENTER_TILE && !rec->ismaster && - !nextnext) { - if (moving_up) { - delta_y = fabsf(delta_y); - } else { - delta_y = -fabsf(delta_y); - } - } else if (type == CENTER_TILE && !rec->ismaster && prevprev && - prevprev->ismaster) { - if (moving_up) { - delta_y = -fabsf(delta_y); - } else { - delta_y = fabsf(delta_y); - } - } else if ((rec->cursor_in_upper_half && moving_up) || - (!rec->cursor_in_upper_half && moving_down)) { - // 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度 - delta_y = fabsf(delta_y); - delta_y = delta_y * 2; - } else { - // 其他情况 → 减小高度 - delta_y = -fabsf(delta_y); - delta_y = delta_y * 2; - } - - if (!rec->ismaster && rec->isleftstack && - type == CENTER_TILE) { - delta_x = delta_x * -1.0f; - } - - if (rec->ismaster && type == CENTER_TILE && - rec->cursor_in_left_half) { - delta_x = delta_x * -1.0f; - } - - if (rec->ismaster && type == CENTER_TILE) { - delta_x = delta_x * 2; - } - - if (type == RIGHT_TILE) { - delta_x = delta_x * -1.0f; - } - - // 直接设置新的比例,基于初始值 + 变化量 - float new_master_mfact_per = - rec->old_master_mfact_per + delta_x; - float new_master_inner_per = - rec->old_master_inner_per + delta_y; - float new_stack_inner_per = rec->old_stack_inner_per + delta_y; - - // 应用限制,确保比例在合理范围内 - new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per)); - new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per)); - new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per)); - - // 应用到所有平铺窗口 - wl_list_for_each(tc, &server.clients, link) { - if (VISIBLEON(tc, rec->mon) && ISTILED(tc)) { - tc->master_mfact_per = new_master_mfact_per; - } - } - - rec->master_inner_per = new_master_inner_per; - rec->stack_inner_per = new_stack_inner_per; - - if (!isdrag) { - arrange(rec->mon, false, false); - return; - } - - if (server.last_apply_drag_time == 0 || - time - server.last_apply_drag_time > drag_tile_refresh_interval) { - arrange(rec->mon, false, false); - server.last_apply_drag_time = time; - } - } -} - -void resize_tile_master_vertical(Client *rec, bool isdrag, int32_t offsetx, - int32_t offsety, uint32_t time, int32_t type) { - Client *tc = NULL; - float delta_x, delta_y; - Client *next = NULL; - Client *prev = NULL; - struct wl_list *node; - - // 从当前节点的下一个开始遍历 - for (node = rec->link.next; node != &server.clients; - node = node->next) { - tc = wl_container_of(node, tc, link); - - if (VISIBLEON(tc, rec->mon) && - ISTILED(tc)) { // 根据你的实际字段名调整 - next = tc; - break; - } - } - - // 从当前节点的上一个开始遍历 - for (node = rec->link.prev; node != &server.clients; - node = node->prev) { - tc = wl_container_of(node, tc, link); - - if (VISIBLEON(tc, rec->mon) && - ISTILED(tc)) { // 根据你的实际字段名调整 - prev = tc; - break; - } - } - - if (!server.start_drag_window && isdrag) { - server.drag_begin_cursorx = server.cursor->x; - server.drag_begin_cursory = server.cursor->y; - server.start_drag_window = true; - - // 记录初始状态 - rec->old_master_mfact_per = rec->master_mfact_per; - rec->old_master_inner_per = rec->master_inner_per; - rec->old_stack_inner_per = rec->stack_inner_per; - rec->cursor_in_upper_half = - server.cursor->y < - rec->geom.y + rec->geom.height / 2; - rec->cursor_in_left_half = - server.cursor->x < - rec->geom.x + rec->geom.width / 2; - // 记录初始几何信息 - rec->drag_begin_geom = rec->geom; - } else { - // 计算相对于屏幕尺寸的比例变化 - // 计算相对于屏幕尺寸的比例变化 - if (isdrag) { - - offsetx = server.cursor->x - server.drag_begin_cursorx; - offsety = server.cursor->y - server.drag_begin_cursory; - } else { - rec->old_master_mfact_per = rec->master_mfact_per; - rec->old_master_inner_per = rec->master_inner_per; - rec->old_stack_inner_per = rec->stack_inner_per; - rec->drag_begin_geom = rec->geom; - rec->cursor_in_upper_half = true; - rec->cursor_in_left_half = false; - } - - if (rec->ismaster) { - // 垂直版本:左右移动调整高度比例,上下移动调整宽度比例 - delta_x = (float)(offsetx) * (rec->old_master_inner_per) / - rec->drag_begin_geom.width; - delta_y = (float)(offsety) * (rec->old_master_mfact_per) / - rec->drag_begin_geom.height; - } else { - delta_x = (float)(offsetx) * (rec->old_stack_inner_per) / - rec->drag_begin_geom.width; - delta_y = (float)(offsety) * - (1 - rec->old_master_mfact_per) / - rec->drag_begin_geom.height; - } - - bool moving_left; - bool moving_right; - - if (!isdrag) { - moving_left = offsetx < 0 ? true : false; - moving_right = offsetx > 0 ? true : false; - } else { - moving_left = server.cursor->x < server.drag_begin_cursorx; - moving_right = server.cursor->x > server.drag_begin_cursorx; - } - - // 调整主区域和栈区域的高度比例(垂直分割) - if (rec->ismaster && !prev) { - if (moving_left) { - delta_x = -fabsf(delta_x); // 向上移动减少主区域高度 - } else { - delta_x = fabsf(delta_x); // 向下移动增加主区域高度 - } - } else if (rec->ismaster && next && !next->ismaster) { - if (moving_left) { - delta_x = fabsf(delta_x); // 向上移动增加主区域高度 - } else { - delta_x = -fabsf(delta_x); // 向下移动减少主区域高度 - } - } else if (!rec->ismaster && prev && prev->ismaster) { - if (moving_left) { - delta_x = -fabsf(delta_x); // 向上移动减少栈区域高度 - } else { - delta_x = fabsf(delta_x); // 向下移动增加栈区域高度 - } - } else if (!rec->ismaster && !next) { - if (moving_left) { - delta_x = fabsf(delta_x); // 向上移动增加栈区域高度 - } else { - delta_x = -fabsf(delta_x); // 向下移动减少栈区域高度 - } - } else if ((rec->cursor_in_left_half && moving_left) || - (!rec->cursor_in_left_half && moving_right)) { - // 光标在窗口左侧且向左移动,或在窗口右侧且向右移动 → 增加宽度 - delta_x = fabsf(delta_x); - delta_x = delta_x * 2; - } else { - // 其他情况 → 减小宽度 - delta_x = -fabsf(delta_x); - delta_x = delta_x * 2; - } - - // 直接设置新的比例,基于初始值 + 变化量 - float new_master_mfact_per = rec->old_master_mfact_per + - delta_y; // 垂直:delta_y调整主区域高度 - float new_master_inner_per = rec->old_master_inner_per + - delta_x; // 垂直:delta_x调整主区域内部宽度 - float new_stack_inner_per = rec->old_stack_inner_per + - delta_x; // 垂直:delta_x调整栈区域内部宽度 - - // 应用限制,确保比例在合理范围内 - new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per)); - new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per)); - new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per)); - - // 应用到所有平铺窗口 - wl_list_for_each(tc, &server.clients, link) { - if (VISIBLEON(tc, rec->mon) && ISTILED(tc)) { - tc->master_mfact_per = new_master_mfact_per; - } - } - - rec->master_inner_per = new_master_inner_per; - rec->stack_inner_per = new_stack_inner_per; - - if (!isdrag) { - arrange(rec->mon, false, false); - return; - } - - if (server.last_apply_drag_time == 0 || - time - server.last_apply_drag_time > drag_tile_refresh_interval) { - arrange(rec->mon, false, false); - server.last_apply_drag_time = time; - } - } -} - -void resize_tile_scroller(Client *rec, bool isdrag, int32_t offsetx, - int32_t offsety, uint32_t time, bool isvertical) { - float delta_x, delta_y; - float new_scroller_proportion; - float new_stack_proportion; - Client *stack_head = get_scroll_stack_head(rec); - - if (rec && rec->mon->visible_tiling_clients == 1 && - !scroller_ignore_proportion_single) - return; - - if (!server.start_drag_window && isdrag) { - server.drag_begin_cursorx = server.cursor->x; - server.drag_begin_cursory = server.cursor->y; - server.start_drag_window = true; - - // 记录初始状态 - stack_head->old_scroller_pproportion = stack_head->scroller_proportion; - rec->old_stack_proportion = rec->stack_proportion; - - rec->cursor_in_left_half = - server.cursor->x < - rec->geom.x + rec->geom.width / 2; - rec->cursor_in_upper_half = - server.cursor->y < - rec->geom.y + rec->geom.height / 2; - // 记录初始几何信息 - rec->drag_begin_geom = rec->geom; - } else { - // 计算相对于屏幕尺寸的比例变化 - // 计算相对于屏幕尺寸的比例变化 - if (isdrag) { - - offsetx = server.cursor->x - server.drag_begin_cursorx; - offsety = server.cursor->y - server.drag_begin_cursory; - } else { - rec->old_master_mfact_per = rec->master_mfact_per; - rec->old_master_inner_per = rec->master_inner_per; - rec->old_stack_inner_per = rec->stack_inner_per; - rec->drag_begin_geom = rec->geom; - stack_head->old_scroller_pproportion = - stack_head->scroller_proportion; - rec->old_stack_proportion = rec->stack_proportion; - rec->cursor_in_upper_half = false; - rec->cursor_in_left_half = false; - } - - if (isvertical) { - delta_y = (float)(offsety) * - (stack_head->old_scroller_pproportion) / - rec->drag_begin_geom.height; - delta_x = (float)(offsetx) * (rec->old_stack_proportion) / - rec->drag_begin_geom.width; - } else { - delta_x = (float)(offsetx) * - (stack_head->old_scroller_pproportion) / - rec->drag_begin_geom.width; - delta_y = (float)(offsety) * (rec->old_stack_proportion) / - rec->drag_begin_geom.height; - } - - bool moving_up; - bool moving_down; - bool moving_left; - bool moving_right; - - if (!isdrag) { - moving_up = offsety < 0 ? true : false; - moving_down = offsety > 0 ? true : false; - moving_left = offsetx < 0 ? true : false; - moving_right = offsetx > 0 ? true : false; - } else { - moving_up = server.cursor->y < server.drag_begin_cursory; - moving_down = server.cursor->y > server.drag_begin_cursory; - moving_left = server.cursor->x < server.drag_begin_cursorx; - moving_right = server.cursor->x > server.drag_begin_cursorx; - } - - if ((rec->cursor_in_upper_half && moving_up) || - (!rec->cursor_in_upper_half && moving_down)) { - // 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度 - delta_y = fabsf(delta_y); - } else { - // 其他情况 → 减小高度 - delta_y = -fabsf(delta_y); - } - - if ((rec->cursor_in_left_half && moving_left) || - (!rec->cursor_in_left_half && moving_right)) { - delta_x = fabsf(delta_x); - } else { - delta_x = -fabsf(delta_x); - } - - if (isvertical) { - if (!rec->next_in_stack && rec->prev_in_stack && - !isdrag) { - delta_x = delta_x * -1.0f; - } - if (!rec->next_in_stack && rec->prev_in_stack && - isdrag) { - if (moving_right) { - delta_x = -fabsf(delta_x); - } else { - delta_x = fabsf(delta_x); - } - } - if (!rec->prev_in_stack && rec->next_in_stack && - isdrag) { - if (moving_left) { - delta_x = -fabsf(delta_x); - } else { - delta_x = fabsf(delta_x); - } - } - - if (isdrag) { - if (moving_up) { - delta_y = -fabsf(delta_y); - } else { - delta_y = fabsf(delta_y); - } - } - - } else { - if (!rec->next_in_stack && rec->prev_in_stack && - !isdrag) { - delta_y = delta_y * -1.0f; - } - if (!rec->next_in_stack && rec->prev_in_stack && - isdrag) { - if (moving_down) { - delta_y = -fabsf(delta_y); - } else { - delta_y = fabsf(delta_y); - } - } - if (!rec->prev_in_stack && rec->next_in_stack && - isdrag) { - if (moving_up) { - delta_y = -fabsf(delta_y); - } else { - delta_y = fabsf(delta_y); - } - } - - if (isdrag) { - if (moving_left) { - delta_x = -fabsf(delta_x); - } else { - delta_x = fabsf(delta_x); - } - } - } - - // 直接设置新的比例,基于初始值 + 变化量 - if (isvertical) { - new_scroller_proportion = - stack_head->old_scroller_pproportion + delta_y; - new_stack_proportion = rec->old_stack_proportion + delta_x; - - } else { - new_scroller_proportion = - stack_head->old_scroller_pproportion + delta_x; - new_stack_proportion = rec->old_stack_proportion + delta_y; - } - - // 应用限制,确保比例在合理范围内 - new_scroller_proportion = - fmaxf(0.1f, fminf(1.0f, new_scroller_proportion)); - new_stack_proportion = fmaxf(0.1f, fminf(1.0f, new_stack_proportion)); - - rec->stack_proportion = new_stack_proportion; - - stack_head->scroller_proportion = new_scroller_proportion; - - if (!isdrag) { - arrange(rec->mon, false, false); - return; - } - - if (server.last_apply_drag_time == 0 || - time - server.last_apply_drag_time > drag_tile_refresh_interval) { - arrange(rec->mon, false, false); - server.last_apply_drag_time = time; - } - } -} - -void resize_tile_client(Client *rec, bool isdrag, int32_t offsetx, - int32_t offsety, uint32_t time) { - - if (!rec || rec->isfullscreen || - rec->ismaximizescreen) - return; - - if (rec->mon->isoverview) - return; - - const Layout *current_layout = - rec->mon->pertag->ltidxs[rec->mon->pertag->curtag]; - if (current_layout->id == TILE || current_layout->id == DECK || - current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE || - (current_layout->id == TGMIX && - rec->mon->visible_tiling_clients <= 3) - - ) { - resize_tile_master_horizontal(rec, isdrag, offsetx, offsety, - time, current_layout->id); - } else if (current_layout->id == VERTICAL_TILE || - current_layout->id == VERTICAL_DECK) { - resize_tile_master_vertical(rec, isdrag, offsetx, offsety, - time, current_layout->id); - } else if (current_layout->id == SCROLLER) { - resize_tile_scroller(rec, isdrag, offsetx, offsety, time, - false); - } else if (current_layout->id == VERTICAL_SCROLLER) { - resize_tile_scroller(rec, isdrag, offsetx, offsety, time, - true); - } -} +/* 通用平铺窗口调整入口函数 */ +void resize_tile_client(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time); +/* 重置显示器上所有平铺窗口的比例 */ void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num, - double total_left_stack_hight_percent, - double total_right_stack_hight_percent, - double total_stack_hight_percent, - double total_master_inner_percent, int32_t master_num, - int32_t stack_num) { - Client *c = NULL; - int32_t i = 0; - uint32_t stack_index = 0; - uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag]; + double total_left_stack_hight_percent, + double total_right_stack_hight_percent, + double total_stack_hight_percent, + double total_master_inner_percent, int32_t master_num, + int32_t stack_num); - if (m->pertag->ltidxs[m->pertag->curtag]->id != CENTER_TILE) { +/* 浮动窗口调整大小 */ +void resize_floating_window(Client *rec); - wl_list_for_each(c, &server.clients, link) { - if (VISIBLEON(c, m) && ISTILED(c)) { - if (total_master_inner_percent > 0.0 && i < nmasters) { - c->ismaster = true; - c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f; - c->master_inner_per = - c->master_inner_per / total_master_inner_percent; - } else { - c->ismaster = false; - c->master_inner_per = - master_num > 0 ? 1.0f / master_num : 1.0f; - c->stack_inner_per = - total_stack_hight_percent - ? c->stack_inner_per / total_stack_hight_percent - : 1.0f; - } - i++; - } - } - } else { - wl_list_for_each(c, &server.clients, link) { - if (VISIBLEON(c, m) && ISTILED(c)) { - if (total_master_inner_percent > 0.0 && i < nmasters) { - c->ismaster = true; - if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) { - c->stack_inner_per = - stack_num > 1 ? 1.0f / ((stack_num - 1) / 2) : 1.0f; +/* 排列显示器上的所有窗口 */ +void arrange(Monitor *m, bool want_animation, bool from_view); - } else { - c->stack_inner_per = - stack_num > 1 ? 2.0f / stack_num : 1.0f; - } - - c->master_inner_per = - c->master_inner_per / total_master_inner_percent; - } else { - stack_index = i - nmasters; - - c->ismaster = false; - c->master_inner_per = - master_num > 0 ? 1.0f / master_num : 1.0f; - if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) { - c->stack_inner_per = - total_right_stack_hight_percent - ? c->stack_inner_per / - total_right_stack_hight_percent - : 1.0f; - } else { - c->stack_inner_per = - total_left_stack_hight_percent - ? c->stack_inner_per / - total_left_stack_hight_percent - : 1.0f; - } - } - i++; - } - } - } -} - -void resize_floating_window(Client *rec) { - int cdx = (int)round(server.cursor->x) - server.grabcx; - int cdy = (int)round(server.cursor->y) - server.grabcy; - - cdx = !(server.rzcorner & 1) && - rec->geom.width - 2 * (int)rec->bw - cdx < 1 - ? 0 - : cdx; - cdy = - !(server.rzcorner & 2) && - rec->geom.height - 2 * (int)rec->bw - cdy < 1 - ? 0 - : cdy; - - const struct wlr_box box = { - .x = rec->geom.x + (server.rzcorner & 1 ? 0 : cdx), - .y = rec->geom.y + (server.rzcorner & 2 ? 0 : cdy), - .width = rec->geom.width + (server.rzcorner & 1 ? cdx : -cdx), - .height = - rec->geom.height + (server.rzcorner & 2 ? cdy : -cdy)}; - - rec->float_geom = box; - - resize(rec, box, 1); - server.grabcx += cdx; - server.grabcy += cdy; -} - -void // 17 -arrange(Monitor *m, bool want_animation, bool from_view) { - Client *c = NULL; - double total_stack_inner_percent = 0; - double total_master_inner_percent = 0; - double total_right_stack_hight_percent = 0; - double total_left_stack_hight_percent = 0; - int32_t i = 0; - int32_t nmasters = 0; - int32_t stack_index = 0; - int32_t master_num = 0; - int32_t stack_num = 0; - - if (!m) - return; - - if (!m->wlr_output->enabled) - return; - m->visible_clients = 0; - m->visible_tiling_clients = 0; - m->visible_scroll_tiling_clients = 0; - - wl_list_for_each(c, &server.clients, link) { - - if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) { - exit_scroller_stack(c); - } - - if (from_view && (c->isglobal || c->isunglobal)) { - set_size_per(m, c); - } - - if (c->mon == m && (c->isglobal || c->isunglobal)) { - c->tags = m->tagset[m->seltags]; - } - - if (from_view && m->sel == NULL && c->isglobal && VISIBLEON(c, m)) { - focusclient(c, 1); - } - - if (VISIBLEON(c, m)) { - if (from_view && !client_only_in_one_tag(c)) { - set_size_per(m, c); - } - - if (!c->isunglobal) - m->visible_clients++; - - if (ISTILED(c)) { - m->visible_tiling_clients++; - } - - if (ISSCROLLTILED(c) && !c->prev_in_stack) { - m->visible_scroll_tiling_clients++; - } - } - } - - nmasters = m->pertag->nmasters[m->pertag->curtag]; - - wl_list_for_each(c, &server.clients, link) { - if (c->iskilling) - continue; - - if (c->mon == m) { - if (VISIBLEON(c, m)) { - if (ISTILED(c)) { - - if (i < nmasters) { - master_num++; - total_master_inner_percent += c->master_inner_per; - } else { - stack_num++; - total_stack_inner_percent += c->stack_inner_per; - stack_index = i - nmasters; - if ((stack_index % 2) ^ - (m->visible_tiling_clients % 2 == 0)) { - c->isleftstack = false; - total_right_stack_hight_percent += - c->stack_inner_per; - } else { - c->isleftstack = true; - total_left_stack_hight_percent += - c->stack_inner_per; - } - } - - i++; - } - - set_arrange_visible(m, c, want_animation); - } else { - set_arrange_hidden(m, c, want_animation); - } - } - - if (c->mon == m && c->ismaximizescreen && !c->animation.tagouted && - !c->animation.tagouting && VISIBLEON(c, m)) { - reset_maximizescreen_size(c); - } - } - - reset_size_per_mon( - m, m->visible_tiling_clients, total_left_stack_hight_percent, - total_right_stack_hight_percent, total_stack_inner_percent, - total_master_inner_percent, master_num, stack_num); - - if (m->isoverview) { - overviewlayout.arrange(m); - } else { - m->pertag->ltidxs[m->pertag->curtag]->arrange(m); - } - - if (!server.start_drag_window) { - motionnotify(0, NULL, 0, 0, 0, 0); - checkidleinhibitor(NULL); - } - - printstatus(); -} +#endif // TILE_RESIZE_H \ No newline at end of file diff --git a/src/layout/layout.c b/src/layout/layout.c new file mode 100644 index 0000000..0df7576 --- /dev/null +++ b/src/layout/layout.c @@ -0,0 +1,26 @@ + +#include "../type.h" +#include "layout.h" + +Layout overviewlayout = {"󰃇", overview, "overview"}; + + +Layout layouts[] = { + // 最少两个,不能删除少于两个 + /* symbol arrange function name */ + {"T", tile, "tile", TILE}, // 平铺布局 + {"S", scroller, "scroller", SCROLLER}, // 滚动布局 + {"G", grid, "grid", GRID}, // 格子布局 + {"M", monocle, "monocle", MONOCLE}, // 单屏布局 + {"K", deck, "deck", DECK}, // 卡片布局 + {"CT", center_tile, "center_tile", CENTER_TILE}, // 居中布局 + {"RT", right_tile, "right_tile", RIGHT_TILE}, // 右布局 + {"VS", vertical_scroller, "vertical_scroller", + VERTICAL_SCROLLER}, // 垂直滚动布局 + {"VT", vertical_tile, "vertical_tile", VERTICAL_TILE}, // 垂直平铺布局 + {"VG", vertical_grid, "vertical_grid", VERTICAL_GRID}, // 垂直格子布局 + {"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局 + {"TG", tgmix, "tgmix", TGMIX}, // 混合布局 +}; + +const uint32_t layouts_len = sizeof(layouts) / sizeof(layouts[0]); \ No newline at end of file diff --git a/src/layout/layout.h b/src/layout/layout.h index f896ac2..063940a 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -1,20 +1,17 @@ -static void tile(Monitor *m); -static void center_tile(Monitor *m); -static void right_tile(Monitor *m); -static void overview(Monitor *m); -static void grid(Monitor *m); -static void scroller(Monitor *m); -static void deck(Monitor *mon); -static void monocle(Monitor *m); -static void vertical_tile(Monitor *m); -static void vertical_overview(Monitor *m); -static void vertical_grid(Monitor *m); -static void vertical_scroller(Monitor *m); -static void vertical_deck(Monitor *mon); -static void tgmix(Monitor *m); - -/* layout(s) */ -Layout overviewlayout = {"󰃇", overview, "overview"}; +void tile(Monitor *m); +void center_tile(Monitor *m); +void right_tile(Monitor *m); +void overview(Monitor *m); +void grid(Monitor *m); +void scroller(Monitor *m); +void deck(Monitor *mon); +void monocle(Monitor *m); +void vertical_tile(Monitor *m); +void vertical_overview(Monitor *m); +void vertical_grid(Monitor *m); +void vertical_scroller(Monitor *m); +void vertical_deck(Monitor *mon); +void tgmix(Monitor *m); enum { TILE, @@ -29,22 +26,4 @@ enum { VERTICAL_DECK, RIGHT_TILE, TGMIX, -}; - -Layout layouts[] = { - // 最少两个,不能删除少于两个 - /* symbol arrange function name */ - {"T", tile, "tile", TILE}, // 平铺布局 - {"S", scroller, "scroller", SCROLLER}, // 滚动布局 - {"G", grid, "grid", GRID}, // 格子布局 - {"M", monocle, "monocle", MONOCLE}, // 单屏布局 - {"K", deck, "deck", DECK}, // 卡片布局 - {"CT", center_tile, "center_tile", CENTER_TILE}, // 居中布局 - {"RT", right_tile, "right_tile", RIGHT_TILE}, // 右布局 - {"VS", vertical_scroller, "vertical_scroller", - VERTICAL_SCROLLER}, // 垂直滚动布局 - {"VT", vertical_tile, "vertical_tile", VERTICAL_TILE}, // 垂直平铺布局 - {"VG", vertical_grid, "vertical_grid", VERTICAL_GRID}, // 垂直格子布局 - {"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局 - {"TG", tgmix, "tgmix", TGMIX}, // 混合布局 }; \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index b5663c7..9aec011 100644 --- a/src/mango.c +++ b/src/mango.c @@ -1,980 +1,74 @@ -/* - * See LICENSE file for copyright and license details. - */ -#include "wlr-layer-shell-unstable-v1-protocol.h" -#include "wlr/util/box.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef XWAYLAND -#include -#include -#include -#endif -#include "common/util.h" -/* macros */ -#define MAX(A, B) ((A) > (B) ? (A) : (B)) -#define MIN(A, B) ((A) < (B) ? (A) : (B)) -#define GEZERO(A) ((A) >= 0 ? (A) : 0) -#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) -#define INSIDEMON(A) \ - (A->geom.x >= A->mon->m.x && A->geom.y >= A->mon->m.y && \ - A->geom.x + A->geom.width <= A->mon->m.x + A->mon->m.width && \ - A->geom.y + A->geom.height <= A->mon->m.y + A->mon->m.height) -#define GEOMINSIDEMON(A, M) \ - (A->x >= M->m.x && A->y >= M->m.y && \ - A->x + A->width <= M->m.x + M->m.width && \ - A->y + A->height <= M->m.y + M->m.height) -#define ISTILED(A) \ - (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ - !(A)->ismaximizescreen && !(A)->isfullscreen && !(A)->isunglobal) -#define ISSCROLLTILED(A) \ - (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ - !(A)->isunglobal) -#define VISIBLEON(C, M) \ - ((C) && (M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) -#define LENGTH(X) (sizeof X / sizeof X[0]) -#define END(A) ((A) + LENGTH(A)) -#define TAGMASK ((1 << LENGTH(tags)) - 1) -#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) -#define ISFULLSCREEN(A) \ - ((A)->isfullscreen || (A)->ismaximizescreen || \ - (A)->overview_ismaximizescreenbak || (A)->overview_isfullscreenbak) -#define LISTEN_STATIC(E, H) \ - do { \ - struct wl_listener *_l = ecalloc(1, sizeof(*_l)); \ - _l->notify = (H); \ - wl_signal_add((E), _l); \ - } while (0) +#include "type.h" +#include "dispatch/bind_define.h" +#include "layout/layout.h" +#include "config/parse_config.h" -#define APPLY_INT_PROP(obj, rule, prop) \ - if (rule->prop >= 0) \ - obj->prop = rule->prop - -#define APPLY_FLOAT_PROP(obj, rule, prop) \ - if (rule->prop > 0.0f) \ - obj->prop = rule->prop - -#define APPLY_STRING_PROP(obj, rule, prop) \ - if (rule->prop != NULL) \ - obj->prop = rule->prop - -#define BAKED_POINTS_COUNT 256 - -/* enums */ -enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; - -enum { VERTICAL, HORIZONTAL }; -enum { SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT }; -enum { CurNormal, CurPressed, CurMove, CurResize }; /* server.cursor */ -enum { XDGShell, LayerShell, X11 }; /* client types */ -enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 -enum { - LyrBg, - LyrBlur, - LyrBottom, - LyrTile, - LyrTop, - LyrFadeOut, - LyrOverlay, - LyrIMPopup, // text-input layer - LyrBlock, - NUM_LAYERS -}; /* server.scene server.layers */ -#ifdef XWAYLAND -enum { - NetWMWindowTypeDialog, - NetWMWindowTypeSplash, - NetWMWindowTypeToolbar, - NetWMWindowTypeUtility, - NetLast -}; /* EWMH atoms */ -#endif -enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */ -enum { NONE, OPEN, MOVE, CLOSE, TAG, FOCUS, OPAFADEIN, OPAFADEOUT }; -enum { UNFOLD, FOLD, INVALIDFOLD }; -enum { PREV, NEXT }; -enum { STATE_UNSPECIFIED = 0, STATE_ENABLED, STATE_DISABLED }; - -enum tearing_mode { - TEARING_DISABLED = 0, - TEARING_ENABLED, - TEARING_FULLSCREEN_ONLY, -}; - -enum seat_config_shortcuts_inhibit { - SHORTCUTS_INHIBIT_DISABLE, - SHORTCUTS_INHIBIT_ENABLE, -}; - -// 事件掩码枚举 -enum print_event_type { - PRINT_ACTIVE = 1 << 0, - PRINT_TAG = 1 << 1, - PRINT_LAYOUT = 1 << 2, - PRINT_TITLE = 1 << 3, - PRINT_APPID = 1 << 4, - PRINT_LAYOUT_SYMBOL = 1 << 5, - PRINT_FULLSCREEN = 1 << 6, - PRINT_FLOATING = 1 << 7, - PRINT_X = 1 << 8, - PRINT_Y = 1 << 9, - PRINT_WIDTH = 1 << 10, - PRINT_HEIGHT = 1 << 11, - PRINT_LAST_LAYER = 1 << 12, - PRINT_KB_LAYOUT = 1 << 13, - PRINT_KEYMODE = 1 << 14, - PRINT_SCALEFACTOR = 1 << 15, - PRINT_FRAME = 1 << 16, - PRINT_ALL = (1 << 17) - 1 // 所有位都设为1 -}; - -typedef struct Pertag Pertag; -typedef struct Monitor Monitor; -typedef struct Client Client; - -struct dvec2 { - double x, y; -}; - -struct ivec2 { - int32_t x, y, width, height; -}; - -typedef struct { - int32_t i; - int32_t i2; - float f; - float f2; - char *v; - char *v2; - char *v3; - uint32_t ui; - uint32_t ui2; -} Arg; - -typedef struct { - char *name; // keymode名称 - bool is_default; // 是否为默认模式 - bool is_common; // 是否为公共模式 -} KeyMode; - -typedef struct { - uint32_t mod; - uint32_t button; - int32_t (*func)(const Arg *); - const Arg arg; -} Button; // 鼠标按键 - -typedef struct { - uint32_t mod; - uint32_t dir; - int32_t (*func)(const Arg *); - const Arg arg; -} Axis; - -typedef struct { - struct wl_list link; - struct wlr_input_device *wlr_device; - struct libinput_device *libinput_device; - struct wl_listener destroy_listener; // 用于监听设备销毁事件 - void *device_data; // 新增:指向设备特定数据(如 Switch) -} InputDevice; - -typedef struct { - struct wl_list link; - struct wlr_switch *wlr_switch; - struct wl_listener toggle; - InputDevice *input_dev; -} Switch; - -struct dwl_animation { - bool should_animate; - bool running; - bool tagining; - bool tagouted; - bool tagouting; - bool begin_fade_in; - bool tag_from_rule; - uint32_t time_started; - uint32_t duration; - struct wlr_box initial; - struct wlr_box current; - int32_t action; -}; - -struct dwl_opacity_animation { - bool running; - float current_opacity; - float target_opacity; - float initial_opacity; - uint32_t time_started; - uint32_t duration; - float current_border_color[4]; - float target_border_color[4]; - float initial_border_color[4]; -}; - -typedef struct { - float width_scale; - float height_scale; - int32_t width; - int32_t height; - enum corner_location corner_location; - bool should_scale; -} BufferData; - -struct Client { - /* Must keep these three elements in this order */ - uint32_t type; /* XDGShell or X11* */ - struct wlr_box geom, pending, float_geom, animainit_geom, - overview_backup_geom, current, - drag_begin_geom; /* layout-relative, includes border */ - Monitor *mon; - struct wlr_scene_tree *scene; - struct wlr_scene_rect *border; /* top, bottom, left, right */ - struct wlr_scene_shadow *shadow; - struct wlr_scene_tree *scene_surface; - struct wl_list link; - struct wl_list flink; - struct wl_list fadeout_link; - union { - struct wlr_xdg_surface *xdg; - struct wlr_xwayland_surface *xwayland; - } surface; - struct wl_listener commit; - struct wl_listener map; - struct wl_listener maximize; - struct wl_listener minimize; - struct wl_listener unmap; - struct wl_listener destroy; - struct wl_listener set_title; - struct wl_listener fullscreen; -#ifdef XWAYLAND - struct wl_listener activate; - struct wl_listener associate; - struct wl_listener dissociate; - struct wl_listener configure; - struct wl_listener set_hints; - struct wl_listener set_geometry; -#endif - uint32_t bw; - uint32_t tags, oldtags, mini_restore_tag; - bool dirty; - uint32_t configure_serial; - struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; - int32_t isfloating, isurgent, isfullscreen, isfakefullscreen, - need_float_size_reduce, isminimized, isoverlay, isnosizehint, - ignore_maximize, ignore_minimize; - int32_t ismaximizescreen; - int32_t overview_backup_bw; - int32_t fullscreen_backup_x, fullscreen_backup_y, fullscreen_backup_w, - fullscreen_backup_h; - int32_t overview_isfullscreenbak, overview_ismaximizescreenbak, - overview_isfloatingbak; - - struct wlr_xdg_toplevel_decoration_v1 *decoration; - struct wl_listener foreign_activate_request; - struct wl_listener foreign_fullscreen_request; - struct wl_listener foreign_close_request; - struct wl_listener foreign_destroy; - struct wl_listener foreign_minimize_request; - struct wl_listener foreign_maximize_request; - struct wl_listener set_decoration_mode; - struct wl_listener destroy_decoration; - - const char *animation_type_open; - const char *animation_type_close; - int32_t is_in_scratchpad; - int32_t iscustomsize; - int32_t iscustompos; - int32_t is_scratchpad_show; - int32_t isglobal; - int32_t isnoborder; - int32_t isnoshadow; - int32_t isnoradius; - int32_t isnoanimation; - int32_t isopensilent; - int32_t istagsilent; - int32_t iskilling; - int32_t istagswitching; - int32_t isnamedscratchpad; - bool is_pending_open_animation; - bool is_restoring_from_ov; - float scroller_proportion; - float stack_proportion; - float old_stack_proportion; - bool need_output_flush; - struct dwl_animation animation; - struct dwl_opacity_animation opacity_animation; - int32_t isterm, noswallow; - int32_t allow_csd; - int32_t force_maximize; - pid_t pid; - Client *swallowing, *swallowedby; - bool is_clip_to_hide; - bool drag_to_tile; - bool scratchpad_switching_mon; - bool fake_no_border; - int32_t nofocus; - int32_t nofadein; - int32_t nofadeout; - int32_t no_force_center; - int32_t isunglobal; - float focused_opacity; - float unfocused_opacity; - char oldmonname[128]; - int32_t noblur; - double master_mfact_per, master_inner_per, stack_inner_per; - double old_master_mfact_per, old_master_inner_per, old_stack_inner_per; - double old_scroller_pproportion; - bool ismaster; - bool cursor_in_upper_half, cursor_in_left_half; - bool isleftstack; - int32_t tearing_hint; - int32_t force_tearing; - int32_t allow_shortcuts_inhibit; - float scroller_proportion_single; - bool isfocusing; - struct Client *next_in_stack; - struct Client *prev_in_stack; -}; - -typedef struct { - struct wl_list link; - struct wl_resource *resource; - Monitor *mon; -} DwlIpcOutput; - -typedef struct { - uint32_t mod; - xkb_keysym_t keysym; - int32_t (*func)(const Arg *); - const Arg arg; -} Key; - -typedef struct { - struct wlr_keyboard_group *wlr_group; - - int32_t nsyms; - const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ - uint32_t mods; /* invalid if nsyms == 0 */ - uint32_t keycode; - struct wl_event_source *key_repeat_source; - - struct wl_listener modifiers; - struct wl_listener key; - struct wl_listener destroy; -} KeyboardGroup; - -typedef struct { - struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor; - struct wl_listener destroy; - struct wl_list link; -} KeyboardShortcutsInhibitor; - -typedef struct { - /* Must keep these three elements in this order */ - uint32_t type; /* LayerShell */ - struct wlr_box geom, current, pending, animainit_geom; - Monitor *mon; - struct wlr_scene_tree *scene; - struct wlr_scene_tree *popups; - struct wlr_scene_shadow *shadow; - struct wlr_scene_layer_surface_v1 *scene_layer; - struct wl_list link; - struct wl_list fadeout_link; - int32_t mapped; - struct wlr_layer_surface_v1 *layer_surface; - - struct wl_listener destroy; - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener surface_commit; - - struct dwl_animation animation; - bool dirty; - int32_t noblur; - int32_t noanim; - int32_t noshadow; - char *animation_type_open; - char *animation_type_close; - bool need_output_flush; - bool being_unmapped; -} LayerSurface; - -typedef struct { - const char *symbol; - void (*arrange)(Monitor *); - const char *name; - uint32_t id; -} Layout; - -struct Monitor { - struct wl_list link; - struct wlr_output *wlr_output; - struct wlr_scene_output *scene_output; - struct wlr_output_state pending; - struct wl_listener frame; - struct wl_listener destroy; - struct wl_listener request_state; - struct wl_listener destroy_lock_surface; - struct wlr_session_lock_surface_v1 *lock_surface; - struct wlr_box m; /* monitor area, layout-relative */ - struct wlr_box w; /* window area, layout-relative */ - struct wl_list layers[4]; /* LayerSurface::link */ - uint32_t seltags; - uint32_t tagset[2]; - - struct wl_list dwl_ipc_outputs; - int32_t gappih; /* horizontal gap between windows */ - int32_t gappiv; /* vertical gap between windows */ - int32_t gappoh; /* horizontal outer gaps */ - int32_t gappov; /* vertical outer gaps */ - Pertag *pertag; - Client *sel, *prevsel; - int32_t isoverview; - int32_t is_in_hotarea; - int32_t asleep; - uint32_t visible_clients; - uint32_t visible_tiling_clients; - uint32_t visible_scroll_tiling_clients; - struct wlr_scene_optimized_blur *blur; - char last_surface_ws_name[256]; - struct wlr_ext_workspace_group_handle_v1 *ext_group; -}; - -typedef struct { - struct wlr_pointer_constraint_v1 *constraint; - struct wl_listener destroy; -} PointerConstraint; - -typedef struct { - struct wlr_scene_tree *scene; - - struct wlr_session_lock_v1 *lock; - struct wl_listener new_surface; - struct wl_listener unlock; - struct wl_listener destroy; -} SessionLock; - -// 在你的头文件中(比如 server.h) -// 在你的头文件中(比如 server.h) -typedef struct { - // 键盘模式相关 - KeyMode keymode; - - // 核心 Wayland/WLRoots 对象 - struct wl_display *dpy; - struct wl_event_loop *event_loop; - struct wlr_backend *backend; - struct wlr_backend *headless_backend; - struct wlr_scene *scene; - struct wlr_scene_tree *layers[NUM_LAYERS]; - struct wlr_renderer *drw; - struct wlr_allocator *alloc; - struct wlr_compositor *compositor; - - // Shell 相关 - struct wlr_xdg_shell *xdg_shell; - struct wlr_xdg_activation_v1 *activation; - struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; - struct wl_list clients; /* tiling order */ - struct wl_list fstack; /* focus order */ - struct wl_list fadeout_clients; - struct wl_list fadeout_layers; - struct wlr_idle_notifier_v1 *idle_notifier; - struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; - struct wlr_layer_shell_v1 *layer_shell; - struct wlr_output_manager_v1 *output_mgr; - struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; - struct wlr_keyboard_shortcuts_inhibit_manager_v1 - *keyboard_shortcuts_inhibit; - struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; - struct wlr_output_power_manager_v1 *power_mgr; - struct wlr_pointer_gestures_v1 *pointer_gestures; - struct wlr_drm_lease_v1_manager *drm_lease_manager; - struct mango_print_status_manager *print_status_manager; - - // 光标和输入相关 - struct wlr_cursor *cursor; - struct wlr_xcursor_manager *cursor_mgr; - struct wlr_session *session; - - // 界面元素 - struct wlr_scene_rect *root_bg; - struct wlr_session_lock_manager_v1 *session_lock_mgr; - struct wlr_scene_rect *locked_bg; - struct wlr_session_lock_v1 *cur_lock; - struct wlr_scene_tree *drag_icon; - struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; - struct wlr_pointer_constraints_v1 *pointer_constraints; - struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; - struct wlr_pointer_constraint_v1 *active_constraint; - - // 座位和键盘 - struct wlr_seat *seat; - KeyboardGroup *kb_group; - struct wl_list inputdevices; - struct wl_list keyboard_shortcut_inhibitors; - - // 布局相关 - struct wlr_output_layout *output_layout; - struct wl_list mons; - Monitor *selmon; - struct wlr_box sgeom; - - // 动画曲线数据 - struct dvec2 *baked_points_move; - struct dvec2 *baked_points_open; - struct dvec2 *baked_points_tag; - struct dvec2 *baked_points_close; - struct dvec2 *baked_points_focus; - struct dvec2 *baked_points_opafadein; - struct dvec2 *baked_points_opafadeout; - - // 事件源 - struct wl_event_source *hide_source; - - // 光标状态 - struct { - enum wp_cursor_shape_device_v1_shape shape; - struct wlr_surface *surface; - int32_t hotspot_x; - int32_t hotspot_y; - } last_cursor; - - // 各种状态变量 - pid_t child_pid; - int32_t locked; - uint32_t locked_mods; - void *exclusive_focus; - uint32_t cursor_mode; - Client *grabc; - int32_t rzcorner; - int32_t grabcx, grabcy; - int32_t drag_begin_cursorx, drag_begin_cursory; - bool start_drag_window; - int32_t last_apply_drag_time; - - int32_t enablegaps; - int32_t axis_apply_time; - int32_t axis_apply_dir; - int32_t scroller_focus_lock; - - uint32_t swipe_fingers; - double swipe_dx; - double swipe_dy; - - bool render_border; - uint32_t chvt_backup_tag; - bool allow_frame_scheduling; - char chvt_backup_selmon[32]; - - bool cursor_hidden; - bool tag_combo; - - // 配置路径 - const char *cli_config_path; -} Server; - -/* function declarations */ -static void init_server(void); -static void free_server(void); -static void applybounds( - Client *c, - struct wlr_box *bbox); // 设置边界规则,能让一些窗口拥有比较适合的大小 -static void applyrules(Client *c); // 窗口规则应用,应用config.h中定义的窗口规则 -static void arrange(Monitor *m, bool want_animation, - bool from_view); // 布局函数,让窗口俺平铺规则移动和重置大小 -static void arrangelayer(Monitor *m, struct wl_list *list, - struct wlr_box *usable_area, int32_t exclusive); -static void arrangelayers(Monitor *m); -static void handle_print_status(struct wl_listener *listener, void *data); -static void axisnotify(struct wl_listener *listener, - void *data); // 滚轮事件处理 -static void buttonpress(struct wl_listener *listener, - void *data); // 鼠标按键事件处理 -static int32_t 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 cleanupmon(struct wl_listener *listener, void *data); // 退出清理 -static void closemon(Monitor *m); -static void cleanuplisteners(void); -static void toggle_hotarea(int32_t x_root, int32_t y_root); // 触发热区 -static void maplayersurfacenotify(struct wl_listener *listener, void *data); -static void commitlayersurfacenotify(struct wl_listener *listener, void *data); -static void commitnotify(struct wl_listener *listener, void *data); -static void createdecoration(struct wl_listener *listener, void *data); -static void createidleinhibitor(struct wl_listener *listener, void *data); -static void createkeyboard(struct wlr_keyboard *keyboard); -static void requestmonstate(struct wl_listener *listener, void *data); -static void createlayersurface(struct wl_listener *listener, void *data); -static void createlocksurface(struct wl_listener *listener, void *data); -static void createmon(struct wl_listener *listener, void *data); -static void createnotify(struct wl_listener *listener, void *data); -static void createpointer(struct wlr_pointer *pointer); -static void configure_pointer(struct libinput_device *device); -static void destroyinputdevice(struct wl_listener *listener, void *data); -static void createswitch(struct wlr_switch *switch_device); -static void switch_toggle(struct wl_listener *listener, void *data); -static void createpointerconstraint(struct wl_listener *listener, void *data); -static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); -static void commitpopup(struct wl_listener *listener, void *data); -static void createpopup(struct wl_listener *listener, void *data); -static void cursorframe(struct wl_listener *listener, void *data); -static void cursorwarptohint(void); -static void destroydecoration(struct wl_listener *listener, void *data); -static void destroydragicon(struct wl_listener *listener, void *data); -static void destroyidleinhibitor(struct wl_listener *listener, void *data); -static void destroylayernodenotify(struct wl_listener *listener, void *data); -static void destroylock(SessionLock *lock, int32_t unlocked); -static void destroylocksurface(struct wl_listener *listener, void *data); -static void destroynotify(struct wl_listener *listener, void *data); -static void destroypointerconstraint(struct wl_listener *listener, void *data); -static void destroysessionlock(struct wl_listener *listener, void *data); -static void destroykeyboardgroup(struct wl_listener *listener, void *data); -static Monitor *dirtomon(enum wlr_direction dir); -static void setcursorshape(struct wl_listener *listener, void *data); - -static void focusclient(Client *c, int32_t lift); - -static void setborder_color(Client *c); -static Client *focustop(Monitor *m); -static void fullscreennotify(struct wl_listener *listener, void *data); -static void gpureset(struct wl_listener *listener, void *data); - -static int32_t keyrepeat(void *data); - -static void inputdevice(struct wl_listener *listener, void *data); -static int32_t keybinding(uint32_t state, bool locked, uint32_t mods, - xkb_keysym_t sym, uint32_t keycode); -static void keypress(struct wl_listener *listener, void *data); -static void keypressmod(struct wl_listener *listener, void *data); -static bool keypressglobal(struct wlr_surface *last_surface, - struct wlr_keyboard *keyboard, - struct wlr_keyboard_key_event *event, uint32_t mods, - xkb_keysym_t keysym, uint32_t keycode); -static void locksession(struct wl_listener *listener, void *data); -static void mapnotify(struct wl_listener *listener, void *data); -static void maximizenotify(struct wl_listener *listener, void *data); -static void minimizenotify(struct wl_listener *listener, void *data); -static void motionabsolute(struct wl_listener *listener, void *data); -static void motionnotify(uint32_t time, struct wlr_input_device *device, - double sx, double sy, double sx_unaccel, - double sy_unaccel); -static void motionrelative(struct wl_listener *listener, void *data); - -static void reset_foreign_tolevel(Client *c); -static void remove_foreign_topleve(Client *c); -static void add_foreign_topleve(Client *c); -static void exchange_two_client(Client *c1, Client *c2); -static void outputmgrapply(struct wl_listener *listener, void *data); -static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, - int32_t test); -static void outputmgrtest(struct wl_listener *listener, void *data); -static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, - double sy, uint32_t time); -static void printstatus(void); -static void quitsignal(int32_t signo); -static void powermgrsetmode(struct wl_listener *listener, void *data); -static void rendermon(struct wl_listener *listener, void *data); -static void requestdecorationmode(struct wl_listener *listener, void *data); -static void requestdrmlease(struct wl_listener *listener, void *data); -static void requeststartdrag(struct wl_listener *listener, void *data); -static void resize(Client *c, struct wlr_box geo, int32_t interact); -static void run(char *startup_cmd); -static void setcursor(struct wl_listener *listener, void *data); -static void setfloating(Client *c, int32_t floating); -static void setfakefullscreen(Client *c, int32_t fakefullscreen); -static void setfullscreen(Client *c, int32_t fullscreen); -static void setmaximizescreen(Client *c, int32_t maximizescreen); -static void reset_maximizescreen_size(Client *c); -static void setgaps(int32_t oh, int32_t ov, int32_t ih, int32_t iv); - -static void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus); -static void setpsel(struct wl_listener *listener, void *data); -static void setsel(struct wl_listener *listener, void *data); -static void setup(void); -static void startdrag(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 unmapnotify(struct wl_listener *listener, void *data); -static void updatemons(struct wl_listener *listener, void *data); -static void updatetitle(struct wl_listener *listener, void *data); -static void urgent(struct wl_listener *listener, void *data); -static void view(const Arg *arg, bool want_animation); - -static void handlesig(int32_t signo); -static void -handle_keyboard_shortcuts_inhibit_new_inhibitor(struct wl_listener *listener, - void *data); -static void virtualkeyboard(struct wl_listener *listener, void *data); -static void virtualpointer(struct wl_listener *listener, void *data); -static void warp_cursor(const Client *c); -static Monitor *xytomon(double x, double y); -static void xytonode(double x, double y, struct wlr_surface **psurface, - Client **pc, LayerSurface **pl, double *nx, double *ny); -static void clear_fullscreen_flag(Client *c); -static pid_t getparentprocess(pid_t p); -static int32_t isdescprocess(pid_t p, pid_t c); -static Client *termforwin(Client *w); -static void swallow(Client *c, Client *w); - -static void warp_cursor_to_selmon(Monitor *m); -uint32_t want_restore_fullscreen(Client *target_client); -static void overview_restore(Client *c, const Arg *arg); -static void overview_backup(Client *c); -static void set_minimized(Client *c); - -static void show_scratchpad(Client *c); -static void show_hide_client(Client *c); -static void tag_client(const Arg *arg, Client *target_client); - -static struct wlr_box setclient_coordinate_center(Client *c, Monitor *m, - struct wlr_box geom, - int32_t offsetx, - int32_t offsety); -static uint32_t get_tags_first_tag(uint32_t tags); - -static struct wlr_output_mode * -get_nearest_output_mode(struct wlr_output *output, int32_t width, - int32_t height, float refresh); - -static void client_commit(Client *c); -static void layer_commit(LayerSurface *l); -static void apply_border(Client *c); -static void client_set_opacity(Client *c, double opacity); -static void init_baked_points(void); -static void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, - int32_t sx, int32_t sy, void *data); - -static Client *direction_select(const Arg *arg); -static void view_in_mon(const Arg *arg, bool want_animation, Monitor *m, - bool changefocus); - -static void buffer_set_effect(Client *c, BufferData buffer_data); -static void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, - int32_t sx, int32_t sy, void *data); -static void client_set_pending_state(Client *c); -static void layer_set_pending_state(LayerSurface *l); -static void set_rect_size(struct wlr_scene_rect *rect, int32_t width, - int32_t height); -static Client *center_tiled_select(Monitor *m); -static void handlecursoractivity(void); -static int32_t hidecursor(void *data); -static bool check_hit_no_border(Client *c); -static void reset_keyboard_layout(void); -static void client_update_oldmonname_record(Client *c, Monitor *m); -static void pending_kill_client(Client *c); -static uint32_t get_tags_first_tag_num(uint32_t source_tags); -static void set_layer_open_animaiton(LayerSurface *l, struct wlr_box geo); -static void init_fadeout_layers(LayerSurface *l); -static void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height); -static void get_layer_target_geometry(LayerSurface *l, - struct wlr_box *target_box); -static void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, - int32_t sx, int32_t sy, void *data); -static double find_animation_curve_at(double t, int32_t type); - -static void apply_opacity_to_rect_nodes(Client *c, struct wlr_scene_node *node, - double animation_passed); -static enum corner_location set_client_corner_location(Client *c); -static double all_output_frame_duration_ms(); -static struct wlr_scene_tree * -wlr_scene_tree_snapshot(struct wlr_scene_node *node, - struct wlr_scene_tree *parent); -static bool is_scroller_layout(Monitor *m); -static bool is_centertile_layout(Monitor *m); -static void create_output(struct wlr_backend *backend, void *data); -static void get_layout_abbr(char *abbr, const char *full_name); -static void apply_named_scratchpad(Client *target_client); -static Client *get_client_by_id_or_title(const char *arg_id, - const char *arg_title); -static bool switch_scratchpad_client_state(Client *c); -static bool check_trackpad_disabled(struct wlr_pointer *pointer); -static uint32_t get_tag_status(uint32_t tag, Monitor *m); -static void enable_adaptive_sync(Monitor *m, struct wlr_output_state *state); -static Client *get_next_stack_client(Client *c, bool reverse); -static void set_float_malposition(Client *tc); -static void set_size_per(Monitor *m, Client *c); -static void resize_tile_client(Client *rec, bool isdrag, int32_t offsetx, - int32_t offsety, uint32_t time); -static void refresh_monitors_workspaces_status(Monitor *m); -static void init_client_properties(Client *c); -static float *get_border_color(Client *c); -static void clear_fullscreen_and_maximized_state(Monitor *m); -static void request_fresh_all_monitors(void); -static Client *find_client_by_direction(Client *tc, const Arg *arg, - bool findfloating, bool ignore_align); -static void exit_scroller_stack(Client *c); -static Client *get_scroll_stack_head(Client *c); -static bool client_only_in_one_tag(Client *c); -static Client *get_focused_stack_client(Client *sc); -static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc); #include "data/static_keymap.h" -#include "dispatch/bind_declare.h" -#include "layout/layout.h" + +extern Layout layouts[]; +extern const uint32_t layouts_len; +extern Layout overviewlayout; +extern Config config; /* variables */ Server server; -static const char broken[] = "broken"; -static const int32_t layermap[] = {LyrBg, LyrBottom, LyrTop, LyrOverlay}; +const char broken[] = "broken"; +const int32_t layermap[] = {LyrBg, LyrBottom, LyrTop, LyrOverlay}; #include "client/client.h" -#include "config/preset.h" -struct Pertag { - uint32_t curtag, prevtag; /* current and previous tag */ - int32_t nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ - float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ - bool no_hide[LENGTH(tags) + 1]; /* no_hide per tag */ - bool no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */ - const Layout - *ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */ -}; +struct wl_signal mango_print_status; -static struct wl_signal mango_print_status; - -static struct wl_listener print_status_listener = {.notify = +struct wl_listener print_status_listener = {.notify = handle_print_status}; -static struct wl_listener cursor_axis = {.notify = axisnotify}; -static struct wl_listener cursor_button = {.notify = buttonpress}; -static struct wl_listener cursor_frame = {.notify = cursorframe}; -static struct wl_listener cursor_motion = {.notify = motionrelative}; -static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; -static struct wl_listener gpu_reset = {.notify = gpureset}; -static struct wl_listener layout_change = {.notify = updatemons}; -static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor}; -static struct wl_listener new_input_device = {.notify = inputdevice}; -static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; -static struct wl_listener new_virtual_pointer = {.notify = virtualpointer}; -static struct wl_listener new_pointer_constraint = { +struct wl_listener cursor_axis = {.notify = axisnotify}; +struct wl_listener cursor_button = {.notify = buttonpress}; +struct wl_listener cursor_frame = {.notify = cursorframe}; +struct wl_listener cursor_motion = {.notify = motionrelative}; +struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; +struct wl_listener gpu_reset = {.notify = gpureset}; +struct wl_listener layout_change = {.notify = updatemons}; +struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor}; +struct wl_listener new_input_device = {.notify = inputdevice}; +struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; +struct wl_listener new_virtual_pointer = {.notify = virtualpointer}; +struct wl_listener new_pointer_constraint = { .notify = createpointerconstraint}; -static struct wl_listener new_output = {.notify = createmon}; -static struct wl_listener new_xdg_toplevel = {.notify = createnotify}; -static struct wl_listener new_xdg_popup = {.notify = createpopup}; -static struct wl_listener new_xdg_decoration = {.notify = createdecoration}; -static struct wl_listener new_layer_surface = {.notify = createlayersurface}; -static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; -static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; -static struct wl_listener output_power_mgr_set_mode = {.notify = +struct wl_listener new_output = {.notify = createmon}; +struct wl_listener new_xdg_toplevel = {.notify = createnotify}; +struct wl_listener new_xdg_popup = {.notify = createpopup}; +struct wl_listener new_xdg_decoration = {.notify = createdecoration}; +struct wl_listener new_layer_surface = {.notify = createlayersurface}; +struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; +struct wl_listener output_mgr_test = {.notify = outputmgrtest}; +struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode}; -static struct wl_listener request_activate = {.notify = urgent}; -static struct wl_listener request_cursor = {.notify = setcursor}; -static struct wl_listener request_set_psel = {.notify = setpsel}; -static struct wl_listener request_set_sel = {.notify = setsel}; -static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; -static struct wl_listener request_start_drag = {.notify = requeststartdrag}; -static struct wl_listener start_drag = {.notify = startdrag}; -static struct wl_listener new_session_lock = {.notify = locksession}; -static struct wl_listener drm_lease_request = {.notify = requestdrmlease}; -static struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor = { +struct wl_listener request_activate = {.notify = urgent}; +struct wl_listener request_cursor = {.notify = setcursor}; +struct wl_listener request_set_psel = {.notify = setpsel}; +struct wl_listener request_set_sel = {.notify = setsel}; +struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; +struct wl_listener request_start_drag = {.notify = requeststartdrag}; +struct wl_listener start_drag = {.notify = startdrag}; +struct wl_listener new_session_lock = {.notify = locksession}; +struct wl_listener drm_lease_request = {.notify = requestdrmlease}; +struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor = { .notify = handle_keyboard_shortcuts_inhibit_new_inhibitor}; #ifdef XWAYLAND -static void activatex11(struct wl_listener *listener, void *data); -static void configurex11(struct wl_listener *listener, void *data); -static void createnotifyx11(struct wl_listener *listener, void *data); -static void dissociatex11(struct wl_listener *listener, void *data); -static void associatex11(struct wl_listener *listener, void *data); -static void sethints(struct wl_listener *listener, void *data); -static void xwaylandready(struct wl_listener *listener, void *data); -static void setgeometrynotify(struct wl_listener *listener, void *data); -static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; -static struct wl_listener xwayland_ready = {.notify = xwaylandready}; -static struct wlr_xwayland *xwayland; +void activatex11(struct wl_listener *listener, void *data); +void configurex11(struct wl_listener *listener, void *data); +void createnotifyx11(struct wl_listener *listener, void *data); +void dissociatex11(struct wl_listener *listener, void *data); +void associatex11(struct wl_listener *listener, void *data); +void sethints(struct wl_listener *listener, void *data); +void xwaylandready(struct wl_listener *listener, void *data); +void setgeometrynotify(struct wl_listener *listener, void *data); +struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; +struct wl_listener xwayland_ready = {.notify = xwaylandready}; +struct wlr_xwayland *xwayland; #endif #include "animation/client.h" @@ -983,7 +77,11 @@ static struct wlr_xwayland *xwayland; #include "animation/tag.h" #include "config/parse_config.h" #include "dispatch/bind_define.h" -#include "ext-protocol/all.h" +#include "ext-protocol/dwl-ipc.h" +#include "ext-protocol/ext-workspace.h" +#include "ext-protocol/foreign-toplevel.h" +#include "ext-protocol/tearing.h" +#include "ext-protocol/text-input.h" #include "fetch/fetch.h" #include "layout/arrange.h" #include "layout/horizontal.h" @@ -1418,7 +516,7 @@ void toggle_hotarea(int32_t x_root, int32_t y_root) { } } -static void apply_rule_properties(Client *c, const ConfigWinRule *r) { +void apply_rule_properties(Client *c, const ConfigWinRule *r) { APPLY_INT_PROP(c, r, isterm); APPLY_INT_PROP(c, r, allow_csd); APPLY_INT_PROP(c, r, force_maximize); @@ -2390,7 +1488,7 @@ void closemon(Monitor *m) { } } -static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, +void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, int32_t sy, void *user_data) { struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer); @@ -3914,7 +3012,7 @@ void locksession(struct wl_listener *listener, void *data) { wlr_session_lock_v1_send_locked(session_lock); } -static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, +void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, int32_t sy, void *user_data) { Client *c = user_data; @@ -4575,7 +3673,7 @@ void requestdecorationmode(struct wl_listener *listener, void *data) { } } -static void requestdrmlease(struct wl_listener *listener, void *data) { +void requestdrmlease(struct wl_listener *listener, void *data) { struct wlr_drm_lease_request_v1 *req = data; struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); @@ -6103,7 +5201,7 @@ void view(const Arg *arg, bool want_animation) { } } -static void +void handle_keyboard_shortcuts_inhibitor_destroy(struct wl_listener *listener, void *data) { KeyboardShortcutsInhibitor *inhibitor = @@ -6333,7 +5431,7 @@ void xwaylandready(struct wl_listener *listener, void *data) { xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); } -static void setgeometrynotify(struct wl_listener *listener, void *data) { +void setgeometrynotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_geometry); wlr_scene_node_set_position(&c->scene->node, c->surface.xwayland->x, diff --git a/src/type.h b/src/type.h new file mode 100644 index 0000000..3e9334f --- /dev/null +++ b/src/type.h @@ -0,0 +1,916 @@ +/* + * See LICENSE file for copyright and license details. + */ +#include "wlr-layer-shell-unstable-v1-protocol.h" +#include "wlr/util/box.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XWAYLAND +#include +#include +#include +#endif +#include "common/util.h" + +/* macros */ +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define GEZERO(A) ((A) >= 0 ? (A) : 0) +#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) +#define INSIDEMON(A) \ + (A->geom.x >= A->mon->m.x && A->geom.y >= A->mon->m.y && \ + A->geom.x + A->geom.width <= A->mon->m.x + A->mon->m.width && \ + A->geom.y + A->geom.height <= A->mon->m.y + A->mon->m.height) +#define GEOMINSIDEMON(A, M) \ + (A->x >= M->m.x && A->y >= M->m.y && \ + A->x + A->width <= M->m.x + M->m.width && \ + A->y + A->height <= M->m.y + M->m.height) +#define ISTILED(A) \ + (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ + !(A)->ismaximizescreen && !(A)->isfullscreen && !(A)->isunglobal) +#define ISSCROLLTILED(A) \ + (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ + !(A)->isunglobal) +#define VISIBLEON(C, M) \ + ((C) && (M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define END(A) ((A) + LENGTH(A)) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) +#define ISFULLSCREEN(A) \ + ((A)->isfullscreen || (A)->ismaximizescreen || \ + (A)->overview_ismaximizescreenbak || (A)->overview_isfullscreenbak) +#define LISTEN_STATIC(E, H) \ + do { \ + struct wl_listener *_l = ecalloc(1, sizeof(*_l)); \ + _l->notify = (H); \ + wl_signal_add((E), _l); \ + } while (0) + +#define APPLY_INT_PROP(obj, rule, prop) \ + if (rule->prop >= 0) \ + obj->prop = rule->prop + +#define APPLY_FLOAT_PROP(obj, rule, prop) \ + if (rule->prop > 0.0f) \ + obj->prop = rule->prop + +#define APPLY_STRING_PROP(obj, rule, prop) \ + if (rule->prop != NULL) \ + obj->prop = rule->prop + +#define BAKED_POINTS_COUNT 256 + +/* enums */ +enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; + +enum { VERTICAL, HORIZONTAL }; +enum { SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT }; +enum { CurNormal, CurPressed, CurMove, CurResize }; /* server.cursor */ +enum { XDGShell, LayerShell, X11 }; /* client types */ +enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 +enum { + LyrBg, + LyrBlur, + LyrBottom, + LyrTile, + LyrTop, + LyrFadeOut, + LyrOverlay, + LyrIMPopup, // text-input layer + LyrBlock, + NUM_LAYERS +}; /* server.scene server.layers */ +#ifdef XWAYLAND +enum { + NetWMWindowTypeDialog, + NetWMWindowTypeSplash, + NetWMWindowTypeToolbar, + NetWMWindowTypeUtility, + NetLast +}; /* EWMH atoms */ +#endif +enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */ +enum { NONE, OPEN, MOVE, CLOSE, TAG, FOCUS, OPAFADEIN, OPAFADEOUT }; +enum { UNFOLD, FOLD, INVALIDFOLD }; +enum { PREV, NEXT }; +enum { STATE_UNSPECIFIED = 0, STATE_ENABLED, STATE_DISABLED }; + +enum tearing_mode { + TEARING_DISABLED = 0, + TEARING_ENABLED, + TEARING_FULLSCREEN_ONLY, +}; + +enum seat_config_shortcuts_inhibit { + SHORTCUTS_INHIBIT_DISABLE, + SHORTCUTS_INHIBIT_ENABLE, +}; + +// 事件掩码枚举 +enum print_event_type { + PRINT_ACTIVE = 1 << 0, + PRINT_TAG = 1 << 1, + PRINT_LAYOUT = 1 << 2, + PRINT_TITLE = 1 << 3, + PRINT_APPID = 1 << 4, + PRINT_LAYOUT_SYMBOL = 1 << 5, + PRINT_FULLSCREEN = 1 << 6, + PRINT_FLOATING = 1 << 7, + PRINT_X = 1 << 8, + PRINT_Y = 1 << 9, + PRINT_WIDTH = 1 << 10, + PRINT_HEIGHT = 1 << 11, + PRINT_LAST_LAYER = 1 << 12, + PRINT_KB_LAYOUT = 1 << 13, + PRINT_KEYMODE = 1 << 14, + PRINT_SCALEFACTOR = 1 << 15, + PRINT_FRAME = 1 << 16, + PRINT_ALL = (1 << 17) - 1 // 所有位都设为1 +}; + +typedef struct Pertag Pertag; +typedef struct Monitor Monitor; +typedef struct Client Client; + +struct dvec2 { + double x, y; +}; + +struct ivec2 { + int32_t x, y, width, height; +}; + +typedef struct { + int32_t i; + int32_t i2; + float f; + float f2; + char *v; + char *v2; + char *v3; + uint32_t ui; + uint32_t ui2; +} Arg; + +typedef struct { + char *name; // keymode名称 + bool is_default; // 是否为默认模式 + bool is_common; // 是否为公共模式 +} KeyMode; + +typedef struct { + uint32_t mod; + uint32_t button; + int32_t (*func)(const Arg *); + const Arg arg; +} Button; // 鼠标按键 + +typedef struct { + uint32_t mod; + uint32_t dir; + int32_t (*func)(const Arg *); + const Arg arg; +} Axis; + +typedef struct { + struct wl_list link; + struct wlr_input_device *wlr_device; + struct libinput_device *libinput_device; + struct wl_listener destroy_listener; // 用于监听设备销毁事件 + void *device_data; // 新增:指向设备特定数据(如 Switch) +} InputDevice; + +typedef struct { + struct wl_list link; + struct wlr_switch *wlr_switch; + struct wl_listener toggle; + InputDevice *input_dev; +} Switch; + +struct dwl_animation { + bool should_animate; + bool running; + bool tagining; + bool tagouted; + bool tagouting; + bool begin_fade_in; + bool tag_from_rule; + uint32_t time_started; + uint32_t duration; + struct wlr_box initial; + struct wlr_box current; + int32_t action; +}; + +struct dwl_opacity_animation { + bool running; + float current_opacity; + float target_opacity; + float initial_opacity; + uint32_t time_started; + uint32_t duration; + float current_border_color[4]; + float target_border_color[4]; + float initial_border_color[4]; +}; + +typedef struct { + float width_scale; + float height_scale; + int32_t width; + int32_t height; + enum corner_location corner_location; + bool should_scale; +} BufferData; + +struct Client { + /* Must keep these three elements in this order */ + uint32_t type; /* XDGShell or X11* */ + struct wlr_box geom, pending, float_geom, animainit_geom, + overview_backup_geom, current, + drag_begin_geom; /* layout-relative, includes border */ + Monitor *mon; + struct wlr_scene_tree *scene; + struct wlr_scene_rect *border; /* top, bottom, left, right */ + struct wlr_scene_shadow *shadow; + struct wlr_scene_tree *scene_surface; + struct wl_list link; + struct wl_list flink; + struct wl_list fadeout_link; + union { + struct wlr_xdg_surface *xdg; + struct wlr_xwayland_surface *xwayland; + } surface; + struct wl_listener commit; + struct wl_listener map; + struct wl_listener maximize; + struct wl_listener minimize; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener set_title; + struct wl_listener fullscreen; +#ifdef XWAYLAND + struct wl_listener activate; + struct wl_listener associate; + struct wl_listener dissociate; + struct wl_listener configure; + struct wl_listener set_hints; + struct wl_listener set_geometry; +#endif + uint32_t bw; + uint32_t tags, oldtags, mini_restore_tag; + bool dirty; + uint32_t configure_serial; + struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; + int32_t isfloating, isurgent, isfullscreen, isfakefullscreen, + need_float_size_reduce, isminimized, isoverlay, isnosizehint, + ignore_maximize, ignore_minimize; + int32_t ismaximizescreen; + int32_t overview_backup_bw; + int32_t fullscreen_backup_x, fullscreen_backup_y, fullscreen_backup_w, + fullscreen_backup_h; + int32_t overview_isfullscreenbak, overview_ismaximizescreenbak, + overview_isfloatingbak; + + struct wlr_xdg_toplevel_decoration_v1 *decoration; + struct wl_listener foreign_activate_request; + struct wl_listener foreign_fullscreen_request; + struct wl_listener foreign_close_request; + struct wl_listener foreign_destroy; + struct wl_listener foreign_minimize_request; + struct wl_listener foreign_maximize_request; + struct wl_listener set_decoration_mode; + struct wl_listener destroy_decoration; + + const char *animation_type_open; + const char *animation_type_close; + int32_t is_in_scratchpad; + int32_t iscustomsize; + int32_t iscustompos; + int32_t is_scratchpad_show; + int32_t isglobal; + int32_t isnoborder; + int32_t isnoshadow; + int32_t isnoradius; + int32_t isnoanimation; + int32_t isopensilent; + int32_t istagsilent; + int32_t iskilling; + int32_t istagswitching; + int32_t isnamedscratchpad; + bool is_pending_open_animation; + bool is_restoring_from_ov; + float scroller_proportion; + float stack_proportion; + float old_stack_proportion; + bool need_output_flush; + struct dwl_animation animation; + struct dwl_opacity_animation opacity_animation; + int32_t isterm, noswallow; + int32_t allow_csd; + int32_t force_maximize; + pid_t pid; + Client *swallowing, *swallowedby; + bool is_clip_to_hide; + bool drag_to_tile; + bool scratchpad_switching_mon; + bool fake_no_border; + int32_t nofocus; + int32_t nofadein; + int32_t nofadeout; + int32_t no_force_center; + int32_t isunglobal; + float focused_opacity; + float unfocused_opacity; + char oldmonname[128]; + int32_t noblur; + double master_mfact_per, master_inner_per, stack_inner_per; + double old_master_mfact_per, old_master_inner_per, old_stack_inner_per; + double old_scroller_pproportion; + bool ismaster; + bool cursor_in_upper_half, cursor_in_left_half; + bool isleftstack; + int32_t tearing_hint; + int32_t force_tearing; + int32_t allow_shortcuts_inhibit; + float scroller_proportion_single; + bool isfocusing; + struct Client *next_in_stack; + struct Client *prev_in_stack; +}; + +typedef struct { + struct wl_list link; + struct wl_resource *resource; + Monitor *mon; +} DwlIpcOutput; + +typedef struct { + uint32_t mod; + xkb_keysym_t keysym; + int32_t (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + struct wlr_keyboard_group *wlr_group; + + int32_t nsyms; + const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ + uint32_t mods; /* invalid if nsyms == 0 */ + uint32_t keycode; + struct wl_event_source *key_repeat_source; + + struct wl_listener modifiers; + struct wl_listener key; + struct wl_listener destroy; +} KeyboardGroup; + +typedef struct { + struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor; + struct wl_listener destroy; + struct wl_list link; +} KeyboardShortcutsInhibitor; + +typedef struct { + /* Must keep these three elements in this order */ + uint32_t type; /* LayerShell */ + struct wlr_box geom, current, pending, animainit_geom; + Monitor *mon; + struct wlr_scene_tree *scene; + struct wlr_scene_tree *popups; + struct wlr_scene_shadow *shadow; + struct wlr_scene_layer_surface_v1 *scene_layer; + struct wl_list link; + struct wl_list fadeout_link; + int32_t mapped; + struct wlr_layer_surface_v1 *layer_surface; + + struct wl_listener destroy; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; + + struct dwl_animation animation; + bool dirty; + int32_t noblur; + int32_t noanim; + int32_t noshadow; + char *animation_type_open; + char *animation_type_close; + bool need_output_flush; + bool being_unmapped; +} LayerSurface; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); + const char *name; + uint32_t id; +} Layout; + +struct Monitor { + struct wl_list link; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; + struct wlr_output_state pending; + struct wl_listener frame; + struct wl_listener destroy; + struct wl_listener request_state; + struct wl_listener destroy_lock_surface; + struct wlr_session_lock_surface_v1 *lock_surface; + struct wlr_box m; /* monitor area, layout-relative */ + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface::link */ + uint32_t seltags; + uint32_t tagset[2]; + + struct wl_list dwl_ipc_outputs; + int32_t gappih; /* horizontal gap between windows */ + int32_t gappiv; /* vertical gap between windows */ + int32_t gappoh; /* horizontal outer gaps */ + int32_t gappov; /* vertical outer gaps */ + Pertag *pertag; + Client *sel, *prevsel; + int32_t isoverview; + int32_t is_in_hotarea; + int32_t asleep; + uint32_t visible_clients; + uint32_t visible_tiling_clients; + uint32_t visible_scroll_tiling_clients; + struct wlr_scene_optimized_blur *blur; + char last_surface_ws_name[256]; + struct wlr_ext_workspace_group_handle_v1 *ext_group; +}; + +typedef struct { + struct wlr_pointer_constraint_v1 *constraint; + struct wl_listener destroy; +} PointerConstraint; + +typedef struct { + struct wlr_scene_tree *scene; + + struct wlr_session_lock_v1 *lock; + struct wl_listener new_surface; + struct wl_listener unlock; + struct wl_listener destroy; +} SessionLock; + +// 在你的头文件中(比如 server.h) +// 在你的头文件中(比如 server.h) +typedef struct { + // 键盘模式相关 + KeyMode keymode; + + // 核心 Wayland/WLRoots 对象 + struct wl_display *dpy; + struct wl_event_loop *event_loop; + struct wlr_backend *backend; + struct wlr_backend *headless_backend; + struct wlr_scene *scene; + struct wlr_scene_tree *layers[NUM_LAYERS]; + struct wlr_renderer *drw; + struct wlr_allocator *alloc; + struct wlr_compositor *compositor; + + // Shell 相关 + struct wlr_xdg_shell *xdg_shell; + struct wlr_xdg_activation_v1 *activation; + struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; + struct wl_list clients; /* tiling order */ + struct wl_list fstack; /* focus order */ + struct wl_list fadeout_clients; + struct wl_list fadeout_layers; + struct wlr_idle_notifier_v1 *idle_notifier; + struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; + struct wlr_layer_shell_v1 *layer_shell; + struct wlr_output_manager_v1 *output_mgr; + struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; + struct wlr_keyboard_shortcuts_inhibit_manager_v1 + *keyboard_shortcuts_inhibit; + struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; + struct wlr_output_power_manager_v1 *power_mgr; + struct wlr_pointer_gestures_v1 *pointer_gestures; + struct wlr_drm_lease_v1_manager *drm_lease_manager; + struct mango_print_status_manager *print_status_manager; + + // 光标和输入相关 + struct wlr_cursor *cursor; + struct wlr_xcursor_manager *cursor_mgr; + struct wlr_session *session; + + // 界面元素 + struct wlr_scene_rect *root_bg; + struct wlr_session_lock_manager_v1 *session_lock_mgr; + struct wlr_scene_rect *locked_bg; + struct wlr_session_lock_v1 *cur_lock; + struct wlr_scene_tree *drag_icon; + struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; + struct wlr_pointer_constraints_v1 *pointer_constraints; + struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; + struct wlr_pointer_constraint_v1 *active_constraint; + + // 座位和键盘 + struct wlr_seat *seat; + KeyboardGroup *kb_group; + struct wl_list inputdevices; + struct wl_list keyboard_shortcut_inhibitors; + + // 布局相关 + struct wlr_output_layout *output_layout; + struct wl_list mons; + Monitor *selmon; + struct wlr_box sgeom; + + // 动画曲线数据 + struct dvec2 *baked_points_move; + struct dvec2 *baked_points_open; + struct dvec2 *baked_points_tag; + struct dvec2 *baked_points_close; + struct dvec2 *baked_points_focus; + struct dvec2 *baked_points_opafadein; + struct dvec2 *baked_points_opafadeout; + + // 事件源 + struct wl_event_source *hide_source; + + // 光标状态 + struct { + enum wp_cursor_shape_device_v1_shape shape; + struct wlr_surface *surface; + int32_t hotspot_x; + int32_t hotspot_y; + } last_cursor; + + // 各种状态变量 + pid_t child_pid; + int32_t locked; + uint32_t locked_mods; + void *exclusive_focus; + uint32_t cursor_mode; + Client *grabc; + int32_t rzcorner; + int32_t grabcx, grabcy; + int32_t drag_begin_cursorx, drag_begin_cursory; + bool start_drag_window; + int32_t last_apply_drag_time; + + int32_t enablegaps; + int32_t axis_apply_time; + int32_t axis_apply_dir; + int32_t scroller_focus_lock; + + uint32_t swipe_fingers; + double swipe_dx; + double swipe_dy; + + bool render_border; + uint32_t chvt_backup_tag; + bool allow_frame_scheduling; + char chvt_backup_selmon[32]; + + bool cursor_hidden; + bool tag_combo; + + // 配置路径 + const char *cli_config_path; +} Server; + + +/* function declarations */ +void init_server(void); +void free_server(void); +void applybounds( + Client *c, + struct wlr_box *bbox); // 设置边界规则,能让一些窗口拥有比较适合的大小 +void applyrules(Client *c); // 窗口规则应用,应用config.h中定义的窗口规则 +void arrange(Monitor *m, bool want_animation, + bool from_view); // 布局函数,让窗口俺平铺规则移动和重置大小 +void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int32_t exclusive); +void arrangelayers(Monitor *m); +void handle_print_status(struct wl_listener *listener, void *data); +void axisnotify(struct wl_listener *listener, + void *data); // 滚轮事件处理 +void buttonpress(struct wl_listener *listener, + void *data); // 鼠标按键事件处理 +int32_t ongesture(struct wlr_pointer_swipe_end_event *event); +void swipe_begin(struct wl_listener *listener, void *data); +void swipe_update(struct wl_listener *listener, void *data); +void swipe_end(struct wl_listener *listener, void *data); +void pinch_begin(struct wl_listener *listener, void *data); +void pinch_update(struct wl_listener *listener, void *data); +void pinch_end(struct wl_listener *listener, void *data); +void hold_begin(struct wl_listener *listener, void *data); +void hold_end(struct wl_listener *listener, void *data); +void checkidleinhibitor(struct wlr_surface *exclude); +void cleanup(void); // 退出清理 +void cleanupmon(struct wl_listener *listener, void *data); // 退出清理 +void closemon(Monitor *m); +void cleanuplisteners(void); +void toggle_hotarea(int32_t x_root, int32_t y_root); // 触发热区 +void maplayersurfacenotify(struct wl_listener *listener, void *data); +void commitlayersurfacenotify(struct wl_listener *listener, void *data); +void commitnotify(struct wl_listener *listener, void *data); +void createdecoration(struct wl_listener *listener, void *data); +void createidleinhibitor(struct wl_listener *listener, void *data); +void createkeyboard(struct wlr_keyboard *keyboard); +void requestmonstate(struct wl_listener *listener, void *data); +void createlayersurface(struct wl_listener *listener, void *data); +void createlocksurface(struct wl_listener *listener, void *data); +void createmon(struct wl_listener *listener, void *data); +void createnotify(struct wl_listener *listener, void *data); +void createpointer(struct wlr_pointer *pointer); +void configure_pointer(struct libinput_device *device); +void destroyinputdevice(struct wl_listener *listener, void *data); +void createswitch(struct wlr_switch *switch_device); +void switch_toggle(struct wl_listener *listener, void *data); +void createpointerconstraint(struct wl_listener *listener, void *data); +void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); +void commitpopup(struct wl_listener *listener, void *data); +void createpopup(struct wl_listener *listener, void *data); +void cursorframe(struct wl_listener *listener, void *data); +void cursorwarptohint(void); +void destroydecoration(struct wl_listener *listener, void *data); +void destroydragicon(struct wl_listener *listener, void *data); +void destroyidleinhibitor(struct wl_listener *listener, void *data); +void destroylayernodenotify(struct wl_listener *listener, void *data); +void destroylock(SessionLock *lock, int32_t unlocked); +void destroylocksurface(struct wl_listener *listener, void *data); +void destroynotify(struct wl_listener *listener, void *data); +void destroypointerconstraint(struct wl_listener *listener, void *data); +void destroysessionlock(struct wl_listener *listener, void *data); +void destroykeyboardgroup(struct wl_listener *listener, void *data); +Monitor *dirtomon(enum wlr_direction dir); +void setcursorshape(struct wl_listener *listener, void *data); + +void focusclient(Client *c, int32_t lift); + +void setborder_color(Client *c); +Client *focustop(Monitor *m); +void fullscreennotify(struct wl_listener *listener, void *data); +void gpureset(struct wl_listener *listener, void *data); + +int32_t keyrepeat(void *data); + +void inputdevice(struct wl_listener *listener, void *data); +int32_t keybinding(uint32_t state, bool locked, uint32_t mods, + xkb_keysym_t sym, uint32_t keycode); +void keypress(struct wl_listener *listener, void *data); +void keypressmod(struct wl_listener *listener, void *data); +bool keypressglobal(struct wlr_surface *last_surface, + struct wlr_keyboard *keyboard, + struct wlr_keyboard_key_event *event, uint32_t mods, + xkb_keysym_t keysym, uint32_t keycode); +void locksession(struct wl_listener *listener, void *data); +void mapnotify(struct wl_listener *listener, void *data); +void maximizenotify(struct wl_listener *listener, void *data); +void minimizenotify(struct wl_listener *listener, void *data); +void motionabsolute(struct wl_listener *listener, void *data); +void motionnotify(uint32_t time, struct wlr_input_device *device, + double sx, double sy, double sx_unaccel, + double sy_unaccel); +void motionrelative(struct wl_listener *listener, void *data); + +void reset_foreign_tolevel(Client *c); +void remove_foreign_topleve(Client *c); +void add_foreign_topleve(Client *c); +void exchange_two_client(Client *c1, Client *c2); +void outputmgrapply(struct wl_listener *listener, void *data); +void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, + int32_t test); +void outputmgrtest(struct wl_listener *listener, void *data); +void pointerfocus(Client *c, struct wlr_surface *surface, double sx, + double sy, uint32_t time); +void printstatus(void); +void quitsignal(int32_t signo); +void powermgrsetmode(struct wl_listener *listener, void *data); +void rendermon(struct wl_listener *listener, void *data); +void requestdecorationmode(struct wl_listener *listener, void *data); +void requestdrmlease(struct wl_listener *listener, void *data); +void requeststartdrag(struct wl_listener *listener, void *data); +void resize(Client *c, struct wlr_box geo, int32_t interact); +void run(char *startup_cmd); +void setcursor(struct wl_listener *listener, void *data); +void setfloating(Client *c, int32_t floating); +void setfakefullscreen(Client *c, int32_t fakefullscreen); +void setfullscreen(Client *c, int32_t fullscreen); +void setmaximizescreen(Client *c, int32_t maximizescreen); +void reset_maximizescreen_size(Client *c); +void setgaps(int32_t oh, int32_t ov, int32_t ih, int32_t iv); + +void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus); +void setpsel(struct wl_listener *listener, void *data); +void setsel(struct wl_listener *listener, void *data); +void setup(void); +void startdrag(struct wl_listener *listener, void *data); + +void unlocksession(struct wl_listener *listener, void *data); +void unmaplayersurfacenotify(struct wl_listener *listener, void *data); +void unmapnotify(struct wl_listener *listener, void *data); +void updatemons(struct wl_listener *listener, void *data); +void updatetitle(struct wl_listener *listener, void *data); +void urgent(struct wl_listener *listener, void *data); +void view(const Arg *arg, bool want_animation); + +void handlesig(int32_t signo); +void +handle_keyboard_shortcuts_inhibit_new_inhibitor(struct wl_listener *listener, + void *data); +void virtualkeyboard(struct wl_listener *listener, void *data); +void virtualpointer(struct wl_listener *listener, void *data); +void warp_cursor(const Client *c); +Monitor *xytomon(double x, double y); +void xytonode(double x, double y, struct wlr_surface **psurface, + Client **pc, LayerSurface **pl, double *nx, double *ny); +void clear_fullscreen_flag(Client *c); +pid_t getparentprocess(pid_t p); +int32_t isdescprocess(pid_t p, pid_t c); +Client *termforwin(Client *w); +void swallow(Client *c, Client *w); + +void warp_cursor_to_selmon(Monitor *m); +uint32_t want_restore_fullscreen(Client *target_client); +void overview_restore(Client *c, const Arg *arg); +void overview_backup(Client *c); +void set_minimized(Client *c); + +void show_scratchpad(Client *c); +void show_hide_client(Client *c); +void tag_client(const Arg *arg, Client *target_client); + +struct wlr_box setclient_coordinate_center(Client *c, Monitor *m, + struct wlr_box geom, + int32_t offsetx, + int32_t offsety); +uint32_t get_tags_first_tag(uint32_t tags); + +struct wlr_output_mode * +get_nearest_output_mode(struct wlr_output *output, int32_t width, + int32_t height, float refresh); + +void client_commit(Client *c); +void layer_commit(LayerSurface *l); +void apply_border(Client *c); +void client_set_opacity(Client *c, double opacity); +void init_baked_points(void); +void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, void *data); + +Client *direction_select(const Arg *arg); +void view_in_mon(const Arg *arg, bool want_animation, Monitor *m, + bool changefocus); + +void buffer_set_effect(Client *c, BufferData buffer_data); +void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, void *data); +void client_set_pending_state(Client *c); +void layer_set_pending_state(LayerSurface *l); +void set_rect_size(struct wlr_scene_rect *rect, int32_t width, + int32_t height); +Client *center_tiled_select(Monitor *m); +void handlecursoractivity(void); +int32_t hidecursor(void *data); +bool check_hit_no_border(Client *c); +void reset_keyboard_layout(void); +void client_update_oldmonname_record(Client *c, Monitor *m); +void pending_kill_client(Client *c); +uint32_t get_tags_first_tag_num(uint32_t source_tags); +void set_layer_open_animaiton(LayerSurface *l, struct wlr_box geo); +void init_fadeout_layers(LayerSurface *l); +void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height); +void get_layer_target_geometry(LayerSurface *l, + struct wlr_box *target_box); +void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, void *data); +double find_animation_curve_at(double t, int32_t type); + +void apply_opacity_to_rect_nodes(Client *c, struct wlr_scene_node *node, + double animation_passed); +enum corner_location set_client_corner_location(Client *c); +double all_output_frame_duration_ms(); +struct wlr_scene_tree * +wlr_scene_tree_snapshot(struct wlr_scene_node *node, + struct wlr_scene_tree *parent); +bool is_scroller_layout(Monitor *m); +bool is_centertile_layout(Monitor *m); +void create_output(struct wlr_backend *backend, void *data); +void get_layout_abbr(char *abbr, const char *full_name); +void apply_named_scratchpad(Client *target_client); +Client *get_client_by_id_or_title(const char *arg_id, + const char *arg_title); +bool switch_scratchpad_client_state(Client *c); +bool check_trackpad_disabled(struct wlr_pointer *pointer); +uint32_t get_tag_status(uint32_t tag, Monitor *m); +void enable_adaptive_sync(Monitor *m, struct wlr_output_state *state); +Client *get_next_stack_client(Client *c, bool reverse); +void set_float_malposition(Client *tc); +void set_size_per(Monitor *m, Client *c); +void resize_tile_client(Client *rec, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time); +void refresh_monitors_workspaces_status(Monitor *m); +void init_client_properties(Client *c); +float *get_border_color(Client *c); +void clear_fullscreen_and_maximized_state(Monitor *m); +void request_fresh_all_monitors(void); +Client *find_client_by_direction(Client *tc, const Arg *arg, + bool findfloating, bool ignore_align); +void exit_scroller_stack(Client *c); +Client *get_scroll_stack_head(Client *c); +bool client_only_in_one_tag(Client *c); +Client *get_focused_stack_client(Client *sc); +bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc); + +#include "config/preset.h" +struct Pertag { + uint32_t curtag, prevtag; /* current and previous tag */ + int32_t nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + bool no_hide[LENGTH(tags) + 1]; /* no_hide per tag */ + bool no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */ + const Layout + *ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */ +};