mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-03-23 05:35:53 -04:00
Merge branch 'mangowm:main' into main
This commit is contained in:
commit
667e69443d
22 changed files with 705 additions and 201 deletions
65
README.md
65
README.md
|
|
@ -1,6 +1,6 @@
|
||||||
# Mango Wayland Compositor
|
# Mango Wayland Compositor
|
||||||
<div>
|
<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>
|
</div>
|
||||||
|
|
||||||
This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
|
This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
|
||||||
|
|
@ -27,11 +27,18 @@ This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
|
||||||
|
|
||||||
https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
|
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
|
# Our discord
|
||||||
[mangowc](https://discord.gg/CPjbDxesh5)
|
[mangowm](https://discord.gg/CPjbDxesh5)
|
||||||
|
|
||||||
# Supported layouts
|
# Supported layouts
|
||||||
|
|
||||||
- tile
|
- tile
|
||||||
- scroller
|
- scroller
|
||||||
- monocle
|
- monocle
|
||||||
|
|
@ -41,21 +48,20 @@ https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
|
||||||
- vertical_tile
|
- vertical_tile
|
||||||
- vertical_grid
|
- vertical_grid
|
||||||
- vertical_scroller
|
- vertical_scroller
|
||||||
|
- tgmix
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
|
[](https://repology.org/project/mangowm/versions)
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- glibc
|
|
||||||
- wayland
|
- wayland
|
||||||
- wayland-protocols
|
- wayland-protocols
|
||||||
- libinput
|
- libinput
|
||||||
- libdrm
|
- libdrm
|
||||||
- libxkbcommon
|
- libxkbcommon
|
||||||
- pixman
|
- pixman
|
||||||
- git
|
|
||||||
- meson
|
|
||||||
- ninja
|
|
||||||
- libdisplay-info
|
- libdisplay-info
|
||||||
- libliftoff
|
- libliftoff
|
||||||
- hwdata
|
- hwdata
|
||||||
|
|
@ -65,9 +71,9 @@ https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
|
||||||
- libxcb
|
- libxcb
|
||||||
|
|
||||||
## Arch Linux
|
## 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
|
```bash
|
||||||
yay -S mangowc-git
|
yay -S mangowm-git
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -81,12 +87,12 @@ eselect repository enable guru
|
||||||
emerge --sync 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:
|
Finally, install the package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
emerge --ask --verbose gui-wm/mangowc
|
emerge --ask --verbose gui-wm/mangowm
|
||||||
```
|
```
|
||||||
|
|
||||||
## Fedora Linux
|
## Fedora Linux
|
||||||
|
|
@ -96,31 +102,32 @@ First, add the [Terra Repository](https://terra.fyralabs.com/).
|
||||||
Then, install the package:
|
Then, install the package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dnf install mangowc
|
dnf install mangowm
|
||||||
```
|
```
|
||||||
|
|
||||||
## GuixSD
|
## Guix System
|
||||||
The package definition is described in the source repository.
|
The package definition is described in the source repository.
|
||||||
First, add `mangowc` channel to `channels.scm` file:
|
First, add `mangowm` channel to `channels.scm` file:
|
||||||
|
|
||||||
```scheme
|
```scheme
|
||||||
;; In $HOME/.config/guix/channels.scm
|
;; In $HOME/.config/guix/channels.scm
|
||||||
(cons (channel
|
(cons (channel
|
||||||
(name 'mangowc)
|
(name 'mangowm)
|
||||||
(url "https://github.com/DreamMaoMao/mangowc.git"))
|
(url "https://github.com/mangowm/mango.git")
|
||||||
|
(branch "main"))
|
||||||
... ;; Your other channels
|
... ;; Your other channels
|
||||||
%default-channels)
|
%default-channels)
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, run `guix pull` and after update you can either run
|
Then, run `guix pull` and after update you can either run
|
||||||
`guix install mangowc` or add it to your configuration via:
|
`guix install mangowm` or add it to your configuration via:
|
||||||
|
|
||||||
```scheme
|
```scheme
|
||||||
(use-modules (mangowc)) ;; Add mangowc module
|
(use-modules (mangowm)) ;; Add mangowm module
|
||||||
|
|
||||||
;; Add mangowc to packages list
|
;; Add mangowm to packages list
|
||||||
(packages (cons
|
(packages (cons*
|
||||||
mangowc
|
mangowm-git
|
||||||
... ;; Other packages you specified
|
... ;; Other packages you specified
|
||||||
%base-packages))
|
%base-packages))
|
||||||
```
|
```
|
||||||
|
|
@ -140,8 +147,8 @@ cd scenefx
|
||||||
meson build -Dprefix=/usr
|
meson build -Dprefix=/usr
|
||||||
sudo ninja -C build install
|
sudo ninja -C build install
|
||||||
|
|
||||||
git clone https://github.com/DreamMaoMao/mangowc.git
|
git clone https://github.com/mangowm/mango.git
|
||||||
cd mangowc
|
cd mangowm
|
||||||
meson build -Dprefix=/usr
|
meson build -Dprefix=/usr
|
||||||
sudo ninja -C build install
|
sudo ninja -C build install
|
||||||
```
|
```
|
||||||
|
|
@ -199,9 +206,9 @@ git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
|
||||||
|
|
||||||
## Config Documentation
|
## 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
|
# NixOS + Home-manager
|
||||||
|
|
||||||
|
|
@ -221,7 +228,7 @@ Here's an example of using the modules in a flake:
|
||||||
};
|
};
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
mango = {
|
mango = {
|
||||||
url = "github:DreamMaoMao/mango";
|
url = "github:mangowm/mango";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -283,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:
|
To package mango for other distributions, you can check the reference setup for:
|
||||||
|
|
||||||
- [nix](https://github.com/DreamMaoMao/mangowc/blob/main/nix/default.nix)
|
- [nix](https://github.com/mangowm/mango/blob/main/nix/default.nix)
|
||||||
- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mangowc-git).
|
- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mangowm-git).
|
||||||
- [gentoo](https://data.gpo.zugaina.org/guru/gui-wm/mangowc)
|
- [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).
|
You might need to package `scenefx` for your distribution, check availability [here](https://github.com/wlrfx/scenefx.git).
|
||||||
|
|
||||||
|
|
|
||||||
5
assets/mango-portals.conf
Normal file
5
assets/mango-portals.conf
Normal 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
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
(define-module (mangowc)
|
(define-module (mangowm)
|
||||||
#:use-module (guix download)
|
#:use-module (guix download)
|
||||||
#:use-module (guix git-download)
|
#:use-module (guix git-download)
|
||||||
#:use-module (guix gexp)
|
#:use-module (guix gexp)
|
||||||
|
|
@ -18,11 +18,11 @@
|
||||||
#:use-module (guix licenses))
|
#:use-module (guix licenses))
|
||||||
|
|
||||||
|
|
||||||
(define-public mangowc-git
|
(define-public mangowm-git
|
||||||
(package
|
(package
|
||||||
(name "mangowc")
|
(name "mangowm")
|
||||||
(version "git")
|
(version "git")
|
||||||
(source (local-file "." "mangowc-checkout"
|
(source (local-file "." "mangowm-checkout"
|
||||||
#:recursive? #t
|
#:recursive? #t
|
||||||
#:select? (or (git-predicate (current-source-directory))
|
#:select? (or (git-predicate (current-source-directory))
|
||||||
(const #t))))
|
(const #t))))
|
||||||
|
|
@ -55,10 +55,10 @@
|
||||||
wlroots
|
wlroots
|
||||||
scenefx))
|
scenefx))
|
||||||
(native-inputs (list pkg-config wayland-protocols))
|
(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")
|
(synopsis "Wayland compositor based on wlroots and scenefx")
|
||||||
(description "A 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.")
|
inspired by dwl but aiming to be more feature-rich.")
|
||||||
(license gpl3)))
|
(license gpl3)))
|
||||||
|
|
||||||
mangowc-git
|
mangowm-git
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
project('mango', ['c', 'cpp'],
|
project('mango', ['c', 'cpp'],
|
||||||
version : '0.12.2',
|
version : '0.12.5',
|
||||||
)
|
)
|
||||||
|
|
||||||
subdir('protocols')
|
subdir('protocols')
|
||||||
|
|
@ -147,5 +147,7 @@ executable('mmsg',
|
||||||
)
|
)
|
||||||
|
|
||||||
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
|
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
|
||||||
install_data('mango.desktop', install_dir : desktop_install_dir)
|
portal_install_dir = join_paths(prefix, 'share/xdg-desktop-portal')
|
||||||
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
|
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'))
|
||||||
|
|
|
||||||
|
|
@ -500,7 +500,7 @@ static const struct wl_registry_listener registry_listener = {
|
||||||
|
|
||||||
static void usage(void) {
|
static void usage(void) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"mmsg - MangoWC IPC\n"
|
"mmsg - MangoWM IPC\n"
|
||||||
"\n"
|
"\n"
|
||||||
"SYNOPSIS:\n"
|
"SYNOPSIS:\n"
|
||||||
"\tmmsg [-OTLq]\n"
|
"\tmmsg [-OTLq]\n"
|
||||||
|
|
@ -517,7 +517,7 @@ static void usage(void) {
|
||||||
"\t-O Get all output (monitor) information\n"
|
"\t-O Get all output (monitor) information\n"
|
||||||
"\t-T Get number of tags\n"
|
"\t-T Get number of tags\n"
|
||||||
"\t-L Get all available layouts\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"
|
"\t-o <output> Select output (monitor)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"GET OPTIONS (used with -g or -w):\n"
|
"GET OPTIONS (used with -g or -w):\n"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
wayland,
|
wayland,
|
||||||
wayland-protocols,
|
wayland-protocols,
|
||||||
wayland-scanner,
|
wayland-scanner,
|
||||||
xcbutilwm,
|
libxcb-wm,
|
||||||
xwayland,
|
xwayland,
|
||||||
meson,
|
meson,
|
||||||
ninja,
|
ninja,
|
||||||
|
|
@ -57,7 +57,7 @@ stdenv.mkDerivation {
|
||||||
]
|
]
|
||||||
++ lib.optionals enableXWayland [
|
++ lib.optionals enableXWayland [
|
||||||
libX11
|
libX11
|
||||||
xcbutilwm
|
libxcb-wm
|
||||||
xwayland
|
xwayland
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
|
||||||
c->geom.height - 2 * c->bw);
|
c->geom.height - 2 * c->bw);
|
||||||
|
|
||||||
|
if (c->configure_serial != 0) {
|
||||||
|
c->mon->resizing_count_pending++;
|
||||||
|
}
|
||||||
|
|
||||||
if (c == grabc) {
|
if (c == grabc) {
|
||||||
c->animation.running = false;
|
c->animation.running = false;
|
||||||
c->need_output_flush = false;
|
c->need_output_flush = false;
|
||||||
|
|
|
||||||
|
|
@ -243,34 +243,6 @@ static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) {
|
||||||
return 0;
|
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. We cannot determine its
|
|
||||||
* stopped state; assume it is not stopped to avoid blocking frame skip.
|
|
||||||
*/
|
|
||||||
if (errno == ECHILD)
|
|
||||||
return 0; // if not our child, assume not stopped
|
|
||||||
/* Other errors, also assume not stopped. */
|
|
||||||
return 0;
|
|
||||||
} 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) {
|
static inline int32_t client_is_unmanaged(Client *c) {
|
||||||
#ifdef XWAYLAND
|
#ifdef XWAYLAND
|
||||||
if (client_is_x11(c))
|
if (client_is_x11(c))
|
||||||
|
|
@ -334,7 +306,6 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
|
||||||
(int32_t)c->geom.x + (int32_t)c->bw &&
|
(int32_t)c->geom.x + (int32_t)c->bw &&
|
||||||
(int32_t)c->surface.xwayland->y ==
|
(int32_t)c->surface.xwayland->y ==
|
||||||
(int32_t)c->geom.y + (int32_t)c->bw) {
|
(int32_t)c->geom.y + (int32_t)c->bw) {
|
||||||
c->configure_serial = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,3 +92,85 @@ uint32_t get_now_in_ms(void) {
|
||||||
uint32_t timespec_to_ms(struct timespec *ts) {
|
uint32_t timespec_to_ms(struct timespec *ts) {
|
||||||
return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,8 @@ int32_t fd_set_nonblock(int32_t fd);
|
||||||
int32_t regex_match(const char *pattern_mb, const char *str_mb);
|
int32_t regex_match(const char *pattern_mb, const char *str_mb);
|
||||||
void wl_list_append(struct wl_list *list, struct wl_list *object);
|
void wl_list_append(struct wl_list *list, struct wl_list *object);
|
||||||
uint32_t get_now_in_ms(void);
|
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, ...);
|
||||||
|
|
@ -211,6 +211,7 @@ typedef struct {
|
||||||
int32_t scroller_ignore_proportion_single;
|
int32_t scroller_ignore_proportion_single;
|
||||||
int32_t scroller_focus_center;
|
int32_t scroller_focus_center;
|
||||||
int32_t scroller_prefer_center;
|
int32_t scroller_prefer_center;
|
||||||
|
int32_t scroller_prefer_overspread;
|
||||||
int32_t edge_scroller_pointer_focus;
|
int32_t edge_scroller_pointer_focus;
|
||||||
int32_t focus_cross_monitor;
|
int32_t focus_cross_monitor;
|
||||||
int32_t exchange_cross_monitor;
|
int32_t exchange_cross_monitor;
|
||||||
|
|
@ -1016,6 +1017,7 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
|
||||||
(*arg).v = strdup(arg_value);
|
(*arg).v = strdup(arg_value);
|
||||||
} else if (strcmp(func_name, "switch_keyboard_layout") == 0) {
|
} else if (strcmp(func_name, "switch_keyboard_layout") == 0) {
|
||||||
func = switch_keyboard_layout;
|
func = switch_keyboard_layout;
|
||||||
|
(*arg).i = CLAMP_INT(atoi(arg_value), 0, 100);
|
||||||
} else if (strcmp(func_name, "setlayout") == 0) {
|
} else if (strcmp(func_name, "setlayout") == 0) {
|
||||||
func = setlayout;
|
func = setlayout;
|
||||||
(*arg).v = strdup(arg_value);
|
(*arg).v = strdup(arg_value);
|
||||||
|
|
@ -1337,6 +1339,8 @@ bool parse_option(Config *config, char *key, char *value) {
|
||||||
config->scroller_focus_center = atoi(value);
|
config->scroller_focus_center = atoi(value);
|
||||||
} else if (strcmp(key, "scroller_prefer_center") == 0) {
|
} else if (strcmp(key, "scroller_prefer_center") == 0) {
|
||||||
config->scroller_prefer_center = atoi(value);
|
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) {
|
} else if (strcmp(key, "edge_scroller_pointer_focus") == 0) {
|
||||||
config->edge_scroller_pointer_focus = atoi(value);
|
config->edge_scroller_pointer_focus = atoi(value);
|
||||||
} else if (strcmp(key, "focus_cross_monitor") == 0) {
|
} else if (strcmp(key, "focus_cross_monitor") == 0) {
|
||||||
|
|
@ -1840,6 +1844,13 @@ bool parse_option(Config *config, char *key, char *value) {
|
||||||
token = strtok(NULL, ",");
|
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++;
|
config->monitor_rules_count++;
|
||||||
return !parse_error;
|
return !parse_error;
|
||||||
} else if (strcmp(key, "tagrule") == 0) {
|
} else if (strcmp(key, "tagrule") == 0) {
|
||||||
|
|
@ -3102,6 +3113,8 @@ void override_config(void) {
|
||||||
CLAMP_INT(config.scroller_ignore_proportion_single, 0, 1);
|
CLAMP_INT(config.scroller_ignore_proportion_single, 0, 1);
|
||||||
scroller_focus_center = CLAMP_INT(config.scroller_focus_center, 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_center = CLAMP_INT(config.scroller_prefer_center, 0, 1);
|
||||||
|
scroller_prefer_overspread =
|
||||||
|
CLAMP_INT(config.scroller_prefer_overspread, 0, 1);
|
||||||
edge_scroller_pointer_focus =
|
edge_scroller_pointer_focus =
|
||||||
CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1);
|
CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1);
|
||||||
scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000);
|
scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000);
|
||||||
|
|
@ -3301,6 +3314,7 @@ void set_value_default() {
|
||||||
scroller_ignore_proportion_single;
|
scroller_ignore_proportion_single;
|
||||||
config.scroller_focus_center = scroller_focus_center;
|
config.scroller_focus_center = scroller_focus_center;
|
||||||
config.scroller_prefer_center = scroller_prefer_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.edge_scroller_pointer_focus = edge_scroller_pointer_focus;
|
||||||
config.focus_cross_monitor = focus_cross_monitor;
|
config.focus_cross_monitor = focus_cross_monitor;
|
||||||
config.exchange_cross_monitor = exchange_cross_monitor;
|
config.exchange_cross_monitor = exchange_cross_monitor;
|
||||||
|
|
@ -3640,6 +3654,16 @@ void reapply_cursor_style(void) {
|
||||||
|
|
||||||
cursor_mgr = wlr_xcursor_manager_create(config.cursor_theme, cursor_size);
|
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;
|
Monitor *m = NULL;
|
||||||
wl_list_for_each(m, &mons, link) {
|
wl_list_for_each(m, &mons, link) {
|
||||||
wlr_xcursor_manager_load(cursor_mgr, m->wlr_output->scale);
|
wlr_xcursor_manager_load(cursor_mgr, m->wlr_output->scale);
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ float scroller_default_proportion_single = 1.0;
|
||||||
int32_t scroller_ignore_proportion_single = 1;
|
int32_t scroller_ignore_proportion_single = 1;
|
||||||
int32_t scroller_focus_center = 0;
|
int32_t scroller_focus_center = 0;
|
||||||
int32_t scroller_prefer_center = 0;
|
int32_t scroller_prefer_center = 0;
|
||||||
|
int32_t scroller_prefer_overspread = 1;
|
||||||
int32_t focus_cross_monitor = 0;
|
int32_t focus_cross_monitor = 0;
|
||||||
int32_t focus_cross_tag = 0;
|
int32_t focus_cross_tag = 0;
|
||||||
int32_t exchange_cross_monitor = 0;
|
int32_t exchange_cross_monitor = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
int32_t bind_to_view(const Arg *arg) {
|
int32_t bind_to_view(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
uint32_t target = arg->ui;
|
uint32_t target = arg->ui;
|
||||||
|
|
||||||
if (view_current_to_back && selmon->pertag->curtag &&
|
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) {
|
int32_t exchange_client(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
Client *c = selmon->sel;
|
Client *c = selmon->sel;
|
||||||
if (!c || c->isfloating)
|
if (!c || c->isfloating)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -112,6 +115,9 @@ int32_t exchange_client(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t exchange_stack_client(const Arg *arg) {
|
int32_t exchange_stack_client(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
Client *c = selmon->sel;
|
Client *c = selmon->sel;
|
||||||
Client *tc = NULL;
|
Client *tc = NULL;
|
||||||
if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen)
|
if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen)
|
||||||
|
|
@ -204,7 +210,7 @@ int32_t focusmon(const Arg *arg) {
|
||||||
if (!m->wlr_output->enabled) {
|
if (!m->wlr_output->enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (regex_match(arg->v, m->wlr_output->name)) {
|
if (match_monitor_spec(arg->v, m)) {
|
||||||
tm = m;
|
tm = m;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -265,42 +271,56 @@ int32_t incnmaster(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t incgaps(const Arg *arg) {
|
int32_t incgaps(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i,
|
setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i,
|
||||||
selmon->gappih + arg->i, selmon->gappiv + arg->i);
|
selmon->gappih + arg->i, selmon->gappiv + arg->i);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t incigaps(const Arg *arg) {
|
int32_t incigaps(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i,
|
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i,
|
||||||
selmon->gappiv + arg->i);
|
selmon->gappiv + arg->i);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t incogaps(const Arg *arg) {
|
int32_t incogaps(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, selmon->gappih,
|
setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, selmon->gappih,
|
||||||
selmon->gappiv);
|
selmon->gappiv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t incihgaps(const Arg *arg) {
|
int32_t incihgaps(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i,
|
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i,
|
||||||
selmon->gappiv);
|
selmon->gappiv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t incivgaps(const Arg *arg) {
|
int32_t incivgaps(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih,
|
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih,
|
||||||
selmon->gappiv + arg->i);
|
selmon->gappiv + arg->i);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t incohgaps(const Arg *arg) {
|
int32_t incohgaps(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
setgaps(selmon->gappoh + arg->i, selmon->gappov, selmon->gappih,
|
setgaps(selmon->gappoh + arg->i, selmon->gappov, selmon->gappih,
|
||||||
selmon->gappiv);
|
selmon->gappiv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t incovgaps(const Arg *arg) {
|
int32_t incovgaps(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
setgaps(selmon->gappoh, selmon->gappov + arg->i, selmon->gappih,
|
setgaps(selmon->gappoh, selmon->gappov + arg->i, selmon->gappih,
|
||||||
selmon->gappiv);
|
selmon->gappiv);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -330,6 +350,8 @@ int32_t setmfact(const Arg *arg) {
|
||||||
|
|
||||||
int32_t killclient(const Arg *arg) {
|
int32_t killclient(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
c = selmon->sel;
|
c = selmon->sel;
|
||||||
if (c) {
|
if (c) {
|
||||||
pending_kill_client(c);
|
pending_kill_client(c);
|
||||||
|
|
@ -399,6 +421,8 @@ int32_t moveresize(const Arg *arg) {
|
||||||
|
|
||||||
int32_t movewin(const Arg *arg) {
|
int32_t movewin(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
c = selmon->sel;
|
c = selmon->sel;
|
||||||
if (!c || c->isfullscreen)
|
if (!c || c->isfullscreen)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -442,6 +466,8 @@ int32_t quit(const Arg *arg) {
|
||||||
|
|
||||||
int32_t resizewin(const Arg *arg) {
|
int32_t resizewin(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
c = selmon->sel;
|
c = selmon->sel;
|
||||||
int32_t offsetx = 0, offsety = 0;
|
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 setlayout(const Arg *arg) {
|
||||||
int32_t jk;
|
int32_t jk;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for (jk = 0; jk < LENGTH(layouts); jk++) {
|
for (jk = 0; jk < LENGTH(layouts); jk++) {
|
||||||
if (strcmp(layouts[jk].name, arg->v) == 0) {
|
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) {
|
int32_t set_proportion(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (selmon->isoverview || !is_scroller_layout(selmon))
|
if (selmon->isoverview || !is_scroller_layout(selmon))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -596,6 +626,8 @@ int32_t smartmovewin(const Arg *arg) {
|
||||||
Client *c = NULL, *tc = NULL;
|
Client *c = NULL, *tc = NULL;
|
||||||
int32_t nx, ny;
|
int32_t nx, ny;
|
||||||
int32_t buttom, top, left, right, tar;
|
int32_t buttom, top, left, right, tar;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
c = selmon->sel;
|
c = selmon->sel;
|
||||||
if (!c || c->isfullscreen)
|
if (!c || c->isfullscreen)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -697,6 +729,8 @@ int32_t smartresizewin(const Arg *arg) {
|
||||||
Client *c = NULL, *tc = NULL;
|
Client *c = NULL, *tc = NULL;
|
||||||
int32_t nw, nh;
|
int32_t nw, nh;
|
||||||
int32_t buttom, top, left, right, tar;
|
int32_t buttom, top, left, right, tar;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
c = selmon->sel;
|
c = selmon->sel;
|
||||||
if (!c || c->isfullscreen)
|
if (!c || c->isfullscreen)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -765,6 +799,8 @@ int32_t smartresizewin(const Arg *arg) {
|
||||||
|
|
||||||
int32_t centerwin(const Arg *arg) {
|
int32_t centerwin(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
c = selmon->sel;
|
c = selmon->sel;
|
||||||
|
|
||||||
if (!c || c->isfullscreen || c->ismaximizescreen)
|
if (!c || c->isfullscreen || c->ismaximizescreen)
|
||||||
|
|
@ -812,7 +848,7 @@ int32_t spawn_shell(const Arg *arg) {
|
||||||
execlp("bash", "bash", "-c", arg->v, (char *)NULL);
|
execlp("bash", "bash", "-c", arg->v, (char *)NULL);
|
||||||
|
|
||||||
// if execlp fails, we should not reach here
|
// 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",
|
"mango: failed to execute command '%s' with shell: %s\n",
|
||||||
arg->v, strerror(errno));
|
arg->v, strerror(errno));
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
|
|
@ -853,7 +889,7 @@ int32_t spawn(const Arg *arg) {
|
||||||
execvp(argv[0], argv);
|
execvp(argv[0], argv);
|
||||||
|
|
||||||
// 4. execvp 失败时:打印错误并直接退出(避免 coredump)
|
// 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));
|
strerror(errno));
|
||||||
_exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作
|
_exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作
|
||||||
}
|
}
|
||||||
|
|
@ -900,7 +936,13 @@ int32_t switch_keyboard_layout(const Arg *arg) {
|
||||||
wlr_log(WLR_INFO, "Only one layout available");
|
wlr_log(WLR_INFO, "Only one layout available");
|
||||||
return 0;
|
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
|
// 6. 应用新 keymap
|
||||||
uint32_t depressed = keyboard->modifiers.depressed;
|
uint32_t depressed = keyboard->modifiers.depressed;
|
||||||
|
|
@ -937,6 +979,9 @@ int32_t switch_layout(const Arg *arg) {
|
||||||
char *target_layout_name = NULL;
|
char *target_layout_name = NULL;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
|
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (config.circle_layout_count != 0) {
|
if (config.circle_layout_count != 0) {
|
||||||
for (jk = 0; jk < config.circle_layout_count; jk++) {
|
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) {
|
int32_t switch_proportion_preset(const Arg *arg) {
|
||||||
float target_proportion = 0;
|
float target_proportion = 0;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (config.scroller_proportion_preset_count == 0) {
|
if (config.scroller_proportion_preset_count == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1032,6 +1079,8 @@ int32_t switch_proportion_preset(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t tag(const Arg *arg) {
|
int32_t tag(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
Client *target_client = selmon->sel;
|
Client *target_client = selmon->sel;
|
||||||
tag_client(arg, target_client);
|
tag_client(arg, target_client);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1039,6 +1088,8 @@ int32_t tag(const Arg *arg) {
|
||||||
|
|
||||||
int32_t tagmon(const Arg *arg) {
|
int32_t tagmon(const Arg *arg) {
|
||||||
Monitor *m = NULL, *cm = NULL;
|
Monitor *m = NULL, *cm = NULL;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
Client *c = focustop(selmon);
|
Client *c = focustop(selmon);
|
||||||
|
|
||||||
if (!c)
|
if (!c)
|
||||||
|
|
@ -1051,7 +1102,7 @@ int32_t tagmon(const Arg *arg) {
|
||||||
if (!cm->wlr_output->enabled) {
|
if (!cm->wlr_output->enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (regex_match(arg->v, cm->wlr_output->name)) {
|
if (match_monitor_spec(arg->v, cm)) {
|
||||||
m = cm;
|
m = cm;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1130,6 +1181,9 @@ int32_t tagsilent(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t tagtoleft(const Arg *arg) {
|
int32_t tagtoleft(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (selmon->sel != NULL &&
|
if (selmon->sel != NULL &&
|
||||||
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 &&
|
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 &&
|
||||||
selmon->tagset[selmon->seltags] > 1) {
|
selmon->tagset[selmon->seltags] > 1) {
|
||||||
|
|
@ -1139,6 +1193,9 @@ int32_t tagtoleft(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t tagtoright(const Arg *arg) {
|
int32_t tagtoright(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (selmon->sel != NULL &&
|
if (selmon->sel != NULL &&
|
||||||
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 &&
|
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 &&
|
||||||
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) {
|
int32_t toggle_render_border(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
render_border = !render_border;
|
render_border = !render_border;
|
||||||
arrange(selmon, false, false);
|
arrange(selmon, false, false);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1202,6 +1261,8 @@ int32_t toggle_scratchpad(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t togglefakefullscreen(const Arg *arg) {
|
int32_t togglefakefullscreen(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = focustop(selmon);
|
||||||
if (sel)
|
if (sel)
|
||||||
setfakefullscreen(sel, !sel->isfakefullscreen);
|
setfakefullscreen(sel, !sel->isfakefullscreen);
|
||||||
|
|
@ -1209,6 +1270,9 @@ int32_t togglefakefullscreen(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t togglefloating(const Arg *arg) {
|
int32_t togglefloating(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = focustop(selmon);
|
||||||
|
|
||||||
if (selmon && selmon->isoverview)
|
if (selmon && selmon->isoverview)
|
||||||
|
|
@ -1230,6 +1294,9 @@ int32_t togglefloating(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t togglefullscreen(const Arg *arg) {
|
int32_t togglefullscreen(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = focustop(selmon);
|
||||||
if (!sel)
|
if (!sel)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1246,6 +1313,9 @@ int32_t togglefullscreen(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t toggleglobal(const Arg *arg) {
|
int32_t toggleglobal(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!selmon->sel)
|
if (!selmon->sel)
|
||||||
return 0;
|
return 0;
|
||||||
if (selmon->sel->is_in_scratchpad) {
|
if (selmon->sel->is_in_scratchpad) {
|
||||||
|
|
@ -1264,12 +1334,18 @@ int32_t toggleglobal(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t togglegaps(const Arg *arg) {
|
int32_t togglegaps(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
enablegaps ^= 1;
|
enablegaps ^= 1;
|
||||||
arrange(selmon, false, false);
|
arrange(selmon, false, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t togglemaximizescreen(const Arg *arg) {
|
int32_t togglemaximizescreen(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = focustop(selmon);
|
||||||
if (!sel)
|
if (!sel)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1288,6 +1364,9 @@ int32_t togglemaximizescreen(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t toggleoverlay(const Arg *arg) {
|
int32_t toggleoverlay(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!selmon->sel || !selmon->sel->mon || selmon->sel->isfullscreen) {
|
if (!selmon->sel || !selmon->sel->mon || selmon->sel->isfullscreen) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1309,6 +1388,9 @@ int32_t toggleoverlay(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t toggletag(const Arg *arg) {
|
int32_t toggletag(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
uint32_t newtags;
|
uint32_t newtags;
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = focustop(selmon);
|
||||||
if (!sel)
|
if (!sel)
|
||||||
|
|
@ -1332,13 +1414,15 @@ int32_t toggletag(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t toggleview(const Arg *arg) {
|
int32_t toggleview(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
uint32_t newtagset;
|
uint32_t newtagset;
|
||||||
uint32_t target;
|
uint32_t target;
|
||||||
|
|
||||||
target = arg->ui == 0 ? ~0 & TAGMASK : arg->ui;
|
target = arg->ui == 0 ? ~0 & TAGMASK : arg->ui;
|
||||||
|
|
||||||
newtagset =
|
newtagset = selmon->tagset[selmon->seltags] ^ (target & TAGMASK);
|
||||||
selmon ? selmon->tagset[selmon->seltags] ^ (target & TAGMASK) : 0;
|
|
||||||
|
|
||||||
if (newtagset) {
|
if (newtagset) {
|
||||||
selmon->tagset[selmon->seltags] = newtagset;
|
selmon->tagset[selmon->seltags] = newtagset;
|
||||||
|
|
@ -1350,6 +1434,9 @@ int32_t toggleview(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t viewtoleft(const Arg *arg) {
|
int32_t viewtoleft(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
uint32_t target = selmon->tagset[selmon->seltags];
|
uint32_t target = selmon->tagset[selmon->seltags];
|
||||||
|
|
||||||
if (selmon->isoverview || selmon->pertag->curtag == 0) {
|
if (selmon->isoverview || selmon->pertag->curtag == 0) {
|
||||||
|
|
@ -1370,6 +1457,9 @@ int32_t viewtoleft(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t viewtoright(const Arg *arg) {
|
int32_t viewtoright(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (selmon->isoverview || selmon->pertag->curtag == 0) {
|
if (selmon->isoverview || selmon->pertag->curtag == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1387,6 +1477,9 @@ int32_t viewtoright(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t viewtoleft_have_client(const Arg *arg) {
|
int32_t viewtoleft_have_client(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
uint32_t n;
|
uint32_t n;
|
||||||
uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]);
|
uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]);
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
@ -1411,6 +1504,9 @@ int32_t viewtoleft_have_client(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t viewtoright_have_client(const Arg *arg) {
|
int32_t viewtoright_have_client(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
uint32_t n;
|
uint32_t n;
|
||||||
uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]);
|
uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]);
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
@ -1435,6 +1531,9 @@ int32_t viewtoright_have_client(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t viewcrossmon(const Arg *arg) {
|
int32_t viewcrossmon(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
focusmon(&(Arg){.v = arg->v, .i = UNDIR});
|
focusmon(&(Arg){.v = arg->v, .i = UNDIR});
|
||||||
view_in_mon(arg, true, selmon, true);
|
view_in_mon(arg, true, selmon, true);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1444,7 +1543,7 @@ int32_t tagcrossmon(const Arg *arg) {
|
||||||
if (!selmon || !selmon->sel)
|
if (!selmon || !selmon->sel)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (regex_match(selmon->wlr_output->name, arg->v)) {
|
if (match_monitor_spec(arg->v, selmon)) {
|
||||||
tag_client(arg, selmon->sel);
|
tag_client(arg, selmon->sel);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1513,6 +1612,8 @@ int32_t setoption(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t minimized(const Arg *arg) {
|
int32_t minimized(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (selmon && selmon->isoverview)
|
if (selmon && selmon->isoverview)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1535,6 +1636,8 @@ void fix_mon_tagset_from_overview(Monitor *m) {
|
||||||
|
|
||||||
int32_t toggleoverview(const Arg *arg) {
|
int32_t toggleoverview(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (selmon->isoverview && ov_tab_mode && arg->i != 1 && selmon->sel) {
|
if (selmon->isoverview && ov_tab_mode && arg->i != 1 && selmon->sel) {
|
||||||
focusstack(&(Arg){.i = 1});
|
focusstack(&(Arg){.i = 1});
|
||||||
|
|
@ -1598,7 +1701,7 @@ int32_t disable_monitor(const Arg *arg) {
|
||||||
Monitor *m = NULL;
|
Monitor *m = NULL;
|
||||||
struct wlr_output_state state = {0};
|
struct wlr_output_state state = {0};
|
||||||
wl_list_for_each(m, &mons, link) {
|
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_state_set_enabled(&state, false);
|
||||||
wlr_output_commit_state(m->wlr_output, &state);
|
wlr_output_commit_state(m->wlr_output, &state);
|
||||||
m->asleep = 1;
|
m->asleep = 1;
|
||||||
|
|
@ -1613,7 +1716,7 @@ int32_t enable_monitor(const Arg *arg) {
|
||||||
Monitor *m = NULL;
|
Monitor *m = NULL;
|
||||||
struct wlr_output_state state = {0};
|
struct wlr_output_state state = {0};
|
||||||
wl_list_for_each(m, &mons, link) {
|
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_state_set_enabled(&state, true);
|
||||||
wlr_output_commit_state(m->wlr_output, &state);
|
wlr_output_commit_state(m->wlr_output, &state);
|
||||||
m->asleep = 0;
|
m->asleep = 0;
|
||||||
|
|
@ -1628,7 +1731,7 @@ int32_t toggle_monitor(const Arg *arg) {
|
||||||
Monitor *m = NULL;
|
Monitor *m = NULL;
|
||||||
struct wlr_output_state state = {0};
|
struct wlr_output_state state = {0};
|
||||||
wl_list_for_each(m, &mons, link) {
|
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_state_set_enabled(&state, !m->wlr_output->enabled);
|
||||||
wlr_output_commit_state(m->wlr_output, &state);
|
wlr_output_commit_state(m->wlr_output, &state);
|
||||||
m->asleep = !m->wlr_output->enabled;
|
m->asleep = !m->wlr_output->enabled;
|
||||||
|
|
@ -1640,6 +1743,8 @@ int32_t toggle_monitor(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t scroller_stack(const Arg *arg) {
|
int32_t scroller_stack(const Arg *arg) {
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
Client *c = selmon->sel;
|
Client *c = selmon->sel;
|
||||||
Client *stack_head = NULL;
|
Client *stack_head = NULL;
|
||||||
Client *source_stack_head = NULL;
|
Client *source_stack_head = NULL;
|
||||||
|
|
|
||||||
|
|
@ -77,15 +77,6 @@ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) {
|
||||||
return NULL;
|
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; }
|
bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; }
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -255,7 +246,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
|
||||||
cursor_rect = (struct wlr_box){0};
|
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)) {
|
if (!output_is_usable(output)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,9 @@ setclient_coordinate_center(Client *c, Monitor *tm, struct wlr_box geom,
|
||||||
int32_t len = 0;
|
int32_t len = 0;
|
||||||
Monitor *m = tm ? tm : selmon;
|
Monitor *m = tm ? tm : selmon;
|
||||||
|
|
||||||
|
if (!m)
|
||||||
|
return geom;
|
||||||
|
|
||||||
uint32_t cbw = check_hit_no_border(c) ? c->bw : 0;
|
uint32_t cbw = check_hit_no_border(c) ? c->bw : 0;
|
||||||
|
|
||||||
if (!c->no_force_center && m) {
|
if (!c->no_force_center && m) {
|
||||||
|
|
|
||||||
|
|
@ -100,10 +100,6 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
|
||||||
surface = wlr_scene_surface_try_from_buffer(
|
surface = wlr_scene_surface_try_from_buffer(
|
||||||
wlr_scene_buffer_from_node(node))
|
wlr_scene_buffer_from_node(node))
|
||||||
->surface;
|
->surface;
|
||||||
else if (node->type == WLR_SCENE_NODE_RECT) {
|
|
||||||
surface = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* start from the topmost layer,
|
/* start from the topmost layer,
|
||||||
find a sureface that can be focused by pointer,
|
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;
|
l = pnode->data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node->type == WLR_SCENE_NODE_RECT) {
|
||||||
|
if (c) {
|
||||||
|
surface = client_surface(c);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (psurface)
|
if (psurface)
|
||||||
|
|
|
||||||
|
|
@ -96,3 +96,81 @@ Monitor *xytomon(double x, double y) {
|
||||||
struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
|
struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
|
||||||
return o ? o->data : NULL;
|
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;
|
||||||
|
}
|
||||||
|
|
@ -283,6 +283,7 @@ void scroller(Monitor *m) {
|
||||||
struct wlr_box target_geom;
|
struct wlr_box target_geom;
|
||||||
int32_t focus_client_index = 0;
|
int32_t focus_client_index = 0;
|
||||||
bool need_scroller = false;
|
bool need_scroller = false;
|
||||||
|
bool over_overspread_to_left = false;
|
||||||
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
||||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||||
int32_t cur_gappov = enablegaps ? m->gappov : 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) {
|
if (n == 1 && scroller_ignore_proportion_single) {
|
||||||
need_scroller = true;
|
need_scroller = true;
|
||||||
}
|
}
|
||||||
|
|
@ -394,18 +435,26 @@ void scroller(Monitor *m) {
|
||||||
tempClients[focus_client_index], &target_geom);
|
tempClients[focus_client_index], &target_geom);
|
||||||
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
|
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
|
||||||
} else if (need_scroller) {
|
} else if (need_scroller) {
|
||||||
if (scroller_focus_center ||
|
if (need_apply_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)) {
|
|
||||||
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
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 {
|
} 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 -
|
? m->w.x + (m->w.width -
|
||||||
root_client->scroller_proportion *
|
tempClients[focus_client_index]
|
||||||
|
->scroller_proportion *
|
||||||
max_client_width -
|
max_client_width -
|
||||||
scroller_structs)
|
scroller_structs)
|
||||||
: m->w.x + scroller_structs;
|
: m->w.x + scroller_structs;
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,7 @@ void vertical_scroller(Monitor *m) {
|
||||||
struct wlr_box target_geom;
|
struct wlr_box target_geom;
|
||||||
int32_t focus_client_index = 0;
|
int32_t focus_client_index = 0;
|
||||||
bool need_scroller = false;
|
bool need_scroller = false;
|
||||||
|
bool over_overspread_to_up = false;
|
||||||
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 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) {
|
if (n == 1 && scroller_ignore_proportion_single) {
|
||||||
need_scroller = true;
|
need_scroller = true;
|
||||||
}
|
}
|
||||||
|
|
@ -381,18 +422,24 @@ void vertical_scroller(Monitor *m) {
|
||||||
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
|
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
|
||||||
cur_gappih);
|
cur_gappih);
|
||||||
} else if (need_scroller) {
|
} else if (need_scroller) {
|
||||||
if (scroller_focus_center ||
|
if (need_apply_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)) {
|
|
||||||
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
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 {
|
} else {
|
||||||
target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2
|
target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2
|
||||||
? m->w.y + (m->w.height -
|
? m->w.y + (m->w.height -
|
||||||
root_client->scroller_proportion *
|
tempClients[focus_client_index]
|
||||||
|
->scroller_proportion *
|
||||||
max_client_height -
|
max_client_height -
|
||||||
scroller_structs)
|
scroller_structs)
|
||||||
: m->w.y + scroller_structs;
|
: m->w.y + scroller_structs;
|
||||||
|
|
|
||||||
300
src/mango.c
300
src/mango.c
|
|
@ -489,6 +489,14 @@ typedef struct {
|
||||||
bool being_unmapped;
|
bool being_unmapped;
|
||||||
} LayerSurface;
|
} 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 {
|
typedef struct {
|
||||||
const char *symbol;
|
const char *symbol;
|
||||||
void (*arrange)(Monitor *);
|
void (*arrange)(Monitor *);
|
||||||
|
|
@ -506,13 +514,15 @@ struct Monitor {
|
||||||
struct wl_listener request_state;
|
struct wl_listener request_state;
|
||||||
struct wl_listener destroy_lock_surface;
|
struct wl_listener destroy_lock_surface;
|
||||||
struct wlr_session_lock_surface_v1 *lock_surface;
|
struct wlr_session_lock_surface_v1 *lock_surface;
|
||||||
struct wl_event_source *skip_timeout;
|
struct wl_event_source *skip_frame_timeout;
|
||||||
struct wlr_box m; /* monitor area, layout-relative */
|
struct wlr_box m; /* monitor area, layout-relative */
|
||||||
struct wlr_box w; /* window area, layout-relative */
|
struct wlr_box w; /* window area, layout-relative */
|
||||||
struct wl_list layers[4]; /* LayerSurface::link */
|
struct wl_list layers[4]; /* LayerSurface::link */
|
||||||
uint32_t seltags;
|
uint32_t seltags;
|
||||||
uint32_t tagset[2];
|
uint32_t tagset[2];
|
||||||
bool skiping_frame;
|
bool skiping_frame;
|
||||||
|
uint32_t resizing_count_pending;
|
||||||
|
uint32_t resizing_count_current;
|
||||||
|
|
||||||
struct wl_list dwl_ipc_outputs;
|
struct wl_list dwl_ipc_outputs;
|
||||||
int32_t gappih; /* horizontal gap between windows */
|
int32_t gappih; /* horizontal gap between windows */
|
||||||
|
|
@ -789,8 +799,10 @@ static Client *get_scroll_stack_head(Client *c);
|
||||||
static bool client_only_in_one_tag(Client *c);
|
static bool client_only_in_one_tag(Client *c);
|
||||||
static Client *get_focused_stack_client(Client *sc);
|
static Client *get_focused_stack_client(Client *sc);
|
||||||
static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc);
|
static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc);
|
||||||
static void monitor_stop_skip_timer(Monitor *m);
|
static void monitor_stop_skip_frame_timer(Monitor *m);
|
||||||
static int monitor_skip_frame_timeout_callback(void *data);
|
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 "data/static_keymap.h"
|
||||||
#include "dispatch/bind_declare.h"
|
#include "dispatch/bind_declare.h"
|
||||||
|
|
@ -895,6 +907,9 @@ static KeyMode keymode = {
|
||||||
.mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'},
|
.mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'},
|
||||||
.isdefault = true,
|
.isdefault = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static char *env_vars[] = {"DISPLAY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP",
|
||||||
|
"XDG_SESSION_TYPE", NULL};
|
||||||
static struct {
|
static struct {
|
||||||
enum wp_cursor_shape_device_v1_shape shape;
|
enum wp_cursor_shape_device_v1_shape shape;
|
||||||
struct wlr_surface *surface;
|
struct wlr_surface *surface;
|
||||||
|
|
@ -1382,6 +1397,9 @@ void applyrules(Client *c) {
|
||||||
Client *fc = NULL;
|
Client *fc = NULL;
|
||||||
Client *parent = NULL;
|
Client *parent = NULL;
|
||||||
|
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
|
||||||
parent = client_get_parent(c);
|
parent = client_get_parent(c);
|
||||||
|
|
||||||
Monitor *mon = parent && parent->mon ? parent->mon : selmon;
|
Monitor *mon = parent && parent->mon ? parent->mon : selmon;
|
||||||
|
|
@ -1470,7 +1488,8 @@ void applyrules(Client *c) {
|
||||||
/*-----------------------apply rule action-------------------------*/
|
/*-----------------------apply rule action-------------------------*/
|
||||||
|
|
||||||
// rule action only apply after map not apply in the init commit
|
// 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;
|
return;
|
||||||
|
|
||||||
// apply swallow rule
|
// apply swallow rule
|
||||||
|
|
@ -1496,6 +1515,7 @@ void applyrules(Client *c) {
|
||||||
setmon(c, mon, newtags,
|
setmon(c, mon, newtags,
|
||||||
!c->isopensilent &&
|
!c->isopensilent &&
|
||||||
!(client_is_x11_popup(c) && client_should_ignore_focus(c)) &&
|
!(client_is_x11_popup(c) && client_should_ignore_focus(c)) &&
|
||||||
|
mon &&
|
||||||
(!c->istagsilent || !newtags ||
|
(!c->istagsilent || !newtags ||
|
||||||
newtags & mon->tagset[mon->seltags]));
|
newtags & mon->tagset[mon->seltags]));
|
||||||
|
|
||||||
|
|
@ -1517,7 +1537,7 @@ void applyrules(Client *c) {
|
||||||
window in the current tag will exit fullscreen and participate in tiling
|
window in the current tag will exit fullscreen and participate in tiling
|
||||||
*/
|
*/
|
||||||
wl_list_for_each(fc, &clients,
|
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) &&
|
VISIBLEON(fc, c->mon) && ISFULLSCREEN(fc) &&
|
||||||
!c->isfloating) {
|
!c->isfloating) {
|
||||||
clear_fullscreen_flag(fc);
|
clear_fullscreen_flag(fc);
|
||||||
|
|
@ -1536,7 +1556,7 @@ void applyrules(Client *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply overlay rule
|
// apply overlay rule
|
||||||
if (c->isoverlay) {
|
if (c->isoverlay && c->scene) {
|
||||||
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
|
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
|
||||||
wlr_scene_node_raise_to_top(&c->scene->node);
|
wlr_scene_node_raise_to_top(&c->scene->node);
|
||||||
}
|
}
|
||||||
|
|
@ -1676,6 +1696,7 @@ void focuslayer(LayerSurface *l) {
|
||||||
void reset_exclusive_layer(Monitor *m) {
|
void reset_exclusive_layer(Monitor *m) {
|
||||||
LayerSurface *l = NULL;
|
LayerSurface *l = NULL;
|
||||||
int32_t i;
|
int32_t i;
|
||||||
|
bool neet_change_focus_to_client = false;
|
||||||
uint32_t layers_above_shell[] = {
|
uint32_t layers_above_shell[] = {
|
||||||
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
|
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
|
||||||
ZWLR_LAYER_SHELL_V1_LAYER_TOP,
|
ZWLR_LAYER_SHELL_V1_LAYER_TOP,
|
||||||
|
|
@ -1689,13 +1710,19 @@ void reset_exclusive_layer(Monitor *m) {
|
||||||
wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
|
wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
|
||||||
if (l == exclusive_focus &&
|
if (l == exclusive_focus &&
|
||||||
l->layer_surface->current.keyboard_interactive !=
|
l->layer_surface->current.keyboard_interactive !=
|
||||||
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
|
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
|
||||||
|
|
||||||
exclusive_focus = NULL;
|
exclusive_focus = NULL;
|
||||||
|
|
||||||
|
neet_change_focus_to_client = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (l->layer_surface->current.keyboard_interactive ==
|
if (l->layer_surface->current.keyboard_interactive ==
|
||||||
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
|
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
|
||||||
l->layer_surface->surface ==
|
l->layer_surface->surface ==
|
||||||
seat->keyboard_state.focused_surface)
|
seat->keyboard_state.focused_surface) {
|
||||||
focusclient(focustop(selmon), 1);
|
neet_change_focus_to_client = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (locked ||
|
if (locked ||
|
||||||
l->layer_surface->current.keyboard_interactive !=
|
l->layer_surface->current.keyboard_interactive !=
|
||||||
|
|
@ -1708,6 +1735,10 @@ void reset_exclusive_layer(Monitor *m) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (neet_change_focus_to_client) {
|
||||||
|
focusclient(focustop(selmon), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void arrangelayers(Monitor *m) {
|
void arrangelayers(Monitor *m) {
|
||||||
|
|
@ -2232,10 +2263,10 @@ void cleanupmon(struct wl_listener *listener, void *data) {
|
||||||
wlr_scene_node_destroy(&m->blur->node);
|
wlr_scene_node_destroy(&m->blur->node);
|
||||||
m->blur = NULL;
|
m->blur = NULL;
|
||||||
}
|
}
|
||||||
if (m->skip_timeout) {
|
if (m->skip_frame_timeout) {
|
||||||
monitor_stop_skip_timer(m);
|
monitor_stop_skip_frame_timer(m);
|
||||||
wl_event_source_remove(m->skip_timeout);
|
wl_event_source_remove(m->skip_frame_timeout);
|
||||||
m->skip_timeout = NULL;
|
m->skip_frame_timeout = NULL;
|
||||||
}
|
}
|
||||||
m->wlr_output->data = NULL;
|
m->wlr_output->data = NULL;
|
||||||
free(m->pertag);
|
free(m->pertag);
|
||||||
|
|
@ -2367,13 +2398,6 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
// 刷新布局,让窗口能感应到exclude_zone变化以及设置独占表面
|
// 刷新布局,让窗口能感应到exclude_zone变化以及设置独占表面
|
||||||
arrangelayers(l->mon);
|
arrangelayers(l->mon);
|
||||||
|
|
||||||
// 按需交互layer需要像正常窗口一样抢占非独占layer的焦点
|
|
||||||
if (!exclusive_focus &&
|
|
||||||
l->layer_surface->current.keyboard_interactive ==
|
|
||||||
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
|
|
||||||
focuslayer(l);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
|
void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
|
||||||
|
|
@ -2394,6 +2418,12 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
|
||||||
arrangelayers(l->mon);
|
arrangelayers(l->mon);
|
||||||
l->layer_surface->current = old_state;
|
l->layer_surface->current = old_state;
|
||||||
|
|
||||||
|
// 按需交互layer只在map之前设置焦点
|
||||||
|
if (!exclusive_focus &&
|
||||||
|
l->layer_surface->current.keyboard_interactive ==
|
||||||
|
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
|
||||||
|
focuslayer(l);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2438,11 +2468,6 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
layer_flush_blur_background(l);
|
layer_flush_blur_background(l);
|
||||||
|
|
||||||
if (layer_surface == exclusive_focus &&
|
|
||||||
layer_surface->current.keyboard_interactive !=
|
|
||||||
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
|
|
||||||
exclusive_focus = NULL;
|
|
||||||
|
|
||||||
if (layer_surface->current.committed == 0 &&
|
if (layer_surface->current.committed == 0 &&
|
||||||
l->mapped == layer_surface->surface->mapped)
|
l->mapped == layer_surface->surface->mapped)
|
||||||
return;
|
return;
|
||||||
|
|
@ -2535,37 +2560,105 @@ void destroydecoration(struct wl_listener *listener, void *data) {
|
||||||
wl_list_remove(&c->set_decoration_mode.link);
|
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_surface *surface = data;
|
||||||
struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface);
|
struct wlr_xdg_popup *wkr_popup =
|
||||||
LayerSurface *l = NULL;
|
wlr_xdg_popup_try_from_wlr_surface(surface);
|
||||||
|
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
struct wlr_box box;
|
LayerSurface *l = NULL;
|
||||||
int32_t type = -1;
|
int32_t type = -1;
|
||||||
|
|
||||||
if (!popup || !popup->base->initial_commit)
|
if (!wkr_popup || !wkr_popup->base->initial_commit)
|
||||||
goto commitpopup_listen_free;
|
goto commitpopup_listen_free;
|
||||||
|
|
||||||
type = toplevel_from_wlr_surface(popup->base->surface, &c, &l);
|
type = toplevel_from_wlr_surface(wkr_popup->base->surface, &c, &l);
|
||||||
if (!popup->parent || !popup->parent->data || type < 0)
|
if (!wkr_popup->parent || !wkr_popup->parent->data || type < 0) {
|
||||||
goto commitpopup_listen_free;
|
wlr_xdg_popup_destroy(wkr_popup);
|
||||||
|
|
||||||
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);
|
|
||||||
goto commitpopup_listen_free;
|
goto commitpopup_listen_free;
|
||||||
}
|
}
|
||||||
box = type == LayerShell ? l->mon->m : c->mon->w;
|
|
||||||
box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x);
|
wlr_scene_node_raise_to_top(wkr_popup->parent->data);
|
||||||
box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y);
|
|
||||||
wlr_xdg_popup_unconstrain_from_box(popup, &box);
|
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:
|
commitpopup_listen_free:
|
||||||
wl_list_remove(&listener->link);
|
wl_list_remove(&popup->commit.link);
|
||||||
free(listener);
|
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) {
|
void createdecoration(struct wl_listener *listener, void *data) {
|
||||||
|
|
@ -2788,9 +2881,11 @@ void createmon(struct wl_listener *listener, void *data) {
|
||||||
struct wl_event_loop *loop = wl_display_get_event_loop(dpy);
|
struct wl_event_loop *loop = wl_display_get_event_loop(dpy);
|
||||||
m = wlr_output->data = ecalloc(1, sizeof(*m));
|
m = wlr_output->data = ecalloc(1, sizeof(*m));
|
||||||
|
|
||||||
m->skip_timeout =
|
m->skip_frame_timeout =
|
||||||
wl_event_loop_add_timer(loop, monitor_skip_frame_timeout_callback, m);
|
wl_event_loop_add_timer(loop, monitor_skip_frame_timeout_callback, m);
|
||||||
m->skiping_frame = false;
|
m->skiping_frame = false;
|
||||||
|
m->resizing_count_pending = 0;
|
||||||
|
m->resizing_count_current = 0;
|
||||||
|
|
||||||
m->wlr_output = wlr_output;
|
m->wlr_output = wlr_output;
|
||||||
m->wlr_output->data = m;
|
m->wlr_output->data = m;
|
||||||
|
|
@ -2809,6 +2904,8 @@ void createmon(struct wl_listener *listener, void *data) {
|
||||||
m->isoverview = 0;
|
m->isoverview = 0;
|
||||||
m->sel = NULL;
|
m->sel = NULL;
|
||||||
m->is_in_hotarea = 0;
|
m->is_in_hotarea = 0;
|
||||||
|
m->m.x = INT32_MAX;
|
||||||
|
m->m.y = INT32_MAX;
|
||||||
float scale = 1;
|
float scale = 1;
|
||||||
enum wl_output_transform rr = WL_OUTPUT_TRANSFORM_NORMAL;
|
enum wl_output_transform rr = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
wlr_output_state_set_scale(&state, scale);
|
wlr_output_state_set_scale(&state, scale);
|
||||||
|
|
@ -3151,13 +3248,6 @@ void createpointerconstraint(struct wl_listener *listener, void *data) {
|
||||||
&pointer_constraint->destroy, destroypointerconstraint);
|
&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) {
|
void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) {
|
||||||
if (active_constraint == constraint)
|
if (active_constraint == constraint)
|
||||||
return;
|
return;
|
||||||
|
|
@ -3253,7 +3343,6 @@ void destroylocksurface(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
if (lock_surface->surface != seat->keyboard_state.focused_surface) {
|
if (lock_surface->surface != seat->keyboard_state.focused_surface) {
|
||||||
if (exclusive_focus && !locked) {
|
if (exclusive_focus && !locked) {
|
||||||
exclusive_focus = NULL;
|
|
||||||
reset_exclusive_layer(m);
|
reset_exclusive_layer(m);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -3263,7 +3352,6 @@ void destroylocksurface(struct wl_listener *listener, void *data) {
|
||||||
surface = wl_container_of(cur_lock->surfaces.next, surface, link);
|
surface = wl_container_of(cur_lock->surfaces.next, surface, link);
|
||||||
client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat));
|
client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat));
|
||||||
} else if (!locked) {
|
} else if (!locked) {
|
||||||
exclusive_focus = NULL;
|
|
||||||
reset_exclusive_layer(selmon);
|
reset_exclusive_layer(selmon);
|
||||||
focusclient(focustop(selmon), 1);
|
focusclient(focustop(selmon), 1);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -4190,19 +4278,6 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
|
||||||
struct wlr_pointer_constraint_v1 *constraint;
|
struct wlr_pointer_constraint_v1 *constraint;
|
||||||
bool should_lock = false;
|
bool should_lock = false;
|
||||||
|
|
||||||
/* Find the client under the pointer and send the event along. */
|
|
||||||
xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy);
|
|
||||||
|
|
||||||
if (cursor_mode == CurPressed && !seat->drag &&
|
|
||||||
surface != seat->pointer_state.focused_surface &&
|
|
||||||
toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w,
|
|
||||||
&l) >= 0) {
|
|
||||||
c = w;
|
|
||||||
surface = seat->pointer_state.focused_surface;
|
|
||||||
sx = cursor->x - (l ? l->scene->node.x : w->geom.x);
|
|
||||||
sy = cursor->y - (l ? l->scene->node.y : w->geom.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* time is 0 in internal calls meant to restore pointer focus. */
|
/* time is 0 in internal calls meant to restore pointer focus. */
|
||||||
if (time) {
|
if (time) {
|
||||||
wlr_relative_pointer_manager_v1_send_relative_motion(
|
wlr_relative_pointer_manager_v1_send_relative_motion(
|
||||||
|
|
@ -4240,6 +4315,19 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
|
||||||
selmon = xytomon(cursor->x, cursor->y);
|
selmon = xytomon(cursor->x, cursor->y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find the client under the pointer and send the event along. */
|
||||||
|
xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy);
|
||||||
|
|
||||||
|
if (cursor_mode == CurPressed && !seat->drag &&
|
||||||
|
surface != seat->pointer_state.focused_surface &&
|
||||||
|
toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w,
|
||||||
|
&l) >= 0) {
|
||||||
|
c = w;
|
||||||
|
surface = seat->pointer_state.focused_surface;
|
||||||
|
sx = cursor->x - (l ? l->scene->node.x : w->geom.x);
|
||||||
|
sy = cursor->y - (l ? l->scene->node.y : w->geom.y);
|
||||||
|
}
|
||||||
|
|
||||||
/* Update drag icon's position */
|
/* Update drag icon's position */
|
||||||
wlr_scene_node_set_position(&drag_icon->node, (int32_t)round(cursor->x),
|
wlr_scene_node_set_position(&drag_icon->node, (int32_t)round(cursor->x),
|
||||||
(int32_t)round(cursor->y));
|
(int32_t)round(cursor->y));
|
||||||
|
|
@ -4441,14 +4529,17 @@ void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, int32_t sx,
|
||||||
}
|
}
|
||||||
|
|
||||||
void client_set_opacity(Client *c, double opacity) {
|
void client_set_opacity(Client *c, double opacity) {
|
||||||
|
opacity = CLAMP_FLOAT(opacity, 0.0f, 1.0f);
|
||||||
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
|
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
|
||||||
scene_buffer_apply_opacity, &opacity);
|
scene_buffer_apply_opacity, &opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void monitor_stop_skip_timer(Monitor *m) {
|
void monitor_stop_skip_frame_timer(Monitor *m) {
|
||||||
if (m->skip_timeout)
|
if (m->skip_frame_timeout)
|
||||||
wl_event_source_timer_update(m->skip_timeout, 0);
|
wl_event_source_timer_update(m->skip_frame_timeout, 0);
|
||||||
m->skiping_frame = false;
|
m->skiping_frame = false;
|
||||||
|
m->resizing_count_pending = 0;
|
||||||
|
m->resizing_count_current = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int monitor_skip_frame_timeout_callback(void *data) {
|
static int monitor_skip_frame_timeout_callback(void *data) {
|
||||||
|
|
@ -4457,27 +4548,28 @@ static int monitor_skip_frame_timeout_callback(void *data) {
|
||||||
|
|
||||||
wl_list_for_each_safe(c, tmp, &clients, link) { c->configure_serial = 0; }
|
wl_list_for_each_safe(c, tmp, &clients, link) { c->configure_serial = 0; }
|
||||||
|
|
||||||
monitor_stop_skip_timer(m);
|
monitor_stop_skip_frame_timer(m);
|
||||||
wlr_output_schedule_frame(m->wlr_output);
|
wlr_output_schedule_frame(m->wlr_output);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void monitor_check_skip_frame_timeout(Monitor *m) {
|
void monitor_check_skip_frame_timeout(Monitor *m) {
|
||||||
if (m->skiping_frame) {
|
if (m->skiping_frame &&
|
||||||
|
m->resizing_count_pending == m->resizing_count_current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m->skip_timeout) {
|
if (m->skip_frame_timeout) {
|
||||||
wl_event_source_timer_update(m->skip_timeout, 100); // 100ms
|
m->resizing_count_current = m->resizing_count_pending;
|
||||||
m->skiping_frame = true;
|
m->skiping_frame = true;
|
||||||
|
wl_event_source_timer_update(m->skip_frame_timeout, 100); // 100ms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rendermon(struct wl_listener *listener, void *data) {
|
void rendermon(struct wl_listener *listener, void *data) {
|
||||||
Monitor *m = wl_container_of(listener, m, frame);
|
Monitor *m = wl_container_of(listener, m, frame);
|
||||||
Client *c = NULL, *tmp = NULL;
|
Client *c = NULL, *tmp = NULL;
|
||||||
struct wlr_output_state pending = {0};
|
|
||||||
LayerSurface *l = NULL, *tmpl = NULL;
|
LayerSurface *l = NULL, *tmpl = NULL;
|
||||||
int32_t i;
|
int32_t i;
|
||||||
struct wl_list *layer_list;
|
struct wl_list *layer_list;
|
||||||
|
|
@ -4513,16 +4605,15 @@ void rendermon(struct wl_listener *listener, void *data) {
|
||||||
// 绘制客户端
|
// 绘制客户端
|
||||||
wl_list_for_each(c, &clients, link) {
|
wl_list_for_each(c, &clients, link) {
|
||||||
need_more_frames = client_draw_frame(c) || need_more_frames;
|
need_more_frames = client_draw_frame(c) || need_more_frames;
|
||||||
if (!animations && !(allow_tearing && frame_allow_tearing) &&
|
if (!animations && !grabc && c->configure_serial &&
|
||||||
c->configure_serial && client_is_rendered_on_mon(c, m) &&
|
client_is_rendered_on_mon(c, m)) {
|
||||||
!client_is_stopped(c) && !grabc) {
|
|
||||||
monitor_check_skip_frame_timeout(m);
|
monitor_check_skip_frame_timeout(m);
|
||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m->skiping_frame) {
|
if (m->skiping_frame) {
|
||||||
monitor_stop_skip_timer(m);
|
monitor_stop_skip_frame_timer(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只有在需要帧时才构建和提交状态
|
// 只有在需要帧时才构建和提交状态
|
||||||
|
|
@ -4535,12 +4626,7 @@ void rendermon(struct wl_listener *listener, void *data) {
|
||||||
skip:
|
skip:
|
||||||
// 发送帧完成通知
|
// 发送帧完成通知
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
if (allow_tearing && frame_allow_tearing) {
|
wlr_scene_output_send_frame_done(m->scene_output, &now);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果需要更多帧,确保安排下一帧
|
// 如果需要更多帧,确保安排下一帧
|
||||||
if (need_more_frames && allow_frame_scheduling) {
|
if (need_more_frames && allow_frame_scheduling) {
|
||||||
|
|
@ -4722,6 +4808,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
|
void // 17
|
||||||
run(char *startup_cmd) {
|
run(char *startup_cmd) {
|
||||||
|
|
||||||
|
|
@ -4779,6 +4900,8 @@ run(char *startup_cmd) {
|
||||||
wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr");
|
wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr");
|
||||||
handlecursoractivity();
|
handlecursoractivity();
|
||||||
|
|
||||||
|
set_activation_env();
|
||||||
|
|
||||||
run_exec();
|
run_exec();
|
||||||
run_exec_once();
|
run_exec_once();
|
||||||
|
|
||||||
|
|
@ -5275,6 +5398,7 @@ void setup(void) {
|
||||||
|
|
||||||
setenv("XCURSOR_SIZE", "24", 1);
|
setenv("XCURSOR_SIZE", "24", 1);
|
||||||
setenv("XDG_CURRENT_DESKTOP", "mango", 1);
|
setenv("XDG_CURRENT_DESKTOP", "mango", 1);
|
||||||
|
|
||||||
parse_config();
|
parse_config();
|
||||||
init_baked_points();
|
init_baked_points();
|
||||||
|
|
||||||
|
|
@ -5977,6 +6101,10 @@ void updatemons(struct wl_listener *listener, void *data) {
|
||||||
c->mon = selmon;
|
c->mon = selmon;
|
||||||
reset_foreign_tolevel(c);
|
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);
|
focusclient(focustop(selmon), 1);
|
||||||
if (selmon->lock_surface) {
|
if (selmon->lock_surface) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue