Merge branch 'main' into wlr20-xcursor

This commit is contained in:
DreamMaoMao 2026-03-08 13:15:32 +08:00 committed by GitHub
commit ea3a1f0b61
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 3091 additions and 829 deletions

View file

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

View file

@ -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/).
@ -23,25 +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) - Ipc support(get/send message from/to compositor by external program)
- Hycov-like overview - Hycov-like overview
- Window effects from scenefx (blur, shadow, corner radius, opacity) - Window effects from scenefx (blur, shadow, corner radius, opacity)
- Zero flickering - every frame is perfect.
Master-Stack Layout https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
https://github.com/user-attachments/assets/a9d4776e-b50b-48fb-94ce-651d8a749b8a # Mango's Vision
Scroller Layout **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.
https://github.com/user-attachments/assets/c9bf9415-fad1-4400-bcdc-3ad2d76de85a **Mango's preference is practicality**: I tend to add features that genuinely help with daily workflows—things that make our work more convenient.
Layer animation
https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
**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
@ -51,21 +48,20 @@ https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
- vertical_tile - vertical_tile
- vertical_grid - vertical_grid
- vertical_scroller - vertical_scroller
- tgmix
# Installation # Installation
[![Packaging status](https://repology.org/badge/vertical-allrepos/mangowm.svg)](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
@ -75,9 +71,9 @@ https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
- libxcb - libxcb
## Arch Linux ## Arch Linux
The package is in the Arch User Repository and is availble 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
``` ```
@ -91,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
@ -106,9 +102,38 @@ 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
``` ```
## 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 ## Other
```bash ```bash
@ -122,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
``` ```
@ -181,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
@ -203,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";
}; };
}; };
@ -265,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).

View file

@ -26,7 +26,7 @@ focused_opacity=1.0
unfocused_opacity=1.0 unfocused_opacity=1.0
# Animation Configuration(support type:zoom,slide) # Animation Configuration(support type:zoom,slide)
# tag_animation_direction: 0-horizontal,1-vertical # tag_animation_direction: 1-horizontal,0-vertical
animations=1 animations=1
layer_animations=1 layer_animations=1
animation_type_open=slide animation_type_open=slide
@ -240,12 +240,11 @@ bind=CTRL+ALT,Left,resizewin,-50,+0
bind=CTRL+ALT,Right,resizewin,+50,+0 bind=CTRL+ALT,Right,resizewin,+50,+0
# Mouse Button Bindings # Mouse Button Bindings
# NONE mode key only work in ov mode # btn_left and btn_right can't bind none mod key
mousebind=SUPER,btn_left,moveresize,curmove mousebind=SUPER,btn_left,moveresize,curmove
mousebind=NONE,btn_middle,togglemaximizescreen,0 mousebind=NONE,btn_middle,togglemaximizescreen,0
mousebind=SUPER,btn_right,moveresize,curresize mousebind=SUPER,btn_right,moveresize,curresize
mousebind=NONE,btn_left,toggleoverview,1
mousebind=NONE,btn_right,killclient,0
# Axis Bindings # Axis Bindings
axisbind=SUPER,UP,viewtoleft_have_client axisbind=SUPER,UP,viewtoleft_have_client

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,6 +1,7 @@
[Desktop Entry] [Desktop Entry]
Encoding=UTF-8 Encoding=UTF-8
Name=Mango Name=Mango
DesktopNames=mango;wlroots
Comment=mango WM Comment=mango WM
Exec=mango Exec=mango
Icon=mango Icon=mango

View file

@ -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,13 @@
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 (define-deprecated-package mangowc
mangowm-git)
mangowm-git

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'], project('mango', ['c', 'cpp'],
version : '0.10.10', version : '0.12.5',
) )
subdir('protocols') subdir('protocols')
@ -56,7 +56,7 @@ endif
if is_git_repo if is_git_repo
# 如果是 Git 目录,获取 Commit Hash 和最新的 tag # 如果是 Git 目录,获取 Commit Hash 和最新的 tag
commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip() 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) version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
else else
# 如果不是 Git 目录,使用项目版本号和 "release" 字符串 # 如果不是 Git 目录,使用项目版本号和 "release" 字符串
@ -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'))

View file

@ -500,12 +500,48 @@ static const struct wl_registry_listener registry_listener = {
static void usage(void) { static void usage(void) {
fprintf(stderr, fprintf(stderr,
"usage:" "mmsg - MangoWM IPC\n"
"\t%s [-OTLq]\n" "\n"
"\t%s [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d " "SYNOPSIS:\n"
"\tmmsg [-OTLq]\n"
"\tmmsg [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d "
"<cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]\n" "<cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]\n"
"\t%s [-o <output>] (-g | -w) [-OotlcvmfxekbA]\n", "\tmmsg [-o <output>] (-g | -w) [-OotlcvmfxekbA]\n"
argv0, argv0, argv0); "\n"
"OPERATION MODES:\n"
"\t-g Get values (tags, layout, focused client)\n"
"\t-s Set values (switch tags, layouts)\n"
"\t-w Watch mode (stream events)\n"
"\n"
"GENERAL OPTIONS:\n"
"\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 mango\n"
"\t-o <output> Select output (monitor)\n"
"\n"
"GET OPTIONS (used with -g or -w):\n"
"\t-O Get output name\n"
"\t-o Get output (monitor) focus information\n"
"\t-t Get selected tags\n"
"\t-l Get current layout\n"
"\t-c Get title and appid of focused clients\n"
"\t-v Get visibility of statusbar\n"
"\t-m Get fullscreen status\n"
"\t-f Get floating status\n"
"\t-x Get focused client geometry\n"
"\t-e Get name of last focused layer\n"
"\t-k Get current keyboard layout\n"
"\t-b Get current keybind mode\n"
"\t-A Get scale factor of monitor\n"
"\n"
"SET OPTIONS (used with -s):\n"
"\t-o <output> Select output (monitor)\n"
"\t-t <tags> Set selected tags (can be used with [+-^.] "
"modifiers)\n"
"\t-l <layout> Set current layout\n"
"\t-c <tags> Get title and appid of focused client\n"
"\t-d <cmd>,<args...> Dispatch internal command (max 5 args)\n");
exit(2); exit(2);
} }

View file

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

View file

@ -1,6 +1,6 @@
wayland_scanner = find_program('wayland-scanner') wayland_scanner = find_program('wayland-scanner')
wayland_protos_dep = dependency('wayland-protocols') 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_code = generator(
wayland_scanner, wayland_scanner,
output: '@BASENAME@-protocol.c', output: '@BASENAME@-protocol.c',

View file

@ -47,7 +47,7 @@ bool is_horizontal_right_stack_layout(Monitor *m) {
return false; return false;
} }
int32_t is_special_animaiton_rule(Client *c) { int32_t is_special_animation_rule(Client *c) {
if (is_scroller_layout(c->mon) && !c->isfloating) { if (is_scroller_layout(c->mon) && !c->isfloating) {
return DOWN; return DOWN;
@ -70,7 +70,7 @@ int32_t is_special_animaiton_rule(Client *c) {
} }
} }
void set_client_open_animaiton(Client *c, struct wlr_box geo) { void set_client_open_animation(Client *c, struct wlr_box geo) {
int32_t slide_direction; int32_t slide_direction;
int32_t horizontal, horizontal_value; int32_t horizontal, horizontal_value;
int32_t vertical, vertical_value; int32_t vertical, vertical_value;
@ -96,7 +96,7 @@ void set_client_open_animaiton(Client *c, struct wlr_box geo) {
geo.y + (geo.height - c->animainit_geom.height) / 2; geo.y + (geo.height - c->animainit_geom.height) / 2;
return; return;
} else { } else {
special_direction = is_special_animaiton_rule(c); special_direction = is_special_animation_rule(c);
center_x = c->geom.x + c->geom.width / 2; center_x = c->geom.x + c->geom.width / 2;
center_y = c->geom.y + c->geom.height / 2; center_y = c->geom.y + c->geom.height / 2;
if (special_direction == UNDIR) { if (special_direction == UNDIR) {
@ -363,7 +363,6 @@ void apply_border(Client *c) {
current_corner_location = set_client_corner_location(c); current_corner_location = set_client_corner_location(c);
} }
// Handle no-border cases
if (hit_no_border && smartgaps) { if (hit_no_border && smartgaps) {
c->bw = 0; c->bw = 0;
c->fake_no_border = true; c->fake_no_border = true;
@ -773,71 +772,71 @@ void init_fadeout_client(Client *c) {
return; return;
} }
Client *fadeout_cient = ecalloc(1, sizeof(*fadeout_cient)); Client *fadeout_client = ecalloc(1, sizeof(*fadeout_client));
wlr_scene_node_set_enabled(&c->scene->node, true); wlr_scene_node_set_enabled(&c->scene->node, true);
client_set_border_color(c, bordercolor); client_set_border_color(c, bordercolor);
fadeout_cient->scene = fadeout_client->scene =
wlr_scene_tree_snapshot(&c->scene->node, layers[LyrFadeOut]); wlr_scene_tree_snapshot(&c->scene->node, layers[LyrFadeOut]);
wlr_scene_node_set_enabled(&c->scene->node, false); wlr_scene_node_set_enabled(&c->scene->node, false);
if (!fadeout_cient->scene) { if (!fadeout_client->scene) {
free(fadeout_cient); free(fadeout_client);
return; return;
} }
fadeout_cient->animation.duration = animation_duration_close; fadeout_client->animation.duration = animation_duration_close;
fadeout_cient->geom = fadeout_cient->current = fadeout_client->geom = fadeout_client->current =
fadeout_cient->animainit_geom = fadeout_cient->animation.initial = fadeout_client->animainit_geom = fadeout_client->animation.initial =
c->animation.current; c->animation.current;
fadeout_cient->mon = c->mon; fadeout_client->mon = c->mon;
fadeout_cient->animation_type_close = c->animation_type_close; fadeout_client->animation_type_close = c->animation_type_close;
fadeout_cient->animation.action = CLOSE; fadeout_client->animation.action = CLOSE;
fadeout_cient->bw = c->bw; fadeout_client->bw = c->bw;
fadeout_cient->nofadeout = c->nofadeout; fadeout_client->nofadeout = c->nofadeout;
// 这里snap节点的坐标设置是使用的相对坐标所以不能加上原来坐标 // 这里snap节点的坐标设置是使用的相对坐标所以不能加上原来坐标
// 这跟普通node有区别 // 这跟普通node有区别
fadeout_cient->animation.initial.x = 0; fadeout_client->animation.initial.x = 0;
fadeout_cient->animation.initial.y = 0; fadeout_client->animation.initial.y = 0;
if ((!c->animation_type_close && if ((!c->animation_type_close &&
strcmp(animation_type_close, "fade") == 0) || strcmp(animation_type_close, "fade") == 0) ||
(c->animation_type_close && (c->animation_type_close &&
strcmp(c->animation_type_close, "fade") == 0)) { strcmp(c->animation_type_close, "fade") == 0)) {
fadeout_cient->current.x = 0; fadeout_client->current.x = 0;
fadeout_cient->current.y = 0; fadeout_client->current.y = 0;
fadeout_cient->current.width = 0; fadeout_client->current.width = 0;
fadeout_cient->current.height = 0; fadeout_client->current.height = 0;
} else if ((c->animation_type_close && } else if ((c->animation_type_close &&
strcmp(c->animation_type_close, "slide") == 0) || strcmp(c->animation_type_close, "slide") == 0) ||
(!c->animation_type_close && (!c->animation_type_close &&
strcmp(animation_type_close, "slide") == 0)) { strcmp(animation_type_close, "slide") == 0)) {
fadeout_cient->current.y = fadeout_client->current.y =
c->geom.y + c->geom.height / 2 > c->mon->m.y + c->mon->m.height / 2 c->geom.y + c->geom.height / 2 > c->mon->m.y + c->mon->m.height / 2
? c->mon->m.height - ? c->mon->m.height -
(c->animation.current.y - c->mon->m.y) // down out (c->animation.current.y - c->mon->m.y) // down out
: c->mon->m.y - c->geom.height; // up out : c->mon->m.y - c->geom.height; // up out
fadeout_cient->current.x = 0; // x无偏差垂直划出 fadeout_client->current.x = 0; // x无偏差垂直划出
} else { } else {
fadeout_cient->current.y = fadeout_client->current.y =
(fadeout_cient->geom.height - (fadeout_client->geom.height -
fadeout_cient->geom.height * zoom_end_ratio) / fadeout_client->geom.height * zoom_end_ratio) /
2; 2;
fadeout_cient->current.x = fadeout_client->current.x =
(fadeout_cient->geom.width - (fadeout_client->geom.width -
fadeout_cient->geom.width * zoom_end_ratio) / fadeout_client->geom.width * zoom_end_ratio) /
2; 2;
fadeout_cient->current.width = fadeout_client->current.width =
fadeout_cient->geom.width * zoom_end_ratio; fadeout_client->geom.width * zoom_end_ratio;
fadeout_cient->current.height = fadeout_client->current.height =
fadeout_cient->geom.height * zoom_end_ratio; fadeout_client->geom.height * zoom_end_ratio;
} }
fadeout_cient->animation.time_started = get_now_in_ms(); fadeout_client->animation.time_started = get_now_in_ms();
wlr_scene_node_set_enabled(&fadeout_cient->scene->node, true); wlr_scene_node_set_enabled(&fadeout_client->scene->node, true);
wl_list_insert(&fadeout_clients, &fadeout_cient->fadeout_link); wl_list_insert(&fadeout_clients, &fadeout_client->fadeout_link);
// 请求刷新屏幕 // 请求刷新屏幕
request_fresh_all_monitors(); request_fresh_all_monitors();
@ -972,7 +971,7 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
c->animainit_geom.height = c->animation.current.height; c->animainit_geom.height = c->animation.current.height;
c->animainit_geom.width = c->animation.current.width; c->animainit_geom.width = c->animation.current.width;
} else if (c->is_pending_open_animation) { } else if (c->is_pending_open_animation) {
set_client_open_animaiton(c, c->geom); set_client_open_animation(c, c->geom);
} else { } else {
c->animainit_geom = c->animation.current; c->animainit_geom = c->animation.current;
} }
@ -981,10 +980,20 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
c->bw = 0; c->bw = 0;
} }
bool hit_no_border = check_hit_no_border(c);
if (hit_no_border && smartgaps) {
c->bw = 0;
c->fake_no_border = true;
}
// c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局 // c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局
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;
@ -1049,20 +1058,11 @@ void client_set_focused_opacity_animation(Client *c) {
sizeof(c->opacity_animation.target_border_color)); sizeof(c->opacity_animation.target_border_color));
c->opacity_animation.target_opacity = c->focused_opacity; c->opacity_animation.target_opacity = c->focused_opacity;
c->opacity_animation.time_started = get_now_in_ms(); c->opacity_animation.time_started = get_now_in_ms();
if (c->opacity_animation.running) { memcpy(c->opacity_animation.initial_border_color,
memcpy(c->opacity_animation.initial_border_color, c->opacity_animation.current_border_color,
c->opacity_animation.current_border_color, sizeof(c->opacity_animation.initial_border_color));
sizeof(c->opacity_animation.initial_border_color)); c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity;
c->opacity_animation.initial_opacity =
c->opacity_animation.current_opacity;
} else {
memcpy(c->opacity_animation.initial_border_color, border_color,
sizeof(c->opacity_animation.initial_border_color));
memcpy(c->opacity_animation.current_border_color, border_color,
sizeof(c->opacity_animation.current_border_color));
c->opacity_animation.initial_opacity = c->unfocused_opacity;
c->opacity_animation.current_opacity = c->unfocused_opacity;
}
c->opacity_animation.running = true; c->opacity_animation.running = true;
} }
@ -1082,20 +1082,10 @@ void client_set_unfocused_opacity_animation(Client *c) {
c->opacity_animation.target_opacity = c->unfocused_opacity; c->opacity_animation.target_opacity = c->unfocused_opacity;
c->opacity_animation.time_started = get_now_in_ms(); c->opacity_animation.time_started = get_now_in_ms();
if (c->opacity_animation.running) { memcpy(c->opacity_animation.initial_border_color,
memcpy(c->opacity_animation.initial_border_color, c->opacity_animation.current_border_color,
c->opacity_animation.current_border_color, sizeof(c->opacity_animation.initial_border_color));
sizeof(c->opacity_animation.initial_border_color)); c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity;
c->opacity_animation.initial_opacity =
c->opacity_animation.current_opacity;
} else {
memcpy(c->opacity_animation.initial_border_color, border_color,
sizeof(c->opacity_animation.initial_border_color));
memcpy(c->opacity_animation.current_border_color, border_color,
sizeof(c->opacity_animation.current_border_color));
c->opacity_animation.initial_opacity = c->focused_opacity;
c->opacity_animation.current_opacity = c->focused_opacity;
}
c->opacity_animation.running = true; c->opacity_animation.running = true;
} }
@ -1130,7 +1120,12 @@ bool client_apply_focus_opacity(Client *c) {
if (target_opacity > opacity) { if (target_opacity > opacity) {
target_opacity = opacity; target_opacity = opacity;
} }
memcpy(c->opacity_animation.current_border_color,
c->opacity_animation.target_border_color,
sizeof(c->opacity_animation.current_border_color));
c->opacity_animation.current_opacity = target_opacity;
client_set_opacity(c, target_opacity); client_set_opacity(c, target_opacity);
client_set_border_color(c, c->opacity_animation.target_border_color);
} else if (animations && c->opacity_animation.running) { } else if (animations && c->opacity_animation.running) {
struct timespec now; struct timespec now;
@ -1161,7 +1156,7 @@ bool client_apply_focus_opacity(Client *c) {
eased_progress; eased_progress;
} }
client_set_border_color(c, c->opacity_animation.current_border_color); client_set_border_color(c, c->opacity_animation.current_border_color);
if (linear_progress == 1.0f) { if (linear_progress >= 1.0f) {
c->opacity_animation.running = false; c->opacity_animation.running = false;
} else { } else {
return true; return true;

View file

@ -243,31 +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, while is very unluckely that
* it is stopped, in order to do not skip frames assume that it is. */
if (errno == ECHILD)
return 1;
} else if (in.si_pid) {
if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED)
return 1;
if (in.si_code == CLD_CONTINUED)
return 0;
}
return 0;
}
static inline int32_t client_is_unmanaged(Client *c) { static inline int32_t client_is_unmanaged(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
@ -319,9 +294,24 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
uint32_t height) { uint32_t height) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { 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, wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw,
c->geom.y + c->bw, width, height); c->geom.y + c->bw, width, height);
return 0; return 1;
} }
#endif #endif
if ((int32_t)width == c->surface.xdg->toplevel->current.width && 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) { 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;
}

