Merge branch 'main' into scenefx-next

This commit is contained in:
DreamMaoMao 2026-03-08 13:03:22 +08:00 committed by GitHub
commit 6e1ceddbd1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1304 additions and 381 deletions

View file

@ -16,7 +16,7 @@ jobs:
days-before-issue-stale: -1
# 手动标记后14 天后关闭
days-before-issue-close: 7
# 使用的标签(必须和你手动添加的标签一致)
# 使用的标签
stale-issue-label: "stale"
# 自动关闭时自动加上的标签
close-issue-label: "automatic-closing"

View file

@ -1,6 +1,6 @@
# Mango Wayland Compositor
<div>
<img src="https://github.com/DreamMaoMao/mangowc/blob/main/assets/mango-transparency-256.png" alt="MangoWC Logo" width="120"/>
<img src="https://github.com/mangowm/mango/blob/main/assets/mango-transparency-256.png" alt="MangoWM Logo" width="120"/>
</div>
This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
@ -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)
[mangowm](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/mangowm.svg)](https://repology.org/project/mangowm/versions)
## Dependencies
- glibc
- wayland
- wayland-protocols
- libinput
- libdrm
- libxkbcommon
- pixman
- git
- meson
- ninja
- libdisplay-info
- libliftoff
- hwdata
@ -64,9 +71,9 @@ https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
- libxcb
## Arch Linux
The package is in the Arch User Repository and is available for manual download [here](https://aur.archlinux.org/packages/mangowc-git) or through a AUR helper like yay:
The package is in the Arch User Repository and is available for manual download [here](https://aur.archlinux.org/packages/mangowm-git) or through a AUR helper like yay:
```bash
yay -S mangowc-git
yay -S mangowm-git
```
@ -80,12 +87,12 @@ eselect repository enable guru
emerge --sync guru
```
Then, add `gui-libs/scenefx` and `gui-wm/mangowc` to the `package.accept_keywords`.
Then, add `gui-libs/scenefx` and `gui-wm/mangowm` to the `package.accept_keywords`.
Finally, install the package:
```bash
emerge --ask --verbose gui-wm/mangowc
emerge --ask --verbose gui-wm/mangowm
```
## Fedora Linux
@ -95,9 +102,38 @@ First, add the [Terra Repository](https://terra.fyralabs.com/).
Then, install the package:
```bash
dnf install mangowc
dnf install mangowm
```
## Guix System
The package definition is described in the source repository.
First, add `mangowm` channel to `channels.scm` file:
```scheme
;; In $HOME/.config/guix/channels.scm
(cons (channel
(name 'mangowm)
(url "https://github.com/mangowm/mango.git")
(branch "main"))
... ;; Your other channels
%default-channels)
```
Then, run `guix pull` and after update you can either run
`guix install mangowm` or add it to your configuration via:
```scheme
(use-modules (mangowm)) ;; Add mangowm module
;; Add mangowm to packages list
(packages (cons*
mangowm-git
... ;; Other packages you specified
%base-packages))
```
And then rebuild your system.
## Other
```bash
@ -111,8 +147,8 @@ cd scenefx
meson build -Dprefix=/usr
sudo ninja -C build install
git clone https://github.com/DreamMaoMao/mangowc.git
cd mangowc
git clone https://github.com/mangowm/mango.git
cd mangowm
meson build -Dprefix=/usr
sudo ninja -C build install
```
@ -170,9 +206,9 @@ git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
## Config Documentation
Refer to the repo wiki [wiki](https://github.com/DreamMaoMao/mango/wiki/)
Refer to the repo wiki [wiki](https://github.com/mangowm/mango/wiki/)
or the website docs [docs](https://mangowc.vercel.app/docs)
or the website docs [docs](https://mangowm.github.io/)
# NixOS + Home-manager
@ -192,7 +228,7 @@ Here's an example of using the modules in a flake:
};
flake-parts.url = "github:hercules-ci/flake-parts";
mango = {
url = "github:DreamMaoMao/mango";
url = "github:mangowm/mango";
inputs.nixpkgs.follows = "nixpkgs";
};
};
@ -254,9 +290,9 @@ Here's an example of using the modules in a flake:
To package mango for other distributions, you can check the reference setup for:
- [nix](https://github.com/DreamMaoMao/mangowc/blob/main/nix/default.nix)
- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mangowc-git).
- [gentoo](https://data.gpo.zugaina.org/guru/gui-wm/mangowc)
- [nix](https://github.com/mangowm/mango/blob/main/nix/default.nix)
- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mangowm-git).
- [gentoo](https://data.gpo.zugaina.org/guru/gui-wm/mangowm)
You might need to package `scenefx` for your distribution, check availability [here](https://github.com/wlrfx/scenefx.git).

View file

@ -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

View file

@ -1,4 +1,4 @@
(define-module (mangowc)
(define-module (mangowm)
#:use-module (guix download)
#:use-module (guix git-download)
#:use-module (guix gexp)
@ -18,11 +18,11 @@
#:use-module (guix licenses))
(define-public mangowc-git
(define-public mangowm-git
(package
(name "mangowc")
(name "mangowm")
(version "git")
(source (local-file "." "mangowc-checkout"
(source (local-file "." "mangowm-checkout"
#:recursive? #t
#:select? (or (git-predicate (current-source-directory))
(const #t))))
@ -55,10 +55,13 @@
wlroots
scenefx))
(native-inputs (list pkg-config wayland-protocols))
(home-page "https://github.com/DreamMaoMao/mangowc")
(home-page "https://github.com/DreamMaoMao/mangowm")
(synopsis "Wayland compositor based on wlroots and scenefx")
(description "A Wayland compositor based on wlroots and scenefx,
inspired by dwl but aiming to be more feature-rich.")
(license gpl3)))
mangowc-git
(define-deprecated-package mangowc
mangowm-git)
mangowm-git

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'],
version : '0.12.1',
version : '0.12.5',
)
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'))

View file

@ -500,7 +500,7 @@ static const struct wl_registry_listener registry_listener = {
static void usage(void) {
fprintf(stderr,
"mmsg - MangoWC IPC\n"
"mmsg - MangoWM IPC\n"
"\n"
"SYNOPSIS:\n"
"\tmmsg [-OTLq]\n"
@ -517,7 +517,7 @@ static void usage(void) {
"\t-O Get all output (monitor) information\n"
"\t-T Get number of tags\n"
"\t-L Get all available layouts\n"
"\t-q Quit MangoWC\n"
"\t-q Quit mango\n"
"\t-o <output> Select output (monitor)\n"
"\n"
"GET OPTIONS (used with -g or -w):\n"

View file

@ -11,7 +11,7 @@
wayland,
wayland-protocols,
wayland-scanner,
xcbutilwm,
libxcb-wm,
xwayland,
meson,
ninja,
@ -57,7 +57,7 @@ stdenv.mkDerivation {
]
++ lib.optionals enableXWayland [
libX11
xcbutilwm
libxcb-wm
xwayland
];

View file

@ -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',

View file

@ -1002,6 +1002,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;

View file

@ -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 &&

View file

@ -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;
}

View file

@ -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);
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, ...);

View file

@ -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;
@ -77,7 +78,8 @@ typedef struct {
int32_t ignore_maximize;
int32_t ignore_minimize;
int32_t isnosizehint;
const char *monitor;
int32_t indleinhibit_when_focus;
char *monitor;
int32_t offsetx;
int32_t offsety;
int32_t width;
@ -110,6 +112,7 @@ typedef struct {
int32_t width, height; // Monitor resolution
float refresh; // Refresh rate
int32_t vrr; // variable refresh rate
int32_t custom; // enable custom mode
} ConfigMonitorRule;
// 修改后的宏定义
@ -210,6 +213,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;
@ -352,6 +356,8 @@ typedef struct {
int32_t single_scratchpad;
int32_t xwayland_persistence;
int32_t syncobj_enable;
float drag_tile_refresh_interval;
float drag_floating_refresh_interval;
int32_t allow_tearing;
int32_t allow_shortcuts_inhibit;
int32_t allow_lock_transparent;
@ -368,6 +374,9 @@ typedef int32_t (*FuncType)(const Arg *);
Config config;
bool parse_config_file(Config *config, const char *file_path, bool must_exist);
bool apply_rule_to_state(Monitor *m, const ConfigMonitorRule *rule,
struct wlr_output_state *state, int vrr, int custom);
bool monitor_matches_rule(Monitor *m, const ConfigMonitorRule *rule);
// Helper function to trim whitespace from start and end of a string
void trim_whitespace(char *str) {
@ -943,6 +952,7 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
(*arg).f = atof(arg_value);
} else if (strcmp(func_name, "switch_proportion_preset") == 0) {
func = switch_proportion_preset;
(*arg).i = parse_circle_direction(arg_value);
} else if (strcmp(func_name, "viewtoleft") == 0) {
func = viewtoleft;
(*arg).i = atoi(arg_value);
@ -1015,6 +1025,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);
@ -1336,6 +1347,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) {
@ -1388,6 +1401,10 @@ bool parse_option(Config *config, char *key, char *value) {
config->xwayland_persistence = atoi(value);
} else if (strcmp(key, "syncobj_enable") == 0) {
config->syncobj_enable = atoi(value);
} else if (strcmp(key, "drag_tile_refresh_interval") == 0) {
config->drag_tile_refresh_interval = atof(value);
} else if (strcmp(key, "drag_floating_refresh_interval") == 0) {
config->drag_floating_refresh_interval = atof(value);
} else if (strcmp(key, "allow_tearing") == 0) {
config->allow_tearing = atoi(value);
} else if (strcmp(key, "allow_shortcuts_inhibit") == 0) {
@ -1790,6 +1807,7 @@ bool parse_option(Config *config, char *key, char *value) {
rule->height = -1;
rule->refresh = 0.0f;
rule->vrr = 0;
rule->custom = 0;
bool parse_error = false;
char *token = strtok(value, ",");
@ -1827,6 +1845,8 @@ bool parse_option(Config *config, char *key, char *value) {
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 if (strcmp(key, "custom") == 0) {
rule->custom = CLAMP_INT(atoi(val), 0, 1);
} else {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Unknown "
@ -1839,6 +1859,13 @@ bool parse_option(Config *config, char *key, char *value) {
token = strtok(NULL, ",");
}
if (!rule->name && !rule->make && !rule->model && !rule->serial) {
fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Monitor rule "
"must have at least one of the following "
"options: name, make, model, serial\n");
return false;
}
config->monitor_rules_count++;
return !parse_error;
} else if (strcmp(key, "tagrule") == 0) {
@ -1995,6 +2022,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;
@ -2009,6 +2037,7 @@ bool parse_option(Config *config, char *key, char *value) {
rule->ignore_maximize = -1;
rule->ignore_minimize = -1;
rule->isnosizehint = -1;
rule->indleinhibit_when_focus = -1;
rule->isterm = -1;
rule->allow_csd = -1;
rule->force_maximize = -1;
@ -2119,6 +2148,8 @@ bool parse_option(Config *config, char *key, char *value) {
rule->ignore_minimize = atoi(val);
} else if (strcmp(key, "isnosizehint") == 0) {
rule->isnosizehint = atoi(val);
} else if (strcmp(key, "indleinhibit_when_focus") == 0) {
rule->indleinhibit_when_focus = atoi(val);
} else if (strcmp(key, "isterm") == 0) {
rule->isterm = atoi(val);
} else if (strcmp(key, "allow_csd") == 0) {
@ -2137,6 +2168,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);
@ -3098,6 +3131,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);
@ -3120,6 +3155,13 @@ void override_config(void) {
// 杂项设置
xwayland_persistence = CLAMP_INT(config.xwayland_persistence, 0, 1);
syncobj_enable = CLAMP_INT(config.syncobj_enable, 0, 1);
drag_tile_refresh_interval =
CLAMP_FLOAT(config.drag_tile_refresh_interval, 1.0f, 16.0f);
drag_floating_refresh_interval =
CLAMP_FLOAT(config.drag_floating_refresh_interval, 1.0f, 16.0f);
drag_tile_to_tile = CLAMP_INT(config.drag_tile_to_tile, 0, 1);
drag_floating_refresh_interval =
CLAMP_FLOAT(config.drag_floating_refresh_interval, 0.0f, 1000.0f);
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);
@ -3297,6 +3339,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;
@ -3307,6 +3350,8 @@ void set_value_default() {
config.single_scratchpad = single_scratchpad;
config.xwayland_persistence = xwayland_persistence;
config.syncobj_enable = syncobj_enable;
config.drag_tile_refresh_interval = drag_tile_refresh_interval;
config.drag_floating_refresh_interval = drag_floating_refresh_interval;
config.allow_tearing = allow_tearing;
config.allow_shortcuts_inhibit = allow_shortcuts_inhibit;
config.allow_lock_transparent = allow_lock_transparent;
@ -3533,17 +3578,15 @@ void reset_blur_params(void) {
void reapply_monitor_rules(void) {
ConfigMonitorRule *mr;
Monitor *m = NULL;
int32_t ji, vrr;
int32_t ji, vrr, custom;
int32_t mx, my;
struct wlr_output_state state;
struct wlr_output_mode *internal_mode = NULL;
wlr_output_state_init(&state);
bool match_rule = false;
wl_list_for_each(m, &mons, link) {
if (!m->wlr_output->enabled) {
if (!m->wlr_output->enabled)
continue;
}
wlr_output_state_init(&state);
for (ji = 0; ji < config.monitor_rules_count; ji++) {
if (config.monitor_rules_count < 1)
@ -3551,78 +3594,29 @@ void reapply_monitor_rules(void) {
mr = &config.monitor_rules[ji];
// 检查是否匹配的变量
match_rule = true;
// 检查四个标识字段的匹配
if (mr->name != NULL) {
if (!regex_match(mr->name, m->wlr_output->name)) {
match_rule = false;
}
}
if (mr->make != NULL) {
if (m->wlr_output->make == NULL ||
strcmp(mr->make, m->wlr_output->make) != 0) {
match_rule = false;
}
}
if (mr->model != NULL) {
if (m->wlr_output->model == NULL ||
strcmp(mr->model, m->wlr_output->model) != 0) {
match_rule = false;
}
}
if (mr->serial != NULL) {
if (m->wlr_output->serial == NULL ||
strcmp(mr->serial, m->wlr_output->serial) != 0) {
match_rule = false;
}
}
// 只有当所有指定的标识都匹配时才应用规则
if (match_rule) {
if (monitor_matches_rule(m, mr)) {
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;
custom = mr->custom >= 0 ? mr->custom : 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);
(void)apply_rule_to_state(m, mr, &state, vrr, custom);
wlr_output_layout_add(output_layout, m->wlr_output, mx, my);
wlr_output_commit_state(m->wlr_output, &state);
break;
}
}
wlr_output_commit_state(m->wlr_output, &state);
wlr_output_state_finish(&state);
updatemons(NULL, NULL);
}
updatemons(NULL, NULL);
}
void reapply_cursor_style(void) {
if (hide_source) {
wl_event_source_timer_update(hide_source, 0);
wl_event_source_remove(hide_source);
hide_source = NULL;
if (hide_cursor_source) {
wl_event_source_timer_update(hide_cursor_source, 0);
wl_event_source_remove(hide_cursor_source);
hide_cursor_source = NULL;
}
wlr_cursor_unset_image(cursor);
@ -3636,6 +3630,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);
@ -3643,12 +3647,13 @@ void reapply_cursor_style(void) {
wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr");
hide_source = wl_event_loop_add_timer(wl_display_get_event_loop(dpy),
hidecursor, cursor);
hide_cursor_source = wl_event_loop_add_timer(wl_display_get_event_loop(dpy),
hidecursor, cursor);
if (cursor_hidden) {
wlr_cursor_unset_image(cursor);
} else {
wl_event_source_timer_update(hide_source, cursor_hide_timeout * 1000);
wl_event_source_timer_update(hide_cursor_source,
cursor_hide_timeout * 1000);
}
}

View file

@ -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;
@ -108,7 +109,7 @@ int32_t drag_warp_cursor = 1;
int32_t xwayland_persistence = 1; /* xwayland persistence */
int32_t syncobj_enable = 0;
int32_t allow_lock_transparent = 0;
double drag_tile_refresh_interval = 16.0;
double drag_tile_refresh_interval = 8.0;
double drag_floating_refresh_interval = 8.0;
int32_t allow_tearing = TEARING_DISABLED;
int32_t allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;

View file

@ -69,4 +69,4 @@ 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);
int32_t scroller_stack(const Arg *arg);

View file

@ -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;
@ -107,11 +110,16 @@ int32_t exchange_client(const Arg *arg) {
if ((c->isfullscreen || c->ismaximizescreen) && !is_scroller_layout(c->mon))
return 0;
exchange_two_client(c, direction_select(arg));
Client *tc = direction_select(arg);
tc = get_focused_stack_client(tc);
exchange_two_client(c, tc);
return 0;
}
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 +212,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 +273,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 +352,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 +423,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 +468,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 +574,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 +601,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 +628,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 +731,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 +801,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)
@ -812,7 +850,7 @@ int32_t spawn_shell(const Arg *arg) {
execlp("bash", "bash", "-c", arg->v, (char *)NULL);
// if execlp fails, we should not reach here
wlr_log(WLR_ERROR,
wlr_log(WLR_DEBUG,
"mango: failed to execute command '%s' with shell: %s\n",
arg->v, strerror(errno));
_exit(EXIT_FAILURE);
@ -853,7 +891,7 @@ int32_t spawn(const Arg *arg) {
execvp(argv[0], argv);
// 4. execvp 失败时:打印错误并直接退出(避免 coredump
wlr_log(WLR_ERROR, "mango: execvp '%s' failed: %s\n", argv[0],
wlr_log(WLR_DEBUG, "mango: execvp '%s' failed: %s\n", argv[0],
strerror(errno));
_exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作
}
@ -900,7 +938,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 +981,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 +1035,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;
@ -1007,13 +1056,28 @@ int32_t switch_proportion_preset(const Arg *arg) {
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;
if (arg->i == NEXT) {
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;
}
} else {
target_proportion =
config.scroller_proportion_preset[i + 1];
break;
if (i == 0) {
target_proportion =
config.scroller_proportion_preset
[config.scroller_proportion_preset_count - 1];
break;
} else {
target_proportion =
config.scroller_proportion_preset[i - 1];
break;
}
}
}
}
@ -1032,6 +1096,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 +1105,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 +1119,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 +1198,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 +1210,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 +1241,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 +1278,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 +1287,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 +1298,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 +1330,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 +1351,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 +1381,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 +1405,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 +1431,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 +1451,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 +1474,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 +1494,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 +1521,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 +1548,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 +1560,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 +1629,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 +1641,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 +1674,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 +1686,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 +1709,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 +1718,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 +1733,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 +1748,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 +1760,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;
@ -1725,4 +1862,4 @@ int32_t scroller_stack(const Arg *arg) {
arrange(selmon, false, false);
return 0;
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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)

View file

@ -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;
}

View file

@ -1,3 +1,82 @@
void save_old_size_per(Monitor *m) {
Client *c = NULL;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
}
}
}
void restore_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
if (!m || !c)
return;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc)) {
fc->old_ismaster = fc->ismaster;
}
}
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
pre_caculate_before_arrange(m, false, false, true);
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) {
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && !c->ismaster) {
set_size_per(m, fc);
}
}
return;
}
// it is possible that the current floating window is moved to another tag,
// but the tag has not executed save_old_size_per
// so it must be judged whether their old size values are initial values
if (!c->ismaster && c->old_stack_inner_per < 1.0 &&
c->old_stack_inner_per > 0.0f && c->stack_inner_per < 1.0 &&
c->stack_inner_per > 0.0f) {
c->stack_inner_per = (1.0 - c->stack_inner_per) *
c->old_stack_inner_per /
(1.0 - c->old_stack_inner_per);
}
if (c->ismaster && c->old_master_inner_per < 1.0 &&
c->old_master_inner_per > 0.0f && c->master_inner_per < 1.0 &&
c->master_inner_per > 0.0f) {
c->master_inner_per = (1.0 - c->master_inner_per) *
c->old_master_inner_per /
(1.0 - c->old_master_inner_per);
}
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c && !fc->ismaster &&
fc->old_ismaster && fc->old_stack_inner_per < 1.0 &&
fc->old_stack_inner_per > 0.0f && fc->stack_inner_per < 1.0 &&
fc->stack_inner_per > 0.0f) {
fc->stack_inner_per = (1.0 - fc->stack_inner_per) *
fc->old_stack_inner_per /
(1.0 - fc->old_stack_inner_per);
fc->old_ismaster = false;
}
}
}
void set_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
bool found = false;
@ -5,8 +84,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;
@ -43,8 +127,7 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
break;
}
if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) &&
ISTILED(tc)) { // 根据你的实际字段名调整
if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
next = tc;
begin_find_nextnext = true;
continue;
@ -60,8 +143,7 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
break;
}
if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) &&
ISTILED(tc)) { // 根据你的实际字段名调整
if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
prev = tc;
begin_find_prevprev = true;
continue;
@ -197,6 +279,23 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
// 应用到所有平铺窗口
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
if (!isdrag && tc != grabc) {
if (!tc->ismaster && new_stack_inner_per != 1.0f &&
grabc->old_stack_inner_per != 1.0f &&
(type != CENTER_TILE ||
!(grabc->isleftstack ^ tc->isleftstack)))
tc->stack_inner_per = (1 - new_stack_inner_per) /
(1 - grabc->old_stack_inner_per) *
tc->stack_inner_per;
if (tc->ismaster && new_master_inner_per != 1.0f &&
grabc->old_master_inner_per != 1.0f)
tc->master_inner_per =
(1.0f - new_master_inner_per) /
(1.0f - grabc->old_master_inner_per) *
tc->master_inner_per;
}
tc->master_mfact_per = new_master_mfact_per;
}
}
@ -229,8 +328,7 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
for (node = grabc->link.next; node != &clients; node = node->next) {
tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) &&
ISTILED(tc)) { // 根据你的实际字段名调整
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
next = tc;
break;
}
@ -240,8 +338,7 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
for (node = grabc->link.prev; node != &clients; node = node->prev) {
tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) &&
ISTILED(tc)) { // 根据你的实际字段名调整
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
prev = tc;
break;
}
@ -354,6 +451,20 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
// 应用到所有平铺窗口
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
if (!isdrag && tc != grabc && type != CENTER_TILE) {
if (!tc->ismaster && new_stack_inner_per != 1.0f &&
grabc->old_stack_inner_per != 1.0f)
tc->stack_inner_per = (1 - new_stack_inner_per) /
(1 - grabc->old_stack_inner_per) *
tc->stack_inner_per;
if (tc->ismaster && new_master_inner_per != 1.0f &&
grabc->old_master_inner_per != 1.0f)
tc->master_inner_per =
(1.0f - new_master_inner_per) /
(1.0f - grabc->old_master_inner_per) *
tc->master_inner_per;
}
tc->master_mfact_per = new_master_mfact_per;
}
}
@ -376,6 +487,7 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, bool isvertical) {
Client *tc = NULL;
float delta_x, delta_y;
float new_scroller_proportion;
float new_stack_proportion;
@ -536,12 +648,22 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
// 应用限制,确保比例在合理范围内
new_scroller_proportion =
fmaxf(0.1f, fminf(1.0f, new_scroller_proportion));
new_stack_proportion = fmaxf(0.1f, fminf(1.0f, new_stack_proportion));
new_stack_proportion = fmaxf(0.1f, fminf(0.9f, new_stack_proportion));
grabc->stack_proportion = new_stack_proportion;
stack_head->scroller_proportion = new_scroller_proportion;
wl_list_for_each(tc, &clients, link) {
if (!isdrag && new_stack_proportion != 1.0f &&
grabc->old_stack_proportion != 1.0f && tc != grabc &&
ISTILED(tc) && get_scroll_stack_head(tc) == stack_head) {
tc->stack_proportion = (1.0f - new_stack_proportion) /
(1.0f - grabc->old_stack_proportion) *
tc->stack_proportion;
}
}
if (!isdrag) {
arrange(grabc->mon, false, false);
return;
@ -584,6 +706,18 @@ void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx,
}
}
/* If there are no calculation omissions,
these two functions will never be triggered.
Just in case to facilitate the final investigation*/
void check_size_per_valid(Client *c) {
if (c->ismaster) {
assert(c->master_inner_per > 0.0f && c->master_inner_per <= 1.0f);
} else {
assert(c->stack_inner_per > 0.0f && c->stack_inner_per <= 1.0f);
}
}
void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
double total_left_stack_hight_percent,
double total_right_stack_hight_percent,
@ -599,6 +733,7 @@ void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
wl_list_for_each(c, &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;
@ -614,17 +749,20 @@ void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
: 1.0f;
}
i++;
check_size_per_valid(c);
}
}
} else {
wl_list_for_each(c, &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;
stack_num > 1 ? 1.0f / ((stack_num - 1) / 2.0f)
: 1.0f;
} else {
c->stack_inner_per =
stack_num > 1 ? 2.0f / stack_num : 1.0f;
@ -653,13 +791,15 @@ void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
}
}
i++;
check_size_per_valid(c);
}
}
}
}
void // 17
arrange(Monitor *m, bool want_animation, bool from_view) {
void pre_caculate_before_arrange(Monitor *m, bool want_animation,
bool from_view, bool only_caculate) {
Client *c = NULL;
double total_stack_inner_percent = 0;
double total_master_inner_percent = 0;
@ -748,14 +888,17 @@ arrange(Monitor *m, bool want_animation, bool from_view) {
i++;
}
set_arrange_visible(m, c, want_animation);
if (!only_caculate)
set_arrange_visible(m, c, want_animation);
} else {
set_arrange_hidden(m, c, want_animation);
if (!only_caculate)
set_arrange_hidden(m, c, want_animation);
}
}
if (c->mon == m && c->ismaximizescreen && !c->animation.tagouted &&
!c->animation.tagouting && VISIBLEON(c, m)) {
if (!only_caculate && c->mon == m && c->ismaximizescreen &&
!c->animation.tagouted && !c->animation.tagouting &&
VISIBLEON(c, m)) {
reset_maximizescreen_size(c);
}
}
@ -764,6 +907,12 @@ arrange(Monitor *m, bool want_animation, bool from_view) {
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);
}
void // 17
arrange(Monitor *m, bool want_animation, bool from_view) {
pre_caculate_before_arrange(m, want_animation, from_view, false);
if (m->isoverview) {
overviewlayout.arrange(m);

View file

@ -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;

View file

@ -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;

File diff suppressed because it is too large Load diff