diff --git a/README.md b/README.md index b78ccfa9..45da36cd 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,22 @@ This project's development is based on [dwl](https://codeberg.org/dwl/dwl/). - Ipc support(get/send message from/to compositor by external program) - Hycov-like overview - Window effects from scenefx (blur, shadow, corner radius, opacity) + - Zero flickering - every frame is perfect. https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f +# Mango's Vision + +**Mango's primary goal is stability**: After months of testing and development—and aside from a few lingering GPU compatibility issues—it should now be stable enough. I don't plan on making many breaking changes. + +**Mango's preference is practicality**: I tend to add features that genuinely help with daily workflows—things that make our work more convenient. + +**Mango won't cater to every user preference**: For niche feature requests, I'll take a wait-and-see approach. I'll only consider adding them if they get a significant number of upvotes. + # Our discord [mangowc](https://discord.gg/CPjbDxesh5) # Supported layouts - - tile - scroller - monocle @@ -40,21 +48,20 @@ https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f - vertical_tile - vertical_grid - vertical_scroller +- tgmix # Installation +[![Packaging status](https://repology.org/badge/vertical-allrepos/mangowc.svg)](https://repology.org/project/mangowc/versions) + ## Dependencies -- glibc - wayland - wayland-protocols - libinput - libdrm - libxkbcommon - pixman -- git -- meson -- ninja - libdisplay-info - libliftoff - hwdata @@ -98,6 +105,35 @@ Then, install the package: dnf install mangowc ``` +## Guix System +The package definition is described in the source repository. +First, add `mangowc` channel to `channels.scm` file: + +```scheme +;; In $HOME/.config/guix/channels.scm +(cons (channel + (name 'mangowc) + (url "https://github.com/DreamMaoMao/mangowc.git") + (branch "main")) + ... ;; Your other channels + %default-channels) +``` + +Then, run `guix pull` and after update you can either run +`guix install mangowc` or add it to your configuration via: + +```scheme +(use-modules (mangowc)) ;; Add mangowc module + +;; Add mangowc to packages list +(packages (cons* + mangowc-git + ... ;; Other packages you specified + %base-packages)) +``` + +And then rebuild your system. + ## Other ```bash diff --git a/config.conf b/assets/config.conf similarity index 100% rename from config.conf rename to assets/config.conf diff --git a/assets/mango-portals.conf b/assets/mango-portals.conf new file mode 100644 index 00000000..aebea31a --- /dev/null +++ b/assets/mango-portals.conf @@ -0,0 +1,5 @@ +[preferred] +default=gtk +org.freedesktop.impl.portal.ScreenCast=wlr +org.freedesktop.impl.portal.Screenshot=wlr +org.freedesktop.impl.portal.Inhibit=none diff --git a/mango.desktop b/assets/mango.desktop similarity index 100% rename from mango.desktop rename to assets/mango.desktop diff --git a/meson.build b/meson.build index 478ef0e6..3f2e5e50 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('mango', ['c', 'cpp'], - version : '0.12.1', + version : '0.12.3', ) subdir('protocols') @@ -56,7 +56,7 @@ endif if is_git_repo # 如果是 Git 目录,获取 Commit Hash 和最新的 tag commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip() - latest_tag = run_command(git, 'describe', '--tags', '--abbrev=0', check : false).stdout().strip() + latest_tag = meson.project_version() version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash) else # 如果不是 Git 目录,使用项目版本号和 "release" 字符串 @@ -147,5 +147,7 @@ executable('mmsg', ) desktop_install_dir = join_paths(prefix, 'share/wayland-sessions') -install_data('mango.desktop', install_dir : desktop_install_dir) -install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango')) +portal_install_dir = join_paths(prefix, 'share/xdg-desktop-portal') +install_data('assets/mango.desktop', install_dir : desktop_install_dir) +install_data('assets/mango-portals.conf', install_dir : portal_install_dir) +install_data('assets/config.conf', install_dir : join_paths(sysconfdir, 'mango')) diff --git a/nix/nixos-modules.nix b/nix/nixos-modules.nix index 33811022..7a9f930a 100644 --- a/nix/nixos-modules.nix +++ b/nix/nixos-modules.nix @@ -9,6 +9,10 @@ in { options = { programs.mango = { enable = lib.mkEnableOption "mango, a wayland compositor based on dwl"; + addLoginEntry = lib.mkEnableOption { + default = true; + description = "Whether to add a login entry to the display manager for mango"; + }; package = lib.mkOption { type = lib.types.package; default = self.packages.${pkgs.stdenv.hostPlatform.system}.mango; @@ -55,7 +59,7 @@ in { programs.xwayland.enable = lib.mkDefault true; services = { - displayManager.sessionPackages = [cfg.package]; + displayManager.sessionPackages = lib.mkIf cfg.addLoginEntry [ cfg.package ]; graphical-desktop.enable = lib.mkDefault true; }; diff --git a/protocols/meson.build b/protocols/meson.build index cafab64a..922a76ed 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -1,6 +1,6 @@ wayland_scanner = find_program('wayland-scanner') wayland_protos_dep = dependency('wayland-protocols') -wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir') +wl_protocol_dir = wayland_protos_dep.get_variable(pkgconfig:'pkgdatadir') wayland_scanner_code = generator( wayland_scanner, output: '@BASENAME@-protocol.c', diff --git a/src/animation/client.h b/src/animation/client.h index 2588fb1c..b6683ecc 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -990,6 +990,10 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); + if (c->configure_serial != 0) { + c->mon->resizing_count_pending++; + } + if (c == grabc) { c->animation.running = false; c->need_output_flush = false; diff --git a/src/client/client.h b/src/client/client.h index 49ab3988..4788e448 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -243,31 +243,6 @@ static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) { 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 unlikely 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)) @@ -319,9 +294,24 @@ static inline uint32_t client_set_size(Client *c, uint32_t width, uint32_t height) { #ifdef XWAYLAND if (client_is_x11(c)) { + + struct wlr_surface_state *state = + &c->surface.xwayland->surface->current; + + if ((int32_t)c->geom.width - 2 * (int32_t)c->bw == + (int32_t)state->width && + (int32_t)c->geom.height - 2 * (int32_t)c->bw == + (int32_t)state->height && + (int32_t)c->surface.xwayland->x == + (int32_t)c->geom.x + (int32_t)c->bw && + (int32_t)c->surface.xwayland->y == + (int32_t)c->geom.y + (int32_t)c->bw) { + return 0; + } + wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw, c->geom.y + c->bw, width, height); - return 0; + return 1; } #endif if ((int32_t)width == c->surface.xdg->toplevel->current.width && diff --git a/src/common/util.c b/src/common/util.c index a15cca7c..025aed6d 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -92,3 +92,85 @@ uint32_t get_now_in_ms(void) { uint32_t timespec_to_ms(struct timespec *ts) { return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000; } + +char *join_strings(char *arr[], const char *sep) { + if (!arr || !arr[0]) { + char *empty = malloc(1); + if (empty) + empty[0] = '\0'; + return empty; + } + + size_t total_len = 0; + int count = 0; + for (int i = 0; arr[i] != NULL; i++) { + total_len += strlen(arr[i]); + count++; + } + if (count > 0) { + total_len += strlen(sep) * (count - 1); + } + + char *result = malloc(total_len + 1); + if (!result) + return NULL; + + result[0] = '\0'; + for (int i = 0; arr[i] != NULL; i++) { + if (i > 0) + strcat(result, sep); + strcat(result, arr[i]); + } + return result; +} + +char *join_strings_with_suffix(char *arr[], const char *suffix, + const char *sep) { + if (!arr || !arr[0]) { + char *empty = malloc(1); + if (empty) + empty[0] = '\0'; + return empty; + } + + size_t total_len = 0; + int count = 0; + for (int i = 0; arr[i] != NULL; i++) { + total_len += strlen(arr[i]) + strlen(suffix); + count++; + } + if (count > 0) { + total_len += strlen(sep) * (count - 1); + } + + char *result = malloc(total_len + 1); + if (!result) + return NULL; + + result[0] = '\0'; + for (int i = 0; arr[i] != NULL; i++) { + if (i > 0) + strcat(result, sep); + strcat(result, arr[i]); + strcat(result, suffix); + } + return result; +} + +char *string_printf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + int len = vsnprintf(NULL, 0, fmt, args); + va_end(args); + if (len < 0) + return NULL; + + char *str = malloc(len + 1); + if (!str) + return NULL; + + va_start(args, fmt); + vsnprintf(str, len + 1, fmt, args); + va_end(args); + return str; +} diff --git a/src/common/util.h b/src/common/util.h index 8fb60338..cb232ac5 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -7,4 +7,8 @@ int32_t fd_set_nonblock(int32_t fd); int32_t regex_match(const char *pattern_mb, const char *str_mb); void wl_list_append(struct wl_list *list, struct wl_list *object); uint32_t get_now_in_ms(void); -uint32_t timespec_to_ms(struct timespec *ts); \ No newline at end of file +uint32_t timespec_to_ms(struct timespec *ts); +char *join_strings(char *arr[], const char *sep); +char *join_strings_with_suffix(char *arr[], const char *suffix, + const char *sep); +char *string_printf(const char *fmt, ...); \ No newline at end of file diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 556fc352..95e54c05 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -58,6 +58,7 @@ typedef struct { uint32_t tags; int32_t isfloating; int32_t isfullscreen; + int32_t isfakefullscreen; float scroller_proportion; const char *animation_type_open; const char *animation_type_close; @@ -89,6 +90,7 @@ typedef struct { int32_t isterm; int32_t allow_csd; int32_t force_maximize; + int32_t force_tiled_state; int32_t force_tearing; int32_t noswallow; int32_t noblur; @@ -209,6 +211,7 @@ typedef struct { int32_t scroller_ignore_proportion_single; int32_t scroller_focus_center; int32_t scroller_prefer_center; + int32_t scroller_prefer_overspread; int32_t edge_scroller_pointer_focus; int32_t focus_cross_monitor; int32_t exchange_cross_monitor; @@ -1014,6 +1017,7 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, (*arg).v = strdup(arg_value); } else if (strcmp(func_name, "switch_keyboard_layout") == 0) { func = switch_keyboard_layout; + (*arg).i = CLAMP_INT(atoi(arg_value), 0, 100); } else if (strcmp(func_name, "setlayout") == 0) { func = setlayout; (*arg).v = strdup(arg_value); @@ -1335,6 +1339,8 @@ bool parse_option(Config *config, char *key, char *value) { config->scroller_focus_center = atoi(value); } else if (strcmp(key, "scroller_prefer_center") == 0) { config->scroller_prefer_center = atoi(value); + } else if (strcmp(key, "scroller_prefer_overspread") == 0) { + config->scroller_prefer_overspread = 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) { @@ -1994,6 +2000,7 @@ bool parse_option(Config *config, char *key, char *value) { // int32_t rule value, relay to a client property rule->isfloating = -1; rule->isfullscreen = -1; + rule->isfakefullscreen = -1; rule->isnoborder = -1; rule->isnoshadow = -1; rule->isnoradius = -1; @@ -2011,6 +2018,7 @@ bool parse_option(Config *config, char *key, char *value) { rule->isterm = -1; rule->allow_csd = -1; rule->force_maximize = -1; + rule->force_tiled_state = -1; rule->force_tearing = -1; rule->noswallow = -1; rule->noblur = -1; @@ -2123,6 +2131,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->allow_csd = atoi(val); } else if (strcmp(key, "force_maximize") == 0) { rule->force_maximize = atoi(val); + } else if (strcmp(key, "force_tiled_state") == 0) { + rule->force_tiled_state = atoi(val); } else if (strcmp(key, "force_tearing") == 0) { rule->force_tearing = atoi(val); } else if (strcmp(key, "noswallow") == 0) { @@ -2133,6 +2143,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->scroller_proportion = atof(val); } else if (strcmp(key, "isfullscreen") == 0) { rule->isfullscreen = atoi(val); + } else if (strcmp(key, "isfakefullscreen") == 0) { + rule->isfakefullscreen = 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); @@ -3094,6 +3106,8 @@ void override_config(void) { 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); + scroller_prefer_overspread = + CLAMP_INT(config.scroller_prefer_overspread, 0, 1); edge_scroller_pointer_focus = CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1); scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000); @@ -3293,6 +3307,7 @@ void set_value_default() { scroller_ignore_proportion_single; config.scroller_focus_center = scroller_focus_center; config.scroller_prefer_center = scroller_prefer_center; + config.scroller_prefer_overspread = scroller_prefer_overspread; config.edge_scroller_pointer_focus = edge_scroller_pointer_focus; config.focus_cross_monitor = focus_cross_monitor; config.exchange_cross_monitor = exchange_cross_monitor; @@ -3632,6 +3647,16 @@ void reapply_cursor_style(void) { cursor_mgr = wlr_xcursor_manager_create(config.cursor_theme, cursor_size); + if (cursor_size > 0) { + char size_str[16]; + snprintf(size_str, sizeof(size_str), "%d", cursor_size); + setenv("XCURSOR_SIZE", size_str, 1); + } + + if (config.cursor_theme) { + setenv("XCURSOR_THEME", config.cursor_theme, 1); + } + Monitor *m = NULL; wl_list_for_each(m, &mons, link) { wlr_xcursor_manager_load(cursor_mgr, m->wlr_output->scale); diff --git a/src/config/preset.h b/src/config/preset.h index ae4424f9..d9824588 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -66,6 +66,7 @@ float scroller_default_proportion_single = 1.0; int32_t scroller_ignore_proportion_single = 1; int32_t scroller_focus_center = 0; int32_t scroller_prefer_center = 0; +int32_t scroller_prefer_overspread = 1; int32_t focus_cross_monitor = 0; int32_t focus_cross_tag = 0; int32_t exchange_cross_monitor = 0; diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 1d29bc97..03d34cbb 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1,5 +1,6 @@ int32_t bind_to_view(const Arg *arg) { - + if (!selmon) + return 0; uint32_t target = arg->ui; if (view_current_to_back && selmon->pertag->curtag && @@ -100,6 +101,8 @@ int32_t defaultgaps(const Arg *arg) { } int32_t exchange_client(const Arg *arg) { + if (!selmon) + return 0; Client *c = selmon->sel; if (!c || c->isfloating) return 0; @@ -112,6 +115,9 @@ int32_t exchange_client(const Arg *arg) { } int32_t exchange_stack_client(const Arg *arg) { + if (!selmon) + return 0; + Client *c = selmon->sel; Client *tc = NULL; if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen) @@ -204,7 +210,7 @@ int32_t focusmon(const Arg *arg) { if (!m->wlr_output->enabled) { continue; } - if (regex_match(arg->v, m->wlr_output->name)) { + if (match_monitor_spec(arg->v, m)) { tm = m; break; } @@ -265,42 +271,56 @@ int32_t incnmaster(const Arg *arg) { } int32_t incgaps(const Arg *arg) { + if (!selmon) + return 0; setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, selmon->gappih + arg->i, selmon->gappiv + arg->i); return 0; } int32_t incigaps(const Arg *arg) { + if (!selmon) + return 0; setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i, selmon->gappiv + arg->i); return 0; } int32_t incogaps(const Arg *arg) { + if (!selmon) + return 0; setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, selmon->gappih, selmon->gappiv); return 0; } int32_t incihgaps(const Arg *arg) { + if (!selmon) + return 0; setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i, selmon->gappiv); return 0; } int32_t incivgaps(const Arg *arg) { + if (!selmon) + return 0; setgaps(selmon->gappoh, selmon->gappov, selmon->gappih, selmon->gappiv + arg->i); return 0; } int32_t incohgaps(const Arg *arg) { + if (!selmon) + return 0; setgaps(selmon->gappoh + arg->i, selmon->gappov, selmon->gappih, selmon->gappiv); return 0; } int32_t incovgaps(const Arg *arg) { + if (!selmon) + return 0; setgaps(selmon->gappoh, selmon->gappov + arg->i, selmon->gappih, selmon->gappiv); return 0; @@ -330,6 +350,8 @@ int32_t setmfact(const Arg *arg) { int32_t killclient(const Arg *arg) { Client *c = NULL; + if (!selmon) + return 0; c = selmon->sel; if (c) { pending_kill_client(c); @@ -399,6 +421,8 @@ int32_t moveresize(const Arg *arg) { int32_t movewin(const Arg *arg) { Client *c = NULL; + if (!selmon) + return 0; c = selmon->sel; if (!c || c->isfullscreen) return 0; @@ -442,6 +466,8 @@ int32_t quit(const Arg *arg) { int32_t resizewin(const Arg *arg) { Client *c = NULL; + if (!selmon) + return 0; c = selmon->sel; int32_t offsetx = 0, offsety = 0; @@ -546,6 +572,8 @@ int32_t restore_minimized(const Arg *arg) { int32_t setlayout(const Arg *arg) { int32_t jk; + if (!selmon) + return 0; for (jk = 0; jk < LENGTH(layouts); jk++) { if (strcmp(layouts[jk].name, arg->v) == 0) { @@ -571,6 +599,8 @@ int32_t setkeymode(const Arg *arg) { } int32_t set_proportion(const Arg *arg) { + if (!selmon) + return 0; if (selmon->isoverview || !is_scroller_layout(selmon)) return 0; @@ -596,6 +626,8 @@ int32_t smartmovewin(const Arg *arg) { Client *c = NULL, *tc = NULL; int32_t nx, ny; int32_t buttom, top, left, right, tar; + if (!selmon) + return 0; c = selmon->sel; if (!c || c->isfullscreen) return 0; @@ -697,6 +729,8 @@ int32_t smartresizewin(const Arg *arg) { Client *c = NULL, *tc = NULL; int32_t nw, nh; int32_t buttom, top, left, right, tar; + if (!selmon) + return 0; c = selmon->sel; if (!c || c->isfullscreen) return 0; @@ -765,6 +799,8 @@ int32_t smartresizewin(const Arg *arg) { int32_t centerwin(const Arg *arg) { Client *c = NULL; + if (!selmon) + return 0; c = selmon->sel; if (!c || c->isfullscreen || c->ismaximizescreen) @@ -900,7 +936,13 @@ int32_t switch_keyboard_layout(const Arg *arg) { wlr_log(WLR_INFO, "Only one layout available"); return 0; } - xkb_layout_index_t next = (current + 1) % num_layouts; + + xkb_layout_index_t next = 0; + if (arg->i > 0 && arg->i <= num_layouts) { + next = arg->i - 1; + } else { + next = (current + 1) % num_layouts; + } // 6. 应用新 keymap uint32_t depressed = keyboard->modifiers.depressed; @@ -937,6 +979,9 @@ int32_t switch_layout(const Arg *arg) { char *target_layout_name = NULL; uint32_t len; + if (!selmon) + return 0; + if (config.circle_layout_count != 0) { for (jk = 0; jk < config.circle_layout_count; jk++) { @@ -988,6 +1033,8 @@ int32_t switch_layout(const Arg *arg) { int32_t switch_proportion_preset(const Arg *arg) { float target_proportion = 0; + if (!selmon) + return 0; if (config.scroller_proportion_preset_count == 0) { return 0; @@ -1032,6 +1079,8 @@ int32_t switch_proportion_preset(const Arg *arg) { } int32_t tag(const Arg *arg) { + if (!selmon) + return 0; Client *target_client = selmon->sel; tag_client(arg, target_client); return 0; @@ -1039,6 +1088,8 @@ int32_t tag(const Arg *arg) { int32_t tagmon(const Arg *arg) { Monitor *m = NULL, *cm = NULL; + if (!selmon) + return 0; Client *c = focustop(selmon); if (!c) @@ -1051,7 +1102,7 @@ int32_t tagmon(const Arg *arg) { if (!cm->wlr_output->enabled) { continue; } - if (regex_match(arg->v, cm->wlr_output->name)) { + if (match_monitor_spec(arg->v, cm)) { m = cm; break; } @@ -1130,6 +1181,9 @@ int32_t tagsilent(const Arg *arg) { } int32_t tagtoleft(const Arg *arg) { + if (!selmon) + return 0; + if (selmon->sel != NULL && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 && selmon->tagset[selmon->seltags] > 1) { @@ -1139,6 +1193,9 @@ int32_t tagtoleft(const Arg *arg) { } int32_t tagtoright(const Arg *arg) { + if (!selmon) + return 0; + if (selmon->sel != NULL && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { @@ -1167,6 +1224,8 @@ int32_t toggle_named_scratchpad(const Arg *arg) { } int32_t toggle_render_border(const Arg *arg) { + if (!selmon) + return 0; render_border = !render_border; arrange(selmon, false, false); return 0; @@ -1202,6 +1261,8 @@ int32_t toggle_scratchpad(const Arg *arg) { } int32_t togglefakefullscreen(const Arg *arg) { + if (!selmon) + return 0; Client *sel = focustop(selmon); if (sel) setfakefullscreen(sel, !sel->isfakefullscreen); @@ -1209,6 +1270,9 @@ int32_t togglefakefullscreen(const Arg *arg) { } int32_t togglefloating(const Arg *arg) { + if (!selmon) + return 0; + Client *sel = focustop(selmon); if (selmon && selmon->isoverview) @@ -1217,17 +1281,22 @@ int32_t togglefloating(const Arg *arg) { if (!sel) return 0; + bool isfloating = sel->isfloating; + if ((sel->isfullscreen || sel->ismaximizescreen)) { - sel->isfloating = 1; + isfloating = 1; } else { - sel->isfloating = !sel->isfloating; + isfloating = !sel->isfloating; } - setfloating(sel, sel->isfloating); + setfloating(sel, isfloating); return 0; } int32_t togglefullscreen(const Arg *arg) { + if (!selmon) + return 0; + Client *sel = focustop(selmon); if (!sel) return 0; @@ -1244,6 +1313,9 @@ int32_t togglefullscreen(const Arg *arg) { } int32_t toggleglobal(const Arg *arg) { + if (!selmon) + return 0; + if (!selmon->sel) return 0; if (selmon->sel->is_in_scratchpad) { @@ -1262,12 +1334,18 @@ int32_t toggleglobal(const Arg *arg) { } int32_t togglegaps(const Arg *arg) { + if (!selmon) + return 0; + enablegaps ^= 1; arrange(selmon, false, false); return 0; } int32_t togglemaximizescreen(const Arg *arg) { + if (!selmon) + return 0; + Client *sel = focustop(selmon); if (!sel) return 0; @@ -1286,6 +1364,9 @@ int32_t togglemaximizescreen(const Arg *arg) { } int32_t toggleoverlay(const Arg *arg) { + if (!selmon) + return 0; + if (!selmon->sel || !selmon->sel->mon || selmon->sel->isfullscreen) { return 0; } @@ -1307,6 +1388,9 @@ int32_t toggleoverlay(const Arg *arg) { } int32_t toggletag(const Arg *arg) { + if (!selmon) + return 0; + uint32_t newtags; Client *sel = focustop(selmon); if (!sel) @@ -1330,13 +1414,15 @@ int32_t toggletag(const Arg *arg) { } int32_t toggleview(const Arg *arg) { + if (!selmon) + return 0; + uint32_t newtagset; uint32_t target; target = arg->ui == 0 ? ~0 & TAGMASK : arg->ui; - newtagset = - selmon ? selmon->tagset[selmon->seltags] ^ (target & TAGMASK) : 0; + newtagset = selmon->tagset[selmon->seltags] ^ (target & TAGMASK); if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; @@ -1348,6 +1434,9 @@ int32_t toggleview(const Arg *arg) { } int32_t viewtoleft(const Arg *arg) { + if (!selmon) + return 0; + uint32_t target = selmon->tagset[selmon->seltags]; if (selmon->isoverview || selmon->pertag->curtag == 0) { @@ -1368,6 +1457,9 @@ int32_t viewtoleft(const Arg *arg) { } int32_t viewtoright(const Arg *arg) { + if (!selmon) + return 0; + if (selmon->isoverview || selmon->pertag->curtag == 0) { return 0; } @@ -1385,6 +1477,9 @@ int32_t viewtoright(const Arg *arg) { } int32_t viewtoleft_have_client(const Arg *arg) { + if (!selmon) + return 0; + uint32_t n; uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]); bool found = false; @@ -1409,6 +1504,9 @@ int32_t viewtoleft_have_client(const Arg *arg) { } int32_t viewtoright_have_client(const Arg *arg) { + if (!selmon) + return 0; + uint32_t n; uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]); bool found = false; @@ -1433,6 +1531,9 @@ int32_t viewtoright_have_client(const Arg *arg) { } int32_t viewcrossmon(const Arg *arg) { + if (!selmon) + return 0; + focusmon(&(Arg){.v = arg->v, .i = UNDIR}); view_in_mon(arg, true, selmon, true); return 0; @@ -1442,7 +1543,7 @@ int32_t tagcrossmon(const Arg *arg) { if (!selmon || !selmon->sel) return 0; - if (regex_match(selmon->wlr_output->name, arg->v)) { + if (match_monitor_spec(arg->v, selmon)) { tag_client(arg, selmon->sel); return 0; } @@ -1511,6 +1612,8 @@ int32_t setoption(const Arg *arg) { } int32_t minimized(const Arg *arg) { + if (!selmon) + return 0; if (selmon && selmon->isoverview) return 0; @@ -1521,8 +1624,20 @@ int32_t minimized(const Arg *arg) { return 0; } +void fix_mon_tagset_from_overview(Monitor *m) { + if (m->tagset[m->seltags] == (m->ovbk_prev_tagset & TAGMASK)) { + m->tagset[m->seltags ^ 1] = m->ovbk_current_tagset; + m->pertag->prevtag = get_tags_first_tag_num(m->ovbk_current_tagset); + } else { + m->tagset[m->seltags ^ 1] = m->ovbk_prev_tagset; + m->pertag->prevtag = get_tags_first_tag_num(m->ovbk_prev_tagset); + } +} + int32_t toggleoverview(const Arg *arg) { Client *c = NULL; + if (!selmon) + return 0; if (selmon->isoverview && ov_tab_mode && arg->i != 1 && selmon->sel) { focusstack(&(Arg){.i = 1}); @@ -1542,6 +1657,8 @@ int32_t toggleoverview(const Arg *arg) { visible_client_number++; } if (visible_client_number > 0) { + selmon->ovbk_current_tagset = selmon->tagset[selmon->seltags]; + selmon->ovbk_prev_tagset = selmon->tagset[selmon->seltags ^ 1]; target = ~0 & TAGMASK; } else { selmon->isoverview ^= 1; @@ -1552,6 +1669,7 @@ int32_t toggleoverview(const Arg *arg) { } else if (!selmon->isoverview && !selmon->sel) { target = (1 << (selmon->pertag->prevtag - 1)); view(&(Arg){.ui = target}, false); + fix_mon_tagset_from_overview(selmon); refresh_monitors_workspaces_status(selmon); return 0; } @@ -1574,7 +1692,7 @@ int32_t toggleoverview(const Arg *arg) { } view(&(Arg){.ui = target}, false); - + fix_mon_tagset_from_overview(selmon); refresh_monitors_workspaces_status(selmon); return 0; } @@ -1583,7 +1701,7 @@ int32_t disable_monitor(const Arg *arg) { Monitor *m = NULL; struct wlr_output_state state = {0}; wl_list_for_each(m, &mons, link) { - if (regex_match(arg->v, m->wlr_output->name)) { + if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&state, false); wlr_output_commit_state(m->wlr_output, &state); m->asleep = 1; @@ -1598,7 +1716,7 @@ int32_t enable_monitor(const Arg *arg) { Monitor *m = NULL; struct wlr_output_state state = {0}; wl_list_for_each(m, &mons, link) { - if (regex_match(arg->v, m->wlr_output->name)) { + if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&state, true); wlr_output_commit_state(m->wlr_output, &state); m->asleep = 0; @@ -1613,7 +1731,7 @@ int32_t toggle_monitor(const Arg *arg) { Monitor *m = NULL; struct wlr_output_state state = {0}; wl_list_for_each(m, &mons, link) { - if (regex_match(arg->v, m->wlr_output->name)) { + if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&state, !m->wlr_output->enabled); wlr_output_commit_state(m->wlr_output, &state); m->asleep = !m->wlr_output->enabled; @@ -1625,6 +1743,8 @@ int32_t toggle_monitor(const Arg *arg) { } int32_t scroller_stack(const Arg *arg) { + if (!selmon) + return 0; Client *c = selmon->sel; Client *stack_head = NULL; Client *source_stack_head = NULL; diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h index dbd97e11..e9f221a4 100644 --- a/src/ext-protocol/text-input.h +++ b/src/ext-protocol/text-input.h @@ -77,15 +77,6 @@ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) { return NULL; } -Monitor *output_nearest_to(int32_t lx, int32_t ly) { - double closest_x, closest_y; - wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x, - &closest_y); - - return output_from_wlr_output( - wlr_output_layout_output_at(output_layout, closest_x, closest_y)); -} - bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; } static bool @@ -255,7 +246,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) { cursor_rect = (struct wlr_box){0}; } - output = output_nearest_to(cursor_rect.x, cursor_rect.y); + output = get_monitor_nearest_to(cursor_rect.x, cursor_rect.y); if (!output_is_usable(output)) { return; } diff --git a/src/fetch/client.h b/src/fetch/client.h index bf30e175..11edb76b 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -83,6 +83,9 @@ setclient_coordinate_center(Client *c, Monitor *tm, struct wlr_box geom, int32_t len = 0; Monitor *m = tm ? tm : selmon; + if (!m) + return geom; + uint32_t cbw = check_hit_no_border(c) ? c->bw : 0; if (!c->no_force_center && m) { @@ -574,23 +577,29 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) { if (id == TILE || id == VERTICAL_TILE || id == DECK || id == VERTICAL_DECK || id == RIGHT_TILE) { - if (fc && !fc->ismaster) + if (tc->ismaster ^ sc->ismaster) return false; - else if (!sc->ismaster) + if (fc && !(fc->ismaster ^ sc->ismaster)) + return false; + else return true; } if (id == TGMIX) { - if (fc && !fc->ismaster) + if (tc->ismaster ^ sc->ismaster) + return false; + if (fc && !(fc->ismaster ^ sc->ismaster)) return false; if (!sc->ismaster && sc->mon->visible_tiling_clients <= 3) return true; } if (id == CENTER_TILE) { - if (fc && !fc->ismaster) + if (tc->ismaster ^ sc->ismaster) return false; - if (!sc->ismaster && sc->geom.x == tc->geom.x) + if (fc && !(fc->ismaster ^ sc->ismaster)) + return false; + if (sc->geom.x == tc->geom.x) return true; else return false; diff --git a/src/fetch/common.h b/src/fetch/common.h index 58e69dc1..57a1a8e6 100644 --- a/src/fetch/common.h +++ b/src/fetch/common.h @@ -100,10 +100,6 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, surface = wlr_scene_surface_try_from_buffer( wlr_scene_buffer_from_node(node)) ->surface; - else if (node->type == WLR_SCENE_NODE_RECT) { - surface = NULL; - break; - } /* start from the topmost layer, find a sureface that can be focused by pointer, @@ -119,6 +115,13 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, l = pnode->data; } } + + if (node->type == WLR_SCENE_NODE_RECT) { + if (c) { + surface = client_surface(c); + } + break; + } } if (psurface) diff --git a/src/fetch/monitor.h b/src/fetch/monitor.h index 7a1ca4dc..d2b4fe62 100644 --- a/src/fetch/monitor.h +++ b/src/fetch/monitor.h @@ -96,3 +96,81 @@ Monitor *xytomon(double x, double y) { struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); return o ? o->data : NULL; } + +Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly) { + double closest_x, closest_y; + wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x, + &closest_y); + + return output_from_wlr_output( + wlr_output_layout_output_at(output_layout, closest_x, closest_y)); +} + +bool match_monitor_spec(char *spec, Monitor *m) { + if (!spec || !m) + return false; + + // if the spec does not contain a colon, treat it as a match on the monitor + // name + if (strchr(spec, ':') == NULL) { + return regex_match(spec, m->wlr_output->name); + } + + char *spec_copy = strdup(spec); + if (!spec_copy) + return false; + + char *name_rule = NULL; + char *make_rule = NULL; + char *model_rule = NULL; + char *serial_rule = NULL; + + char *token = strtok(spec_copy, "&&"); + while (token) { + char *colon = strchr(token, ':'); + if (colon) { + *colon = '\0'; + char *key = token; + char *value = colon + 1; + + if (strcmp(key, "name") == 0) + name_rule = strdup(value); + else if (strcmp(key, "make") == 0) + make_rule = strdup(value); + else if (strcmp(key, "model") == 0) + model_rule = strdup(value); + else if (strcmp(key, "serial") == 0) + serial_rule = strdup(value); + } + token = strtok(NULL, "&&"); + } + + bool match = true; + + if (name_rule) { + if (!regex_match(name_rule, m->wlr_output->name)) + match = false; + } + if (make_rule) { + if (!m->wlr_output->make || strcmp(make_rule, m->wlr_output->make) != 0) + match = false; + } + if (model_rule) { + if (!m->wlr_output->model || + strcmp(model_rule, m->wlr_output->model) != 0) + match = false; + } + if (serial_rule) { + if (!m->wlr_output->serial || + strcmp(serial_rule, m->wlr_output->serial) != 0) + match = false; + } + + free(spec_copy); + free(name_rule); + free(make_rule); + free(model_rule); + free(serial_rule); + + return match; +} \ No newline at end of file diff --git a/src/layout/arrange.h b/src/layout/arrange.h index 37213640..1ef89c3a 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -1,3 +1,45 @@ +void restore_size_per(Monitor *m, Client *c) { + Client *fc = NULL; + double total_master_inner_per = 0; + double total_stack_inner_per = 0; + + if (!m || !c) + return; + + const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag]; + + if (current_layout->id == SCROLLER || + current_layout->id == VERTICAL_SCROLLER || current_layout->id == GRID || + current_layout->id == VERTICAL_GRID || current_layout->id == DECK || + current_layout->id == VERTICAL_DECK || current_layout->id == MONOCLE) { + return; + } + + if (current_layout->id == CENTER_TILE || c->ismaster) { + wl_list_for_each(fc, &clients, link) { + if (VISIBLEON(fc, m) && ISTILED(fc) && !c->ismaster) { + set_size_per(m, fc); + } + } + return; + } + + wl_list_for_each(fc, &clients, link) { + if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) { + if (fc->ismaster) { + total_master_inner_per += fc->master_inner_per; + } else { + total_stack_inner_per += fc->stack_inner_per; + } + } + } + + if (!c->ismaster && total_stack_inner_per) { + c->stack_inner_per = total_stack_inner_per * c->stack_inner_per / + (1 - c->stack_inner_per); + } +} + void set_size_per(Monitor *m, Client *c) { Client *fc = NULL; bool found = false; @@ -5,8 +47,13 @@ void set_size_per(Monitor *m, Client *c) { if (!m || !c) return; + const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag]; + wl_list_for_each(fc, &clients, link) { if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) { + if (current_layout->id == CENTER_TILE && + !(fc->isleftstack ^ c->isleftstack)) + continue; c->master_mfact_per = fc->master_mfact_per; c->master_inner_per = fc->master_inner_per; c->stack_inner_per = fc->stack_inner_per; diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index e1a335d1..8140934a 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -283,6 +283,7 @@ void scroller(Monitor *m) { struct wlr_box target_geom; int32_t focus_client_index = 0; bool need_scroller = false; + bool over_overspread_to_left = false; int32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0; @@ -371,6 +372,46 @@ void scroller(Monitor *m) { } } + bool need_apply_overspread = + scroller_prefer_overspread && m->visible_scroll_tiling_clients > 1 && + (focus_client_index == 0 || focus_client_index == n - 1) && + tempClients[focus_client_index]->scroller_proportion < 1.0f; + + if (need_apply_overspread) { + + if (focus_client_index == 0) { + over_overspread_to_left = true; + } else { + over_overspread_to_left = false; + } + + if (over_overspread_to_left && + (!INSIDEMON(tempClients[1]) || + (tempClients[1]->scroller_proportion + + tempClients[focus_client_index]->scroller_proportion >= + 1.0f))) { + need_scroller = true; + } else if (!over_overspread_to_left && + (!INSIDEMON(tempClients[focus_client_index - 1]) || + (tempClients[focus_client_index - 1]->scroller_proportion + + tempClients[focus_client_index]->scroller_proportion >= + 1.0f))) { + need_scroller = true; + } else { + need_apply_overspread = false; + } + } + + bool need_apply_center = + scroller_focus_center || m->visible_scroll_tiling_clients == 1 || + (scroller_prefer_center && !need_apply_overspread && + (!m->prevsel || + (ISSCROLLTILED(m->prevsel) && + (m->prevsel->scroller_proportion * max_client_width) + + (tempClients[focus_client_index]->scroller_proportion * + max_client_width) > + m->w.width - 2 * scroller_structs - cur_gappih))); + if (n == 1 && scroller_ignore_proportion_single) { need_scroller = true; } @@ -394,18 +435,26 @@ void scroller(Monitor *m) { tempClients[focus_client_index], &target_geom); arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv); } else if (need_scroller) { - if (scroller_focus_center || - ((!m->prevsel || - (ISSCROLLTILED(m->prevsel) && - (m->prevsel->scroller_proportion * max_client_width) + - (root_client->scroller_proportion * max_client_width) > - m->w.width - 2 * scroller_structs - cur_gappih)) && - scroller_prefer_center)) { + if (need_apply_center) { target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; + } else if (need_apply_overspread) { + if (over_overspread_to_left) { + target_geom.x = m->w.x + scroller_structs; + } else { + target_geom.x = + m->w.x + + (m->w.width - + tempClients[focus_client_index]->scroller_proportion * + max_client_width - + scroller_structs); + } + } else { - target_geom.x = root_client->geom.x > m->w.x + (m->w.width) / 2 + target_geom.x = tempClients[focus_client_index]->geom.x > + m->w.x + (m->w.width) / 2 ? m->w.x + (m->w.width - - root_client->scroller_proportion * + tempClients[focus_client_index] + ->scroller_proportion * max_client_width - scroller_structs) : m->w.x + scroller_structs; diff --git a/src/layout/vertical.h b/src/layout/vertical.h index f7bd442c..4759e7a5 100644 --- a/src/layout/vertical.h +++ b/src/layout/vertical.h @@ -270,6 +270,7 @@ void vertical_scroller(Monitor *m) { struct wlr_box target_geom; int32_t focus_client_index = 0; bool need_scroller = false; + bool over_overspread_to_up = false; int32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0; @@ -355,6 +356,46 @@ void vertical_scroller(Monitor *m) { } } + bool need_apply_overspread = + scroller_prefer_overspread && m->visible_scroll_tiling_clients > 1 && + (focus_client_index == 0 || focus_client_index == n - 1) && + tempClients[focus_client_index]->scroller_proportion < 1.0f; + + if (need_apply_overspread) { + + if (focus_client_index == 0) { + over_overspread_to_up = true; + } else { + over_overspread_to_up = false; + } + + if (over_overspread_to_up && + (!INSIDEMON(tempClients[1]) || + (tempClients[1]->scroller_proportion + + tempClients[focus_client_index]->scroller_proportion >= + 1.0f))) { + need_scroller = true; + } else if (!over_overspread_to_up && + (!INSIDEMON(tempClients[focus_client_index - 1]) || + (tempClients[focus_client_index - 1]->scroller_proportion + + tempClients[focus_client_index]->scroller_proportion >= + 1.0f))) { + need_scroller = true; + } else { + need_apply_overspread = false; + } + } + + bool need_apply_center = + scroller_focus_center || m->visible_scroll_tiling_clients == 1 || + (scroller_prefer_center && !need_apply_overspread && + (!m->prevsel || + (ISSCROLLTILED(m->prevsel) && + (m->prevsel->scroller_proportion * max_client_height) + + (tempClients[focus_client_index]->scroller_proportion * + max_client_height) > + m->w.height - 2 * scroller_structs - cur_gappiv))); + if (n == 1 && scroller_ignore_proportion_single) { need_scroller = true; } @@ -381,18 +422,24 @@ void vertical_scroller(Monitor *m) { arrange_stack_vertical(tempClients[focus_client_index], target_geom, cur_gappih); } else if (need_scroller) { - if (scroller_focus_center || - ((!m->prevsel || - (ISSCROLLTILED(m->prevsel) && - (m->prevsel->scroller_proportion * max_client_height) + - (root_client->scroller_proportion * max_client_height) > - m->w.height - 2 * scroller_structs - cur_gappiv)) && - scroller_prefer_center)) { + if (need_apply_center) { target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; + } else if (need_apply_overspread) { + if (over_overspread_to_up) { + target_geom.y = m->w.y + scroller_structs; + } else { + target_geom.y = + m->w.y + + (m->w.height - + tempClients[focus_client_index]->scroller_proportion * + max_client_height - + scroller_structs); + } } else { target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2 ? m->w.y + (m->w.height - - root_client->scroller_proportion * + tempClients[focus_client_index] + ->scroller_proportion * max_client_height - scroller_structs) : m->w.y + scroller_structs; diff --git a/src/mango.c b/src/mango.c index 3ce74222..d4cc1fba 100644 --- a/src/mango.c +++ b/src/mango.c @@ -3,6 +3,7 @@ */ #include "wlr-layer-shell-unstable-v1-protocol.h" #include "wlr/util/box.h" +#include "wlr/util/edges.h" #include #include #include @@ -337,6 +338,7 @@ struct Client { struct wl_listener configure; struct wl_listener set_hints; struct wl_listener set_geometry; + struct wl_listener commmitx11; #endif uint32_t bw; uint32_t tags, oldtags, mini_restore_tag; @@ -390,6 +392,7 @@ struct Client { int32_t isterm, noswallow; int32_t allow_csd; int32_t force_maximize; + int32_t force_tiled_state; pid_t pid; Client *swallowing, *swallowedby; bool is_clip_to_hide; @@ -483,6 +486,14 @@ typedef struct { bool being_unmapped; } LayerSurface; +typedef struct { + struct wlr_xdg_popup *wlr_popup; + uint32_t type; + struct wl_listener destroy; + struct wl_listener commit; + struct wl_listener reposition; +} Popup; + typedef struct { const char *symbol; void (*arrange)(Monitor *); @@ -500,11 +511,15 @@ struct Monitor { struct wl_listener request_state; struct wl_listener destroy_lock_surface; struct wlr_session_lock_surface_v1 *lock_surface; + struct wl_event_source *skip_frame_timeout; 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]; + bool skiping_frame; + uint32_t resizing_count_pending; + uint32_t resizing_count_current; struct wl_list dwl_ipc_outputs; int32_t gappih; /* horizontal gap between windows */ @@ -512,6 +527,8 @@ struct Monitor { int32_t gappoh; /* horizontal outer gaps */ int32_t gappov; /* vertical outer gaps */ Pertag *pertag; + uint32_t ovbk_current_tagset; + uint32_t ovbk_prev_tagset; Client *sel, *prevsel; int32_t isoverview; int32_t is_in_hotarea; @@ -779,6 +796,10 @@ 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); +static void monitor_stop_skip_frame_timer(Monitor *m); +static int monitor_skip_frame_timeout_callback(void *data); +static Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly); +static bool match_monitor_spec(char *spec, Monitor *m); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -883,6 +904,9 @@ static KeyMode keymode = { .mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'}, .isdefault = true, }; + +static char *env_vars[] = {"DISPLAY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP", + "XDG_SESSION_TYPE", NULL}; static struct { enum wp_cursor_shape_device_v1_shape shape; struct wlr_surface *surface; @@ -942,11 +966,13 @@ static struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor = { .notify = handle_keyboard_shortcuts_inhibit_new_inhibitor}; #ifdef XWAYLAND +static void fix_xwayland_unmanaged_coordinate(Client *c); static int32_t synckeymap(void *data); 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 commitx11(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); @@ -1283,6 +1309,7 @@ static 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); + APPLY_INT_PROP(c, r, force_tiled_state); APPLY_INT_PROP(c, r, force_tearing); APPLY_INT_PROP(c, r, noswallow); APPLY_INT_PROP(c, r, nofocus); @@ -1291,6 +1318,7 @@ static void apply_rule_properties(Client *c, const ConfigWinRule *r) { APPLY_INT_PROP(c, r, no_force_center); APPLY_INT_PROP(c, r, isfloating); APPLY_INT_PROP(c, r, isfullscreen); + APPLY_INT_PROP(c, r, isfakefullscreen); APPLY_INT_PROP(c, r, isnoborder); APPLY_INT_PROP(c, r, isnoshadow); APPLY_INT_PROP(c, r, isnoradius); @@ -1366,11 +1394,22 @@ void applyrules(Client *c) { Client *fc = NULL; Client *parent = NULL; + if (!c) + return; + parent = client_get_parent(c); Monitor *mon = parent && parent->mon ? parent->mon : selmon; c->isfloating = client_is_float_type(c) || parent; + +#ifdef XWAYLAND + if (c->isfloating && client_is_x11(c)) { + fix_xwayland_unmanaged_coordinate(c); + c->float_geom = c->geom; + } +#endif + if (!(appid = client_get_appid(c))) appid = broken; if (!(title = client_get_title(c))) @@ -1446,7 +1485,8 @@ void applyrules(Client *c) { /*-----------------------apply rule action-------------------------*/ // rule action only apply after map not apply in the init commit - if (!client_surface(c)->mapped) + struct wlr_surface *surface = client_surface(c); + if (!surface || !surface->mapped) return; // apply swallow rule @@ -1472,6 +1512,7 @@ void applyrules(Client *c) { setmon(c, mon, newtags, !c->isopensilent && !(client_is_x11_popup(c) && client_should_ignore_focus(c)) && + mon && (!c->istagsilent || !newtags || newtags & mon->tagset[mon->seltags])); @@ -1484,12 +1525,16 @@ void applyrules(Client *c) { setfullscreen(c, fullscreen_state_backup); + if (c->isfakefullscreen) { + setfakefullscreen(c, 1); + } + /* if there is a new non-floating window in the current tag, the fullscreen window in the current tag will exit fullscreen and participate in tiling */ wl_list_for_each(fc, &clients, - link) if (fc && fc != c && c->tags & fc->tags && + link) if (fc && fc != c && c->tags & fc->tags && c->mon && VISIBLEON(fc, c->mon) && ISFULLSCREEN(fc) && !c->isfloating) { clear_fullscreen_flag(fc); @@ -1508,7 +1553,7 @@ void applyrules(Client *c) { } // apply overlay rule - if (c->isoverlay) { + if (c->isoverlay && c->scene) { wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); wlr_scene_node_raise_to_top(&c->scene->node); } @@ -2204,6 +2249,11 @@ void cleanupmon(struct wl_listener *listener, void *data) { wlr_scene_node_destroy(&m->blur->node); m->blur = NULL; } + if (m->skip_frame_timeout) { + monitor_stop_skip_frame_timer(m); + wl_event_source_remove(m->skip_frame_timeout); + m->skip_frame_timeout = NULL; + } m->wlr_output->data = NULL; free(m->pertag); free(m); @@ -2263,6 +2313,19 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, } } +void layer_flush_blur_background(LayerSurface *l) { + if (!blur) + return; + + // 如果背景层发生变化,标记优化的模糊背景缓存需要更新 + if (l->layer_surface->current.layer == + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { + if (l->mon) { + wlr_scene_optimized_blur_mark_dirty(l->mon->blur); + } + } +} + void maplayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *l = wl_container_of(listener, l, map); struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; @@ -2390,15 +2453,7 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { } } - if (blur) { - // 如果背景层发生变化,标记优化的模糊背景缓存需要更新 - if (layer_surface->current.layer == - ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { - if (l->mon) { - wlr_scene_optimized_blur_mark_dirty(l->mon->blur); - } - } - } + layer_flush_blur_background(l); if (layer_surface == exclusive_focus && layer_surface->current.keyboard_interactive != @@ -2438,9 +2493,6 @@ void commitnotify(struct wl_listener *listener, void *data) { setmon(c, NULL, 0, true); /* Make sure to reapply rules in mapnotify() */ - client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | - WLR_EDGE_RIGHT); - uint32_t serial = wlr_xdg_surface_schedule_configure(c->surface.xdg); if (serial > 0) { c->configure_serial = serial; @@ -2500,37 +2552,105 @@ void destroydecoration(struct wl_listener *listener, void *data) { wl_list_remove(&c->set_decoration_mode.link); } -void commitpopup(struct wl_listener *listener, void *data) { +static void popup_unconstrain(Popup *popup) { + struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + + if (!wlr_popup || !wlr_popup->parent) { + return; + } + + struct wlr_scene_node *parent_node = wlr_popup->parent->data; + if (!parent_node) { + wlr_log(WLR_ERROR, "Popup parent has no scene node"); + return; + } + int parent_lx, parent_ly; + wlr_scene_node_coords(parent_node, &parent_lx, &parent_ly); + + struct wlr_box *scheduled = &wlr_popup->scheduled.geometry; + int popup_lx = parent_lx + scheduled->x; + int popup_ly = parent_ly + scheduled->y; + + Monitor *mon = get_monitor_nearest_to(popup_lx, popup_ly); + + struct wlr_box usable = popup->type == LayerShell ? mon->m : mon->w; + + struct wlr_box constraint_box = { + .x = usable.x - parent_lx, + .y = usable.y - parent_ly, + .width = usable.width, + .height = usable.height, + }; + + wlr_xdg_popup_unconstrain_from_box(wlr_popup, &constraint_box); +} + +static void destroypopup(struct wl_listener *listener, void *data) { + Popup *popup = wl_container_of(listener, popup, destroy); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->reposition.link); + free(popup); +} + +static void commitpopup(struct wl_listener *listener, void *data) { + Popup *popup = wl_container_of(listener, popup, commit); + struct wlr_surface *surface = data; - struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface); - LayerSurface *l = NULL; + struct wlr_xdg_popup *wkr_popup = + wlr_xdg_popup_try_from_wlr_surface(surface); + Client *c = NULL; - struct wlr_box box; + LayerSurface *l = NULL; int32_t type = -1; - if (!popup || !popup->base->initial_commit) + if (!wkr_popup || !wkr_popup->base->initial_commit) goto commitpopup_listen_free; - type = toplevel_from_wlr_surface(popup->base->surface, &c, &l); - if (!popup->parent || !popup->parent->data || type < 0) - goto commitpopup_listen_free; - - wlr_scene_node_raise_to_top(popup->parent->data); - - popup->base->surface->data = - wlr_scene_xdg_surface_create(popup->parent->data, popup->base); - if ((l && !l->mon) || (c && !c->mon)) { - wlr_xdg_popup_destroy(popup); + type = toplevel_from_wlr_surface(wkr_popup->base->surface, &c, &l); + if (!wkr_popup->parent || !wkr_popup->parent->data || type < 0) { + wlr_xdg_popup_destroy(wkr_popup); goto commitpopup_listen_free; } - box = type == LayerShell ? l->mon->m : c->mon->w; - box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x); - box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y); - wlr_xdg_popup_unconstrain_from_box(popup, &box); + + wlr_scene_node_raise_to_top(wkr_popup->parent->data); + + wkr_popup->base->surface->data = + wlr_scene_xdg_surface_create(wkr_popup->parent->data, wkr_popup->base); + if ((l && !l->mon) || (c && !c->mon)) { + wlr_xdg_popup_destroy(wkr_popup); + goto commitpopup_listen_free; + } + + popup->type = type; + popup->wlr_popup = wkr_popup; + + popup_unconstrain(popup); commitpopup_listen_free: - wl_list_remove(&listener->link); - free(listener); + wl_list_remove(&popup->commit.link); + popup->commit.notify = NULL; +} + +static void repositionpopup(struct wl_listener *listener, void *data) { + Popup *popup = wl_container_of(listener, popup, reposition); + popup_unconstrain(popup); +} + +static void createpopup(struct wl_listener *listener, void *data) { + struct wlr_xdg_popup *wlr_popup = data; + + Popup *popup = calloc(1, sizeof(Popup)); + if (!popup) + return; + + popup->destroy.notify = destroypopup; + wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); + + popup->commit.notify = commitpopup; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + + popup->reposition.notify = repositionpopup; + wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); } void createdecoration(struct wl_listener *listener, void *data) { @@ -2750,7 +2870,15 @@ void createmon(struct wl_listener *listener, void *data) { return; } + struct wl_event_loop *loop = wl_display_get_event_loop(dpy); m = wlr_output->data = ecalloc(1, sizeof(*m)); + + m->skip_frame_timeout = + wl_event_loop_add_timer(loop, monitor_skip_frame_timeout_callback, m); + m->skiping_frame = false; + m->resizing_count_pending = 0; + m->resizing_count_current = 0; + m->wlr_output = wlr_output; m->wlr_output->data = m; @@ -2768,6 +2896,8 @@ void createmon(struct wl_listener *listener, void *data) { m->isoverview = 0; m->sel = NULL; m->is_in_hotarea = 0; + m->m.x = INT32_MAX; + m->m.y = INT32_MAX; float scale = 1; enum wl_output_transform rr = WL_OUTPUT_TRANSFORM_NORMAL; wlr_output_state_set_scale(&state, scale); @@ -3110,13 +3240,6 @@ void createpointerconstraint(struct wl_listener *listener, void *data) { &pointer_constraint->destroy, destroypointerconstraint); } -void createpopup(struct wl_listener *listener, void *data) { - /* This event is raised when a client (either xdg-shell or layer-shell) - * creates a new popup. */ - struct wlr_xdg_popup *popup = data; - LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); -} - void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) { if (active_constraint == constraint) return; @@ -3814,6 +3937,23 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, void init_client_properties(Client *c) { c->isfocusing = false; + c->isfloating = 0; + c->isfakefullscreen = 0; + c->isnoanimation = 0; + c->isopensilent = 0; + c->istagsilent = 0; + c->noswallow = 0; + c->isterm = 0; + c->noblur = 0; + c->tearing_hint = 0; + c->overview_isfullscreenbak = 0; + c->overview_ismaximizescreenbak = 0; + c->overview_isfloatingbak = 0; + c->pid = 0; + c->swallowing = NULL; + c->swallowedby = NULL; + c->ismaster = 0; + c->isleftstack = 0; c->ismaximizescreen = 0; c->isfullscreen = 0; c->need_float_size_reduce = 0; @@ -3856,6 +3996,7 @@ void init_client_properties(Client *c) { c->isterm = 0; c->allow_csd = 0; c->force_maximize = 0; + c->force_tiled_state = 1; c->force_tearing = 0; c->allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; c->scroller_proportion_single = 0.0f; @@ -3866,6 +4007,7 @@ void init_client_properties(Client *c) { c->stack_proportion = 0.0f; c->next_in_stack = NULL; c->prev_in_stack = NULL; + memset(c->oldmonname, 0, sizeof(c->oldmonname)); memcpy(c->opacity_animation.initial_border_color, bordercolor, sizeof(c->opacity_animation.initial_border_color)); memcpy(c->opacity_animation.current_border_color, bordercolor, @@ -3913,18 +4055,19 @@ mapnotify(struct wl_listener *listener, void *data) { */ if (client_is_unmanaged(c)) { /* Unmanaged clients always are floating */ +#ifdef XWAYLAND + if (client_is_x11(c)) { + fix_xwayland_unmanaged_coordinate(c); + LISTEN(&c->surface.xwayland->events.set_geometry, &c->set_geometry, + setgeometrynotify); + } +#endif wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); if (client_wants_focus(c)) { focusclient(c, 1); exclusive_focus = c; } -#ifdef XWAYLAND - if (client_is_x11(c)) { - LISTEN(&c->surface.xwayland->events.set_geometry, &c->set_geometry, - setgeometrynotify); - } -#endif return; } @@ -3970,8 +4113,10 @@ mapnotify(struct wl_listener *listener, void *data) { applyrules(c); - client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | - WLR_EDGE_RIGHT); + if (!c->isfloating || c->force_tiled_state) { + client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | + WLR_EDGE_RIGHT); + } // apply buffer effects of client wlr_scene_node_for_each_buffer(&c->scene_surface->node, @@ -4376,10 +4521,42 @@ void client_set_opacity(Client *c, double opacity) { scene_buffer_apply_opacity, &opacity); } +void monitor_stop_skip_frame_timer(Monitor *m) { + if (m->skip_frame_timeout) + wl_event_source_timer_update(m->skip_frame_timeout, 0); + m->skiping_frame = false; + m->resizing_count_pending = 0; + m->resizing_count_current = 0; +} + +static int monitor_skip_frame_timeout_callback(void *data) { + Monitor *m = data; + Client *c, *tmp; + + wl_list_for_each_safe(c, tmp, &clients, link) { c->configure_serial = 0; } + + monitor_stop_skip_frame_timer(m); + wlr_output_schedule_frame(m->wlr_output); + + return 1; +} + +void monitor_check_skip_frame_timeout(Monitor *m) { + if (m->skiping_frame && + m->resizing_count_pending == m->resizing_count_current) { + return; + } + + if (m->skip_frame_timeout) { + m->resizing_count_current = m->resizing_count_pending; + m->skiping_frame = true; + wl_event_source_timer_update(m->skip_frame_timeout, 100); // 100ms + } +} + void rendermon(struct wl_listener *listener, void *data) { Monitor *m = wl_container_of(listener, m, frame); Client *c = NULL, *tmp = NULL; - struct wlr_output_state pending = {0}; LayerSurface *l = NULL, *tmpl = NULL; int32_t i; struct wl_list *layer_list; @@ -4415,13 +4592,17 @@ void rendermon(struct wl_listener *listener, void *data) { // 绘制客户端 wl_list_for_each(c, &clients, link) { need_more_frames = client_draw_frame(c) || need_more_frames; - if (!animations && !(allow_tearing && frame_allow_tearing) && - c->configure_serial && !c->isfloating && - client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) { + if (!animations && !grabc && c->configure_serial && + client_is_rendered_on_mon(c, m)) { + monitor_check_skip_frame_timeout(m); goto skip; } } + if (m->skiping_frame) { + monitor_stop_skip_frame_timer(m); + } + // 只有在需要帧时才构建和提交状态 if (allow_tearing && frame_allow_tearing) { apply_tear_state(m); @@ -4432,12 +4613,7 @@ void rendermon(struct wl_listener *listener, void *data) { skip: // 发送帧完成通知 clock_gettime(CLOCK_MONOTONIC, &now); - if (allow_tearing && frame_allow_tearing) { - wlr_scene_output_send_frame_done(m->scene_output, &now); - } else { - wlr_scene_output_send_frame_done(m->scene_output, &now); - wlr_output_state_finish(&pending); - } + wlr_scene_output_send_frame_done(m->scene_output, &now); // 如果需要更多帧,确保安排下一帧 if (need_more_frames && allow_frame_scheduling) { @@ -4619,6 +4795,41 @@ void exchange_two_client(Client *c1, Client *c2) { } } +void set_activation_env() { + if (!getenv("DBUS_SESSION_BUS_ADDRESS")) { + wlr_log(WLR_INFO, "Not updating dbus execution environment: " + "DBUS_SESSION_BUS_ADDRESS not set"); + return; + } + + wlr_log(WLR_INFO, "Updating dbus execution environment"); + + char *env_keys = join_strings(env_vars, " "); + + // first command: dbus-update-activation-environment + const char *arg1 = env_keys; + char *cmd1 = string_printf("dbus-update-activation-environment %s", arg1); + if (!cmd1) { + wlr_log(WLR_ERROR, "Failed to allocate command string"); + goto cleanup; + } + spawn(&(Arg){.v = cmd1}); + free(cmd1); + + // second command: systemctl --user + const char *action = "import-environment"; + char *cmd2 = string_printf("systemctl --user %s %s", action, env_keys); + if (!cmd2) { + wlr_log(WLR_ERROR, "Failed to allocate command string"); + goto cleanup; + } + spawn(&(Arg){.v = cmd2}); + free(cmd2); + +cleanup: + free(env_keys); +} + void // 17 run(char *startup_cmd) { @@ -4676,6 +4887,8 @@ run(char *startup_cmd) { wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr"); handlecursoractivity(); + set_activation_env(); + run_exec(); run_exec_once(); @@ -4790,12 +5003,19 @@ setfloating(Client *c, int32_t floating) { } if (!c->isfloating && old_floating_state) { - set_size_per(c->mon, c); + restore_size_per(c->mon, c); } if (!c->force_maximize) client_set_maximized(c, false); + if (!c->isfloating || c->force_tiled_state) { + client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | + WLR_EDGE_RIGHT); + } else { + client_set_tiled(c, WLR_EDGE_NONE); + } + arrange(c->mon, false, false); setborder_color(c); printstatus(); @@ -4869,7 +5089,7 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) { wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrTop : LyrTile]); if (!c->ismaximizescreen && old_maximizescreen_state) { - set_size_per(c->mon, c); + restore_size_per(c->mon, c); } if (!c->force_maximize && !c->ismaximizescreen) { @@ -4940,7 +5160,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自 } if (!c->isfullscreen && old_fullscreen_state) { - set_size_per(c->mon, c); + restore_size_per(c->mon, c); } arrange(c->mon, false, false); @@ -5070,9 +5290,15 @@ void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) { /* Make sure window actually overlaps with the monitor */ reset_foreign_tolevel(c); resize(c, c->geom, 0); - c->tags = - newtags ? newtags - : m->tagset[m->seltags]; /* assign tags of target monitor */ + if (!newtags && !m->isoverview) { + c->tags = m->tagset[m->seltags]; + } else if (!newtags && m->isoverview) { + c->tags = m->ovbk_current_tagset; + } else if (newtags) { + c->tags = newtags; + } else { + c->tags = m->tagset[m->seltags]; + } setfloating(c, c->isfloating); setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */ } @@ -5159,6 +5385,7 @@ void setup(void) { setenv("XCURSOR_SIZE", "24", 1); setenv("XDG_CURRENT_DESKTOP", "mango", 1); + parse_config(); init_baked_points(); @@ -5531,6 +5758,9 @@ void overview_backup(Client *c) { c->ismaximizescreen = 0; } c->bw = c->isnoborder ? 0 : borderpx; + + client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | + WLR_EDGE_RIGHT); } // overview切回到普通视图还原窗口的状态 @@ -5570,6 +5800,10 @@ void overview_restore(Client *c, const Arg *arg) { !c->isfullscreen) { // 如果是在ov模式中创建的窗口,没有bw记录 c->bw = c->isnoborder ? 0 : borderpx; } + + if (c->isfloating && !c->force_tiled_state) { + client_set_tiled(c, WLR_EDGE_NONE); + } } void handlecursoractivity(void) { @@ -5616,6 +5850,7 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) { focusclient(focustop(selmon), 1); motionnotify(0, NULL, 0, 0, 0, 0); l->being_unmapped = false; + layer_flush_blur_background(l); wlr_scene_node_destroy(&l->shadow->node); l->shadow = NULL; } @@ -5838,6 +6073,10 @@ void updatemons(struct wl_listener *listener, void *data) { c->mon = selmon; reset_foreign_tolevel(c); } + if (c->tags == 0 && !c->is_in_scratchpad) { + c->tags = selmon->tagset[selmon->seltags]; + set_size_per(selmon, c); + } } focusclient(focustop(selmon), 1); if (selmon->lock_surface) { @@ -6055,6 +6294,18 @@ void virtualpointer(struct wl_listener *listener, void *data) { } #ifdef XWAYLAND +void fix_xwayland_unmanaged_coordinate(Client *c) { + if (!selmon) + return; + + // 1. 如果窗口已经在当前活动显示器内,直接返回 + if (c->geom.x >= selmon->m.x && c->geom.x < selmon->m.x + selmon->m.width && + c->geom.y >= selmon->m.y && c->geom.y < selmon->m.y + selmon->m.height) + return; + + c->geom = setclient_coordinate_center(c, selmon, c->geom, 0, 0); +} + int32_t synckeymap(void *data) { reset_keyboard_layout(); // we only need to sync keymap once @@ -6155,17 +6406,34 @@ void createnotifyx11(struct wl_listener *listener, void *data) { LISTEN(&xsurface->events.request_minimize, &c->minimize, minimizenotify); } +void commitx11(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, commmitx11); + struct wlr_surface_state *state = &c->surface.xwayland->surface->current; + + if ((int32_t)c->geom.width - 2 * (int32_t)c->bw == (int32_t)state->width && + (int32_t)c->geom.height - 2 * (int32_t)c->bw == + (int32_t)state->height && + (int32_t)c->surface.xwayland->x == + (int32_t)c->geom.x + (int32_t)c->bw && + (int32_t)c->surface.xwayland->y == + (int32_t)c->geom.y + (int32_t)c->bw) { + c->configure_serial = 0; + } +} + void associatex11(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, associate); LISTEN(&client_surface(c)->events.map, &c->map, mapnotify); LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify); + LISTEN(&client_surface(c)->events.commit, &c->commmitx11, commitx11); } void dissociatex11(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, dissociate); wl_list_remove(&c->map.link); wl_list_remove(&c->unmap.link); + wl_list_remove(&c->commmitx11.link); } void sethints(struct wl_listener *listener, void *data) {