View file

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

File diff suppressed because it is too large Load diff

View file

@ -47,8 +47,9 @@ int32_t log_level = WLR_ERROR;
uint32_t numlockon = 0; // 是否打开右边小键盘 uint32_t numlockon = 0; // 是否打开右边小键盘
uint32_t capslock = 0; // 是否启用快捷键 uint32_t capslock = 0; // 是否启用快捷键
uint32_t ov_tab_mode = 0; // alt tab切换模式 uint32_t ov_tab_mode = 0; // alt tab切换模式
uint32_t hotarea_size = 10; // 热区大小,10x10 uint32_t hotarea_size = 10; // 热区大小,10x10
uint32_t hotarea_corner = BOTTOM_LEFT;
uint32_t enable_hotarea = 1; // 是否启用鼠标热区 uint32_t enable_hotarea = 1; // 是否启用鼠标热区
int32_t smartgaps = 0; /* 1 means no outer gap when there is only one window */ int32_t smartgaps = 0; /* 1 means no outer gap when there is only one window */
int32_t sloppyfocus = 1; /* focus follows mouse */ int32_t sloppyfocus = 1; /* focus follows mouse */
@ -62,9 +63,10 @@ float scratchpad_height_ratio = 0.9;
int32_t scroller_structs = 20; int32_t scroller_structs = 20;
float scroller_default_proportion = 0.9; float scroller_default_proportion = 0.9;
float scroller_default_proportion_single = 1.0; float scroller_default_proportion_single = 1.0;
int32_t scroller_ignore_proportion_single = 0; 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;
@ -101,12 +103,14 @@ int32_t overviewgappo = 30; /* overview时 窗口与窗口 缝隙大小 */
* behavior */ * behavior */
float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0};
int32_t warpcursor = 1; /* Warp cursor to focused client */ int32_t warpcursor = 1;
int32_t drag_corner = 3;
int32_t drag_warp_cursor = 1;
int32_t xwayland_persistence = 1; /* xwayland persistence */ int32_t xwayland_persistence = 1; /* xwayland persistence */
int32_t syncobj_enable = 0; int32_t syncobj_enable = 0;
int32_t adaptive_sync = 0;
int32_t allow_lock_transparent = 0; int32_t allow_lock_transparent = 0;
double drag_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_tearing = TEARING_DISABLED;
int32_t allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; int32_t allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;
@ -233,4 +237,3 @@ double shadows_blur = 15;
int32_t shadows_position_x = 0; int32_t shadows_position_x = 0;
int32_t shadows_position_y = 0; int32_t shadows_position_y = 0;
float shadowscolor[] = COLOR(0x000000ff); float shadowscolor[] = COLOR(0x000000ff);
;

View file

@ -69,3 +69,4 @@ int32_t setoption(const Arg *arg);
int32_t disable_monitor(const Arg *arg); int32_t disable_monitor(const Arg *arg);
int32_t enable_monitor(const Arg *arg); int32_t enable_monitor(const Arg *arg);
int32_t toggle_monitor(const Arg *arg); int32_t toggle_monitor(const Arg *arg);
int32_t scroller_stack(const Arg *arg);

View file

@ -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;
@ -107,11 +110,16 @@ int32_t exchange_client(const Arg *arg) {
if ((c->isfullscreen || c->ismaximizescreen) && !is_scroller_layout(c->mon)) if ((c->isfullscreen || c->ismaximizescreen) && !is_scroller_layout(c->mon))
return 0; 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; return 0;
} }
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)
@ -129,6 +137,7 @@ int32_t exchange_stack_client(const Arg *arg) {
int32_t focusdir(const Arg *arg) { int32_t focusdir(const Arg *arg) {
Client *c = NULL; Client *c = NULL;
c = direction_select(arg); c = direction_select(arg);
c = get_focused_stack_client(c);
if (c) { if (c) {
focusclient(c, 1); focusclient(c, 1);
if (warpcursor) if (warpcursor)
@ -203,7 +212,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;
} }
@ -264,42 +273,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;
@ -329,6 +352,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);
@ -337,6 +362,9 @@ int32_t killclient(const Arg *arg) {
} }
int32_t moveresize(const Arg *arg) { int32_t moveresize(const Arg *arg) {
const char *cursors[] = {"nw-resize", "ne-resize", "sw-resize",
"se-resize"};
if (cursor_mode != CurNormal && cursor_mode != CurPressed) if (cursor_mode != CurNormal && cursor_mode != CurPressed)
return 0; return 0;
xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL);
@ -362,10 +390,29 @@ int32_t moveresize(const Arg *arg) {
/* Doesn't work for X11 output - the next absolute motion event /* Doesn't work for X11 output - the next absolute motion event
* returns the cursor to where it started */ * returns the cursor to where it started */
if (grabc->isfloating) { if (grabc->isfloating) {
wlr_cursor_warp_closest(cursor, NULL, rzcorner = drag_corner;
grabc->geom.x + grabc->geom.width, grabcx = (int)round(cursor->x);
grabc->geom.y + grabc->geom.height); grabcy = (int)round(cursor->y);
wlr_cursor_set_xcursor(cursor, cursor_mgr, "bottom_right_corner"); if (rzcorner == 4)
/* identify the closest corner index */
rzcorner = (grabcx - grabc->geom.x <
grabc->geom.x + grabc->geom.width - grabcx
? 0
: 1) +
(grabcy - grabc->geom.y <
grabc->geom.y + grabc->geom.height - grabcy
? 0
: 2);
if (drag_warp_cursor) {
grabcx = rzcorner & 1 ? grabc->geom.x + grabc->geom.width
: grabc->geom.x;
grabcy = rzcorner & 2 ? grabc->geom.y + grabc->geom.height
: grabc->geom.y;
wlr_cursor_warp_closest(cursor, NULL, grabcx, grabcy);
}
wlr_cursor_set_xcursor(cursor, cursor_mgr, cursors[rzcorner]);
} else { } else {
wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab"); wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab");
} }
@ -376,6 +423,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;
@ -419,12 +468,18 @@ 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;
if (!c || c->isfullscreen || c->ismaximizescreen) if (!c || c->isfullscreen || c->ismaximizescreen)
return 0; return 0;
int32_t animations_state_backup = animations;
if (!c->isfloating)
animations = 0;
if (ISTILED(c)) { if (ISTILED(c)) {
switch (arg->ui) { switch (arg->ui) {
case NUM_TYPE_MINUS: case NUM_TYPE_MINUS:
@ -450,6 +505,7 @@ int32_t resizewin(const Arg *arg) {
break; break;
} }
resize_tile_client(c, false, offsetx, offsety, 0); resize_tile_client(c, false, offsetx, offsety, 0);
animations = animations_state_backup;
return 0; return 0;
} }
@ -480,6 +536,7 @@ int32_t resizewin(const Arg *arg) {
c->iscustomsize = 1; c->iscustomsize = 1;
c->float_geom = c->geom; c->float_geom = c->geom;
resize(c, c->geom, 0); resize(c, c->geom, 0);
animations = animations_state_backup;
return 0; return 0;
} }
@ -517,6 +574,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) {
@ -542,6 +601,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;
@ -550,12 +611,14 @@ int32_t set_proportion(const Arg *arg) {
!scroller_ignore_proportion_single) !scroller_ignore_proportion_single)
return 0; return 0;
if (selmon->sel) { Client *tc = selmon->sel;
if (tc) {
tc = get_scroll_stack_head(tc);
uint32_t max_client_width = uint32_t max_client_width =
selmon->w.width - 2 * scroller_structs - gappih; selmon->w.width - 2 * scroller_structs - gappih;
selmon->sel->scroller_proportion = arg->f; tc->scroller_proportion = arg->f;
selmon->sel->geom.width = max_client_width * arg->f; tc->geom.width = max_client_width * arg->f;
// resize(selmon->sel, selmon->sel->geom, 0);
arrange(selmon, false, false); arrange(selmon, false, false);
} }
return 0; return 0;
@ -565,6 +628,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;
@ -666,6 +731,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;
@ -734,6 +801,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)
@ -749,10 +818,13 @@ int32_t centerwin(const Arg *arg) {
if (!is_scroller_layout(selmon)) if (!is_scroller_layout(selmon))
return 0; return 0;
Client *stack_head = get_scroll_stack_head(c);
if (selmon->pertag->ltidxs[selmon->pertag->curtag]->id == SCROLLER) { if (selmon->pertag->ltidxs[selmon->pertag->curtag]->id == SCROLLER) {
c->geom.x = selmon->w.x + (selmon->w.width - c->geom.width) / 2; stack_head->geom.x =
selmon->w.x + (selmon->w.width - stack_head->geom.width) / 2;
} else { } else {
c->geom.y = selmon->w.y + (selmon->w.height - c->geom.height) / 2; stack_head->geom.y =
selmon->w.y + (selmon->w.height - stack_head->geom.height) / 2;
} }
arrange(selmon, false, false); arrange(selmon, false, false);
@ -778,7 +850,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);
@ -819,7 +891,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 避免缓冲区刷新等操作
} }
@ -866,16 +938,20 @@ 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;
uint32_t latched = keyboard->modifiers.latched; uint32_t latched = keyboard->modifiers.latched;
uint32_t locked = keyboard->modifiers.locked; uint32_t locked = keyboard->modifiers.locked;
wlr_keyboard_set_keymap(keyboard, keyboard->keymap);
wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, next); wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, next);
keyboard->modifiers.group = 0;
// 7. 更新 seat // 7. 更新 seat
wlr_seat_set_keyboard(seat, keyboard); wlr_seat_set_keyboard(seat, keyboard);
@ -889,10 +965,7 @@ int32_t switch_keyboard_layout(const Arg *arg) {
struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data; struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data;
wlr_keyboard_set_keymap(tkb, keyboard->keymap);
wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, next); wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, next);
tkb->modifiers.group = 0;
// 7. 更新 seat // 7. 更新 seat
wlr_seat_set_keyboard(seat, tkb); wlr_seat_set_keyboard(seat, tkb);
wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers); wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers);
@ -908,6 +981,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++) {
@ -959,6 +1035,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;
@ -971,18 +1049,35 @@ int32_t switch_proportion_preset(const Arg *arg) {
!scroller_ignore_proportion_single) !scroller_ignore_proportion_single)
return 0; return 0;
if (selmon->sel) { Client *tc = selmon->sel;
if (tc) {
tc = get_scroll_stack_head(tc);
for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) { for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) {
if (config.scroller_proportion_preset[i] == if (config.scroller_proportion_preset[i] ==
selmon->sel->scroller_proportion) { tc->scroller_proportion) {
if (i == config.scroller_proportion_preset_count - 1) {
target_proportion = config.scroller_proportion_preset[0]; if (arg->i == NEXT) {
break; 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 { } else {
target_proportion = if (i == 0) {
config.scroller_proportion_preset[i + 1]; target_proportion =
break; config.scroller_proportion_preset
[config.scroller_proportion_preset_count - 1];
break;
} else {
target_proportion =
config.scroller_proportion_preset[i - 1];
break;
}
} }
} }
} }
@ -993,15 +1088,16 @@ int32_t switch_proportion_preset(const Arg *arg) {
uint32_t max_client_width = uint32_t max_client_width =
selmon->w.width - 2 * scroller_structs - gappih; selmon->w.width - 2 * scroller_structs - gappih;
selmon->sel->scroller_proportion = target_proportion; tc->scroller_proportion = target_proportion;
selmon->sel->geom.width = max_client_width * target_proportion; tc->geom.width = max_client_width * target_proportion;
// resize(selmon->sel, selmon->sel->geom, 0);
arrange(selmon, false, false); arrange(selmon, false, false);
} }
return 0; return 0;
} }
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;
@ -1009,6 +1105,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)
@ -1021,7 +1119,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;
} }
@ -1093,12 +1191,16 @@ int32_t tagsilent(const Arg *arg) {
clear_fullscreen_flag(fc); clear_fullscreen_flag(fc);
} }
} }
exit_scroller_stack(target_client);
focusclient(focustop(selmon), 1); focusclient(focustop(selmon), 1);
arrange(target_client->mon, false, false); arrange(target_client->mon, false, false);
return 0; return 0;
} }
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) {
@ -1108,6 +1210,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)) {
@ -1136,6 +1241,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;
@ -1171,6 +1278,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);
@ -1178,6 +1287,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)
@ -1186,17 +1298,22 @@ int32_t togglefloating(const Arg *arg) {
if (!sel) if (!sel)
return 0; return 0;
bool isfloating = sel->isfloating;
if ((sel->isfullscreen || sel->ismaximizescreen)) { if ((sel->isfullscreen || sel->ismaximizescreen)) {
sel->isfloating = 1; isfloating = 1;
} else { } else {
sel->isfloating = !sel->isfloating; isfloating = !sel->isfloating;
} }
setfloating(sel, sel->isfloating); setfloating(sel, isfloating);
return 0; return 0;
} }
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;
@ -1213,6 +1330,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) {
@ -1221,20 +1341,28 @@ int32_t toggleglobal(const Arg *arg) {
selmon->sel->isnamedscratchpad = 0; selmon->sel->isnamedscratchpad = 0;
} }
selmon->sel->isglobal ^= 1; selmon->sel->isglobal ^= 1;
// selmon->sel->tags = if (selmon->sel->isglobal &&
// selmon->sel->isglobal ? TAGMASK : selmon->tagset[selmon->seltags]; (selmon->sel->prev_in_stack || selmon->sel->next_in_stack)) {
// focustop(selmon); exit_scroller_stack(selmon->sel);
arrange(selmon, false, false);
}
setborder_color(selmon->sel); setborder_color(selmon->sel);
return 0; return 0;
} }
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;
@ -1253,6 +1381,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;
} }
@ -1274,6 +1405,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)
@ -1297,13 +1431,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;
@ -1315,6 +1451,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) {
@ -1335,6 +1474,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;
} }
@ -1352,6 +1494,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;
@ -1376,6 +1521,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;
@ -1400,15 +1548,23 @@ 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;
} }
int32_t tagcrossmon(const Arg *arg) { int32_t tagcrossmon(const Arg *arg) {
if (!selmon->sel) if (!selmon || !selmon->sel)
return 0; return 0;
if (match_monitor_spec(arg->v, selmon)) {
tag_client(arg, selmon->sel);
return 0;
}
tagmon(&(Arg){.ui = arg->ui, .i = UNDIR, .v = arg->v}); tagmon(&(Arg){.ui = arg->ui, .i = UNDIR, .v = arg->v});
return 0; return 0;
} }
@ -1473,6 +1629,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;
@ -1483,8 +1641,20 @@ int32_t minimized(const Arg *arg) {
return 0; 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) { 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});
@ -1504,6 +1674,8 @@ int32_t toggleoverview(const Arg *arg) {
visible_client_number++; visible_client_number++;
} }
if (visible_client_number > 0) { 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; target = ~0 & TAGMASK;
} else { } else {
selmon->isoverview ^= 1; selmon->isoverview ^= 1;
@ -1514,6 +1686,7 @@ int32_t toggleoverview(const Arg *arg) {
} else if (!selmon->isoverview && !selmon->sel) { } else if (!selmon->isoverview && !selmon->sel) {
target = (1 << (selmon->pertag->prevtag - 1)); target = (1 << (selmon->pertag->prevtag - 1));
view(&(Arg){.ui = target}, false); view(&(Arg){.ui = target}, false);
fix_mon_tagset_from_overview(selmon);
refresh_monitors_workspaces_status(selmon); refresh_monitors_workspaces_status(selmon);
return 0; return 0;
} }
@ -1536,7 +1709,7 @@ int32_t toggleoverview(const Arg *arg) {
} }
view(&(Arg){.ui = target}, false); view(&(Arg){.ui = target}, false);
fix_mon_tagset_from_overview(selmon);
refresh_monitors_workspaces_status(selmon); refresh_monitors_workspaces_status(selmon);
return 0; return 0;
} }
@ -1545,7 +1718,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;
@ -1560,7 +1733,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;
@ -1575,7 +1748,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;
@ -1585,3 +1758,108 @@ int32_t toggle_monitor(const Arg *arg) {
} }
return 0; return 0;
} }
int32_t scroller_stack(const Arg *arg) {
if (!selmon)
return 0;
Client *c = selmon->sel;
Client *stack_head = NULL;
Client *source_stack_head = NULL;
if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon))
return 0;
if (c && (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal))
return 0;
bool is_horizontal_layout =
c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true
: false;
Client *target_client = find_client_by_direction(c, arg, false, true);
if (target_client && (!client_only_in_one_tag(target_client) ||
target_client->isglobal || target_client->isunglobal))
return 0;
if (target_client) {
stack_head = get_scroll_stack_head(target_client);
}
if (c) {
source_stack_head = get_scroll_stack_head(c);
}
if (stack_head == source_stack_head) {
return 0;
}
if (c->isfullscreen) {
setfullscreen(c, 0);
}
if (c->ismaximizescreen) {
setmaximizescreen(c, 0);
}
if (c->prev_in_stack) {
if ((is_horizontal_layout && arg->i == LEFT) ||
(!is_horizontal_layout && arg->i == UP)) {
exit_scroller_stack(c);
wl_list_remove(&c->link);
wl_list_insert(source_stack_head->link.prev, &c->link);
arrange(selmon, false, false);
} else if ((is_horizontal_layout && arg->i == RIGHT) ||
(!is_horizontal_layout && arg->i == DOWN)) {
exit_scroller_stack(c);
wl_list_remove(&c->link);
wl_list_insert(&source_stack_head->link, &c->link);
arrange(selmon, false, false);
}
return 0;
} else if (c->next_in_stack) {
Client *next_in_stack = c->next_in_stack;
if ((is_horizontal_layout && arg->i == LEFT) ||
(!is_horizontal_layout && arg->i == UP)) {
exit_scroller_stack(c);
wl_list_remove(&c->link);
wl_list_insert(next_in_stack->link.prev, &c->link);
arrange(selmon, false, false);
} else if ((is_horizontal_layout && arg->i == RIGHT) ||
(!is_horizontal_layout && arg->i == DOWN)) {
exit_scroller_stack(c);
wl_list_remove(&c->link);
wl_list_insert(&next_in_stack->link, &c->link);
arrange(selmon, false, false);
}
return 0;
}
if (!target_client || target_client->mon != c->mon) {
return 0;
}
exit_scroller_stack(c);
// Find the tail of target_client's stack
Client *stack_tail = target_client;
while (stack_tail->next_in_stack) {
stack_tail = stack_tail->next_in_stack;
}
// Add c to the stack
stack_tail->next_in_stack = c;
c->prev_in_stack = stack_tail;
c->next_in_stack = NULL;
if (stack_head->ismaximizescreen) {
setmaximizescreen(stack_head, 0);
}
if (stack_head->isfullscreen) {
setfullscreen(stack_head, 0);
}
arrange(selmon, false, false);
return 0;
}

View file

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

View file

@ -12,7 +12,9 @@ bool check_hit_no_border(Client *c) {
} }
} }
if (no_border_when_single && c && c->mon && c->mon->visible_clients == 1) { if (no_border_when_single && c && c->mon &&
((ISSCROLLTILED(c) && c->mon->visible_scroll_tiling_clients == 1) ||
c->mon->visible_clients == 1)) {
hit_no_border = true; hit_no_border = true;
} }
return hit_no_border; return hit_no_border;
@ -81,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) {
@ -215,6 +220,27 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
} }
} }
} }
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) { if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y) { if (tempClients[_i]->geom.y < sel_y) {
@ -252,6 +278,27 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
} }
} }
} }
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) { if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y) { if (tempClients[_i]->geom.y > sel_y) {
@ -289,6 +336,27 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
} }
} }
} }
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) { if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x) { if (tempClients[_i]->geom.x < sel_x) {
@ -326,6 +394,27 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
} }
} }
} }
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) { if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x) { if (tempClients[_i]->geom.x > sel_x) {
@ -369,7 +458,9 @@ Client *direction_select(const Arg *arg) {
} }
return find_client_by_direction( return find_client_by_direction(
tc, arg, true, is_scroller_layout(selmon) && !selmon->isoverview); tc, arg, true,
(is_scroller_layout(selmon) || is_centertile_layout(selmon)) &&
!selmon->isoverview);
} }
/* We probably should change the name of this, it sounds like /* We probably should change the name of this, it sounds like
@ -448,3 +539,96 @@ bool client_only_in_one_tag(Client *c) {
return false; return false;
} }
} }
Client *get_scroll_stack_head(Client *c) {
Client *scroller_stack_head = c;
if (!scroller_stack_head)
return NULL;
while (scroller_stack_head->prev_in_stack) {
scroller_stack_head = scroller_stack_head->prev_in_stack;
}
return scroller_stack_head;
}
bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) {
if (!sc || !tc)
return false;
uint32_t id = sc->mon->pertag->ltidxs[sc->mon->pertag->curtag]->id;
if (id != SCROLLER && id != VERTICAL_SCROLLER && id != TILE &&
id != VERTICAL_TILE && id != DECK && id != VERTICAL_DECK &&
id != CENTER_TILE && id != RIGHT_TILE && id != TGMIX)
return false;
if (id == SCROLLER || id == VERTICAL_SCROLLER) {
Client *source_stack_head = get_scroll_stack_head(sc);
Client *target_stack_head = get_scroll_stack_head(tc);
Client *fc_head = fc ? get_scroll_stack_head(fc) : NULL;
if (fc && fc->prev_in_stack && fc_head == source_stack_head)
return false;
if (source_stack_head == target_stack_head)
return true;
else
return false;
}
if (id == TILE || id == VERTICAL_TILE || id == DECK ||
id == VERTICAL_DECK || id == RIGHT_TILE) {
if (tc->ismaster ^ sc->ismaster)
return false;
if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
else
return true;
}
if (id == TGMIX) {
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 (tc->ismaster ^ sc->ismaster)
return false;
if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
if (sc->geom.x == tc->geom.x)
return true;
else
return false;
}
return false;
}
Client *get_focused_stack_client(Client *sc) {
if (!sc || sc->isfloating)
return sc;
Client *tc = NULL;
Client *fc = focustop(sc->mon);
if (fc->isfloating || sc->isfloating)
return sc;
wl_list_for_each(tc, &fstack, flink) {
if (tc->iskilling || tc->isunglobal)
continue;
if (!VISIBLEON(tc, sc->mon))
continue;
if (tc == fc)
continue;
if (client_is_in_same_stack(sc, tc, fc)) {
return tc;
}
}
return sc;
}

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

View file

@ -26,6 +26,14 @@ bool is_scroller_layout(Monitor *m) {
return false; return false;
} }
bool is_centertile_layout(Monitor *m) {
if (m->pertag->ltidxs[m->pertag->curtag]->id == CENTER_TILE)
return true;
return false;
}
uint32_t get_tag_status(uint32_t tag, Monitor *m) { uint32_t get_tag_status(uint32_t tag, Monitor *m) {
Client *c = NULL; Client *c = NULL;
uint32_t status = 0; uint32_t status = 0;
@ -88,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;
}

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) { void set_size_per(Monitor *m, Client *c) {
Client *fc = NULL; Client *fc = NULL;
bool found = false; bool found = false;
@ -5,8 +84,13 @@ void set_size_per(Monitor *m, Client *c) {
if (!m || !c) if (!m || !c)
return; return;
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
wl_list_for_each(fc, &clients, link) { wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) { 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_mfact_per = fc->master_mfact_per;
c->master_inner_per = fc->master_inner_per; c->master_inner_per = fc->master_inner_per;
c->stack_inner_per = fc->stack_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; break;
} }
if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
ISTILED(tc)) { // 根据你的实际字段名调整
next = tc; next = tc;
begin_find_nextnext = true; begin_find_nextnext = true;
continue; continue;
@ -60,8 +143,7 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
break; break;
} }
if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
ISTILED(tc)) { // 根据你的实际字段名调整
prev = tc; prev = tc;
begin_find_prevprev = true; begin_find_prevprev = true;
continue; continue;
@ -197,6 +279,23 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
// 应用到所有平铺窗口 // 应用到所有平铺窗口
wl_list_for_each(tc, &clients, link) { wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) { 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; tc->master_mfact_per = new_master_mfact_per;
} }
} }
@ -210,7 +309,7 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
} }
if (last_apply_drap_time == 0 || if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_refresh_interval) { time - last_apply_drap_time > drag_tile_refresh_interval) {
arrange(grabc->mon, false, false); arrange(grabc->mon, false, false);
last_apply_drap_time = time; last_apply_drap_time = time;
} }
@ -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) { for (node = grabc->link.next; node != &clients; node = node->next) {
tc = wl_container_of(node, tc, link); tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) && if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
ISTILED(tc)) { // 根据你的实际字段名调整
next = tc; next = tc;
break; 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) { for (node = grabc->link.prev; node != &clients; node = node->prev) {
tc = wl_container_of(node, tc, link); tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) && if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
ISTILED(tc)) { // 根据你的实际字段名调整
prev = tc; prev = tc;
break; break;
} }
@ -354,6 +451,20 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
// 应用到所有平铺窗口 // 应用到所有平铺窗口
wl_list_for_each(tc, &clients, link) { wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) { 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; tc->master_mfact_per = new_master_mfact_per;
} }
} }
@ -367,7 +478,7 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
} }
if (last_apply_drap_time == 0 || if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_refresh_interval) { time - last_apply_drap_time > drag_tile_refresh_interval) {
arrange(grabc->mon, false, false); arrange(grabc->mon, false, false);
last_apply_drap_time = time; last_apply_drap_time = time;
} }
@ -376,8 +487,11 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
void resize_tile_scroller(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) { int32_t offsety, uint32_t time, bool isvertical) {
Client *tc = NULL;
float delta_x, delta_y; float delta_x, delta_y;
float new_scroller_proportion; float new_scroller_proportion;
float new_stack_proportion;
Client *stack_head = get_scroll_stack_head(grabc);
if (grabc && grabc->mon->visible_tiling_clients == 1 && if (grabc && grabc->mon->visible_tiling_clients == 1 &&
!scroller_ignore_proportion_single) !scroller_ignore_proportion_single)
@ -389,7 +503,8 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
start_drag_window = true; start_drag_window = true;
// 记录初始状态 // 记录初始状态
grabc->old_scroller_pproportion = grabc->scroller_proportion; stack_head->old_scroller_pproportion = stack_head->scroller_proportion;
grabc->old_stack_proportion = grabc->stack_proportion;
grabc->cursor_in_left_half = grabc->cursor_in_left_half =
cursor->x < grabc->geom.x + grabc->geom.width / 2; cursor->x < grabc->geom.x + grabc->geom.width / 2;
@ -409,15 +524,26 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
grabc->old_master_inner_per = grabc->master_inner_per; grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per; grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->drag_begin_geom = grabc->geom; grabc->drag_begin_geom = grabc->geom;
grabc->old_scroller_pproportion = grabc->scroller_proportion; stack_head->old_scroller_pproportion =
stack_head->scroller_proportion;
grabc->old_stack_proportion = grabc->stack_proportion;
grabc->cursor_in_upper_half = false; grabc->cursor_in_upper_half = false;
grabc->cursor_in_left_half = false; grabc->cursor_in_left_half = false;
} }
delta_x = (float)(offsetx) * (grabc->old_scroller_pproportion) / if (isvertical) {
grabc->drag_begin_geom.width; delta_y = (float)(offsety) *
delta_y = (float)(offsety) * (grabc->old_scroller_pproportion) / (stack_head->old_scroller_pproportion) /
grabc->drag_begin_geom.height; grabc->drag_begin_geom.height;
delta_x = (float)(offsetx) * (grabc->old_stack_proportion) /
grabc->drag_begin_geom.width;
} else {
delta_x = (float)(offsetx) *
(stack_head->old_scroller_pproportion) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_stack_proportion) /
grabc->drag_begin_geom.height;
}
bool moving_up; bool moving_up;
bool moving_down; bool moving_down;
@ -452,18 +578,91 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
delta_x = -fabsf(delta_x); delta_x = -fabsf(delta_x);
} }
if (isvertical) {
if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) {
delta_x = delta_x * -1.0f;
}
if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) {
if (moving_right) {
delta_x = -fabsf(delta_x);
} else {
delta_x = fabsf(delta_x);
}
}
if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) {
if (moving_left) {
delta_x = -fabsf(delta_x);
} else {
delta_x = fabsf(delta_x);
}
}
if (isdrag) {
if (moving_up) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
}
} else {
if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) {
delta_y = delta_y * -1.0f;
}
if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) {
if (moving_down) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
}
if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) {
if (moving_up) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
}
if (isdrag) {
if (moving_left) {
delta_x = -fabsf(delta_x);
} else {
delta_x = fabsf(delta_x);
}
}
}
// 直接设置新的比例,基于初始值 + 变化量 // 直接设置新的比例,基于初始值 + 变化量
if (isvertical) { if (isvertical) {
new_scroller_proportion = grabc->old_scroller_pproportion + delta_y; new_scroller_proportion =
stack_head->old_scroller_pproportion + delta_y;
new_stack_proportion = grabc->old_stack_proportion + delta_x;
} else { } else {
new_scroller_proportion = grabc->old_scroller_pproportion + delta_x; new_scroller_proportion =
stack_head->old_scroller_pproportion + delta_x;
new_stack_proportion = grabc->old_stack_proportion + delta_y;
} }
// 应用限制,确保比例在合理范围内 // 应用限制,确保比例在合理范围内
new_scroller_proportion = new_scroller_proportion =
fmaxf(0.1f, fminf(1.0f, new_scroller_proportion)); fmaxf(0.1f, fminf(1.0f, new_scroller_proportion));
new_stack_proportion = fmaxf(0.1f, fminf(0.9f, new_stack_proportion));
grabc->scroller_proportion = new_scroller_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) { if (!isdrag) {
arrange(grabc->mon, false, false); arrange(grabc->mon, false, false);
@ -471,7 +670,7 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
} }
if (last_apply_drap_time == 0 || if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_refresh_interval) { time - last_apply_drap_time > drag_tile_refresh_interval) {
arrange(grabc->mon, false, false); arrange(grabc->mon, false, false);
last_apply_drap_time = time; last_apply_drap_time = time;
} }
@ -507,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, void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
double total_left_stack_hight_percent, double total_left_stack_hight_percent,
double total_right_stack_hight_percent, double total_right_stack_hight_percent,
@ -522,6 +733,7 @@ void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) { if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) { if (total_master_inner_percent > 0.0 && i < nmasters) {
c->ismaster = true; c->ismaster = true;
c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f; c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f;
@ -537,17 +749,20 @@ void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
: 1.0f; : 1.0f;
} }
i++; i++;
check_size_per_valid(c);
} }
} }
} else { } else {
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) { if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) { if (total_master_inner_percent > 0.0 && i < nmasters) {
c->ismaster = true; c->ismaster = true;
if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) { if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) {
c->stack_inner_per = 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 { } else {
c->stack_inner_per = c->stack_inner_per =
stack_num > 1 ? 2.0f / stack_num : 1.0f; stack_num > 1 ? 2.0f / stack_num : 1.0f;
@ -576,13 +791,15 @@ void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
} }
} }
i++; i++;
check_size_per_valid(c);
} }
} }
} }
} }
void // 17 void pre_caculate_before_arrange(Monitor *m, bool want_animation,
arrange(Monitor *m, bool want_animation, bool from_view) { bool from_view, bool only_caculate) {
Client *c = NULL; Client *c = NULL;
double total_stack_inner_percent = 0; double total_stack_inner_percent = 0;
double total_master_inner_percent = 0; double total_master_inner_percent = 0;
@ -605,14 +822,20 @@ arrange(Monitor *m, bool want_animation, bool from_view) {
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) {
exit_scroller_stack(c);
}
if (from_view && (c->isglobal || c->isunglobal)) { if (from_view && (c->isglobal || c->isunglobal)) {
set_size_per(m, c); set_size_per(m, c);
} }
if (c->mon == m && (c->isglobal || c->isunglobal)) { if (c->mon == m && (c->isglobal || c->isunglobal)) {
c->tags = m->tagset[m->seltags]; c->tags = m->tagset[m->seltags];
if (c->mon->sel == NULL) }
focusclient(c, 0);
if (from_view && m->sel == NULL && c->isglobal && VISIBLEON(c, m)) {
focusclient(c, 1);
} }
if (VISIBLEON(c, m)) { if (VISIBLEON(c, m)) {
@ -627,7 +850,7 @@ arrange(Monitor *m, bool want_animation, bool from_view) {
m->visible_tiling_clients++; m->visible_tiling_clients++;
} }
if (ISSCROLLTILED(c)) { if (ISSCROLLTILED(c) && !c->prev_in_stack) {
m->visible_scroll_tiling_clients++; m->visible_scroll_tiling_clients++;
} }
} }
@ -665,14 +888,17 @@ arrange(Monitor *m, bool want_animation, bool from_view) {
i++; i++;
} }
set_arrange_visible(m, c, want_animation); if (!only_caculate)
set_arrange_visible(m, c, want_animation);
} else { } 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 && if (!only_caculate && c->mon == m && c->ismaximizescreen &&
!c->animation.tagouting && VISIBLEON(c, m)) { !c->animation.tagouted && !c->animation.tagouting &&
VISIBLEON(c, m)) {
reset_maximizescreen_size(c); reset_maximizescreen_size(c);
} }
} }
@ -681,6 +907,12 @@ arrange(Monitor *m, bool want_animation, bool from_view) {
m, m->visible_tiling_clients, total_left_stack_hight_percent, m, m->visible_tiling_clients, total_left_stack_hight_percent,
total_right_stack_hight_percent, total_stack_inner_percent, total_right_stack_hight_percent, total_stack_inner_percent,
total_master_inner_percent, master_num, stack_num); 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) { if (m->isoverview) {
overviewlayout.arrange(m); overviewlayout.arrange(m);

View file

@ -117,6 +117,7 @@ void deck(Monitor *m) {
Client *c = NULL; Client *c = NULL;
Client *fc = NULL; Client *fc = NULL;
float mfact; float mfact;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
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;
@ -142,8 +143,8 @@ void deck(Monitor *m) {
: m->pertag->mfacts[m->pertag->curtag]; : m->pertag->mfacts[m->pertag->curtag];
// Calculate master width including outer gaps // Calculate master width including outer gaps
if (n > m->nmaster) if (n > nmasters)
mw = m->nmaster ? round((m->w.width - 2 * cur_gappoh) * mfact) : 0; mw = nmasters ? round((m->w.width - 2 * cur_gappoh) * mfact) : 0;
else else
mw = m->w.width - 2 * cur_gappoh; mw = m->w.width - 2 * cur_gappoh;
@ -151,7 +152,7 @@ void deck(Monitor *m) {
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) if (!VISIBLEON(c, m) || !ISTILED(c))
continue; continue;
if (i < m->nmaster) { if (i < nmasters) {
c->master_mfact_per = mfact; c->master_mfact_per = mfact;
// Master area clients // Master area clients
resize( resize(
@ -160,7 +161,7 @@ void deck(Monitor *m) {
.y = m->w.y + cur_gappov + my, .y = m->w.y + cur_gappov + my,
.width = mw, .width = mw,
.height = (m->w.height - 2 * cur_gappov - my) / .height = (m->w.height - 2 * cur_gappov - my) /
(MIN(n, m->nmaster) - i)}, (MIN(n, nmasters) - i)},
0); 0);
my += c->geom.height; my += c->geom.height;
} else { } else {
@ -212,6 +213,66 @@ void horizontal_scroll_adjust_fullandmax(Client *c,
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;
} }
void arrange_stack(Client *scroller_stack_head, struct wlr_box geometry,
int32_t gappiv) {
int32_t stack_size = 0;
Client *iter = scroller_stack_head;
while (iter) {
stack_size++;
iter = iter->next_in_stack;
}
if (stack_size == 0)
return;
float total_proportion = 0.0f;
iter = scroller_stack_head;
while (iter) {
if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) {
iter->stack_proportion =
stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1);
}
total_proportion += iter->stack_proportion;
iter = iter->next_in_stack;
}
iter = scroller_stack_head;
while (iter) {
iter->stack_proportion = iter->stack_proportion / total_proportion;
iter = iter->next_in_stack;
}
int32_t client_height;
int32_t current_y = geometry.y;
int32_t remain_client_height = geometry.height - (stack_size - 1) * gappiv;
float remain_proportion = 1.0f;
iter = scroller_stack_head;
while (iter) {
client_height =
remain_client_height * (iter->stack_proportion / remain_proportion);
struct wlr_box client_geom = {.x = geometry.x,
.y = current_y,
.width = geometry.width,
.height = client_height};
resize(iter, client_geom, 0);
remain_proportion -= iter->stack_proportion;
remain_client_height -= client_height;
current_y += client_height + gappiv;
iter = iter->next_in_stack;
}
}
void horizontal_check_scroller_root_inside_mon(Client *c,
struct wlr_box *geometry) {
if (!GEOMINSIDEMON(geometry, c->mon)) {
geometry->x = c->mon->w.x + (c->mon->w.width - geometry->width) / 2;
}
}
// 滚动布局 // 滚动布局
void scroller(Monitor *m) { void scroller(Monitor *m) {
int32_t i, n, j; int32_t i, n, j;
@ -222,9 +283,11 @@ 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;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
cur_gappih = cur_gappih =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappih; smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappih;
@ -251,7 +314,7 @@ void scroller(Monitor *m) {
// 第二次遍历,填充 tempClients // 第二次遍历,填充 tempClients
j = 0; j = 0;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) {
tempClients[j] = c; tempClients[j] = c;
j++; j++;
} }
@ -269,7 +332,8 @@ void scroller(Monitor *m) {
target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion; target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion;
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;
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;
resize(c, target_geom, 0); horizontal_check_scroller_root_inside_mon(c, &target_geom);
arrange_stack(c, target_geom, cur_gappiv);
free(tempClients); // 释放内存 free(tempClients); // 释放内存
return; return;
} }
@ -283,6 +347,11 @@ void scroller(Monitor *m) {
root_client = center_tiled_select(m); root_client = center_tiled_select(m);
} }
// root_client might be in a stack, find the stack head
if (root_client) {
root_client = get_scroll_stack_head(root_client);
}
if (!root_client) { if (!root_client) {
free(tempClients); // 释放内存 free(tempClients); // 释放内存
return; return;
@ -303,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;
} }
@ -317,31 +426,47 @@ void scroller(Monitor *m) {
&target_geom); &target_geom);
if (tempClients[focus_client_index]->isfullscreen) { if (tempClients[focus_client_index]->isfullscreen) {
target_geom.x = m->m.x; target_geom.x = m->m.x;
resize(tempClients[focus_client_index], target_geom, 0); horizontal_check_scroller_root_inside_mon(
tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} else if (tempClients[focus_client_index]->ismaximizescreen) { } else if (tempClients[focus_client_index]->ismaximizescreen) {
target_geom.x = m->w.x + cur_gappoh; target_geom.x = m->w.x + cur_gappoh;
resize(tempClients[focus_client_index], target_geom, 0); horizontal_check_scroller_root_inside_mon(
tempClients[focus_client_index], &target_geom);
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;
} }
resize(tempClients[focus_client_index], target_geom, 0); horizontal_check_scroller_root_inside_mon(
tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} else { } else {
target_geom.x = c->geom.x; target_geom.x = c->geom.x;
resize(tempClients[focus_client_index], target_geom, 0); horizontal_check_scroller_root_inside_mon(
tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} }
for (i = 1; i <= focus_client_index; i++) { for (i = 1; i <= focus_client_index; i++) {
@ -351,7 +476,7 @@ void scroller(Monitor *m) {
target_geom.x = tempClients[focus_client_index - i + 1]->geom.x - target_geom.x = tempClients[focus_client_index - i + 1]->geom.x -
cur_gappih - target_geom.width; cur_gappih - target_geom.width;
resize(c, target_geom, 0); arrange_stack(c, target_geom, cur_gappiv);
} }
for (i = 1; i < n - focus_client_index; i++) { for (i = 1; i < n - focus_client_index; i++) {
@ -361,7 +486,7 @@ void scroller(Monitor *m) {
target_geom.x = tempClients[focus_client_index + i - 1]->geom.x + target_geom.x = tempClients[focus_client_index + i - 1]->geom.x +
cur_gappih + cur_gappih +
tempClients[focus_client_index + i - 1]->geom.width; tempClients[focus_client_index + i - 1]->geom.width;
resize(c, target_geom, 0); arrange_stack(c, target_geom, cur_gappiv);
} }
free(tempClients); // 最后释放内存 free(tempClients); // 最后释放内存

View file

@ -17,8 +17,8 @@ static void tgmix(Monitor *m);
Layout overviewlayout = {"󰃇", overview, "overview"}; Layout overviewlayout = {"󰃇", overview, "overview"};
enum { enum {
SCROLLER,
TILE, TILE,
SCROLLER,
GRID, GRID,
MONOCLE, MONOCLE,
DECK, DECK,
@ -34,8 +34,8 @@ enum {
Layout layouts[] = { Layout layouts[] = {
// 最少两个,不能删除少于两个 // 最少两个,不能删除少于两个
/* symbol arrange function name */ /* symbol arrange function name */
{"S", scroller, "scroller", SCROLLER}, // 滚动布局
{"T", tile, "tile", TILE}, // 平铺布局 {"T", tile, "tile", TILE}, // 平铺布局
{"S", scroller, "scroller", SCROLLER}, // 滚动布局
{"G", grid, "grid", GRID}, // 格子布局 {"G", grid, "grid", GRID}, // 格子布局
{"M", monocle, "monocle", MONOCLE}, // 单屏布局 {"M", monocle, "monocle", MONOCLE}, // 单屏布局
{"K", deck, "deck", DECK}, // 卡片布局 {"K", deck, "deck", DECK}, // 卡片布局

View file

@ -110,6 +110,7 @@ void vertical_deck(Monitor *m) {
Client *c = NULL; Client *c = NULL;
Client *fc = NULL; Client *fc = NULL;
float mfact; float mfact;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
int32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
@ -134,8 +135,8 @@ void vertical_deck(Monitor *m) {
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag]; : m->pertag->mfacts[m->pertag->curtag];
if (n > m->nmaster) if (n > nmasters)
mh = m->nmaster ? round((m->w.height - 2 * cur_gappov) * mfact) : 0; mh = nmasters ? round((m->w.height - 2 * cur_gappov) * mfact) : 0;
else else
mh = m->w.height - 2 * cur_gappov; mh = m->w.height - 2 * cur_gappov;
@ -143,13 +144,13 @@ void vertical_deck(Monitor *m) {
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) if (!VISIBLEON(c, m) || !ISTILED(c))
continue; continue;
if (i < m->nmaster) { if (i < nmasters) {
resize( resize(
c, c,
(struct wlr_box){.x = m->w.x + cur_gappoh + mx, (struct wlr_box){.x = m->w.x + cur_gappoh + mx,
.y = m->w.y + cur_gappov, .y = m->w.y + cur_gappov,
.width = (m->w.width - 2 * cur_gappoh - mx) / .width = (m->w.width - 2 * cur_gappoh - mx) /
(MIN(n, m->nmaster) - i), (MIN(n, nmasters) - i),
.height = mh}, .height = mh},
0); 0);
mx += c->geom.width; mx += c->geom.width;
@ -199,6 +200,66 @@ void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) {
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;
} }
void arrange_stack_vertical(Client *scroller_stack_head,
struct wlr_box geometry, int32_t gappih) {
int32_t stack_size = 0;
Client *iter = scroller_stack_head;
while (iter) {
stack_size++;
iter = iter->next_in_stack;
}
if (stack_size == 0)
return;
float total_proportion = 0.0f;
iter = scroller_stack_head;
while (iter) {
if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) {
iter->stack_proportion =
stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1);
}
total_proportion += iter->stack_proportion;
iter = iter->next_in_stack;
}
iter = scroller_stack_head;
while (iter) {
iter->stack_proportion = iter->stack_proportion / total_proportion;
iter = iter->next_in_stack;
}
int32_t client_width;
int32_t current_x = geometry.x;
int32_t remain_client_width = geometry.width - (stack_size - 1) * gappih;
float remain_proportion = 1.0f;
iter = scroller_stack_head;
while (iter) {
client_width =
remain_client_width * (iter->stack_proportion / remain_proportion);
struct wlr_box client_geom = {.y = geometry.y,
.x = current_x,
.height = geometry.height,
.width = client_width};
resize(iter, client_geom, 0);
remain_proportion -= iter->stack_proportion;
remain_client_width -= client_width;
current_x += client_width + gappih;
iter = iter->next_in_stack;
}
}
void vertical_check_scroller_root_inside_mon(Client *c,
struct wlr_box *geometry) {
if (!GEOMINSIDEMON(geometry, c->mon)) {
geometry->y = c->mon->w.y + (c->mon->w.height - geometry->height) / 2;
}
}
// 竖屏滚动布局 // 竖屏滚动布局
void vertical_scroller(Monitor *m) { void vertical_scroller(Monitor *m) {
int32_t i, n, j; int32_t i, n, j;
@ -209,9 +270,11 @@ 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;
int32_t cur_gappih = enablegaps ? m->gappih : 0;
cur_gappiv = cur_gappiv =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv; smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv;
@ -235,7 +298,7 @@ void vertical_scroller(Monitor *m) {
j = 0; j = 0;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) {
tempClients[j] = c; tempClients[j] = c;
j++; j++;
} }
@ -253,7 +316,8 @@ void vertical_scroller(Monitor *m) {
target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion; target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion;
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;
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;
resize(c, target_geom, 0); vertical_check_scroller_root_inside_mon(c, &target_geom);
arrange_stack_vertical(c, target_geom, cur_gappih);
free(tempClients); free(tempClients);
return; return;
} }
@ -267,6 +331,11 @@ void vertical_scroller(Monitor *m) {
root_client = center_tiled_select(m); root_client = center_tiled_select(m);
} }
// root_client might be in a stack, find the stack head
if (root_client) {
root_client = get_scroll_stack_head(root_client);
}
if (!root_client) { if (!root_client) {
free(tempClients); free(tempClients);
return; return;
@ -287,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;
} }
@ -302,31 +411,49 @@ void vertical_scroller(Monitor *m) {
if (tempClients[focus_client_index]->isfullscreen) { if (tempClients[focus_client_index]->isfullscreen) {
target_geom.y = m->m.y; target_geom.y = m->m.y;
resize(tempClients[focus_client_index], target_geom, 0); vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
&target_geom);
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih);
} else if (tempClients[focus_client_index]->ismaximizescreen) { } else if (tempClients[focus_client_index]->ismaximizescreen) {
target_geom.y = m->w.y + cur_gappov; target_geom.y = m->w.y + cur_gappov;
resize(tempClients[focus_client_index], target_geom, 0); vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
&target_geom);
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
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;
} }
resize(tempClients[focus_client_index], target_geom, 0); vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
&target_geom);
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih);
} else { } else {
target_geom.y = c->geom.y; target_geom.y = c->geom.y;
resize(tempClients[focus_client_index], target_geom, 0); vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
&target_geom);
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih);
} }
for (i = 1; i <= focus_client_index; i++) { for (i = 1; i <= focus_client_index; i++) {
@ -336,7 +463,7 @@ void vertical_scroller(Monitor *m) {
target_geom.y = tempClients[focus_client_index - i + 1]->geom.y - target_geom.y = tempClients[focus_client_index - i + 1]->geom.y -
cur_gappiv - target_geom.height; cur_gappiv - target_geom.height;
resize(c, target_geom, 0); arrange_stack_vertical(c, target_geom, cur_gappih);
} }
for (i = 1; i < n - focus_client_index; i++) { for (i = 1; i < n - focus_client_index; i++) {
@ -346,7 +473,7 @@ void vertical_scroller(Monitor *m) {
target_geom.y = tempClients[focus_client_index + i - 1]->geom.y + target_geom.y = tempClients[focus_client_index + i - 1]->geom.y +
cur_gappiv + cur_gappiv +
tempClients[focus_client_index + i - 1]->geom.height; tempClients[focus_client_index + i - 1]->geom.height;
resize(c, target_geom, 0); arrange_stack_vertical(c, target_geom, cur_gappih);
} }
free(tempClients); free(tempClients);

File diff suppressed because it is too large Load diff