Merge branch 'main' into sharewindow

This commit is contained in:
DreamMaoMao 2026-03-08 13:12:32 +08:00 committed by GitHub
commit 92a3e99e91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 12270 additions and 4090 deletions

View file

@ -17,6 +17,20 @@ https://github.com/DreamMaoMao/wlroots.git
mango version:
wlroots version:
## Crash track
1.you need to build mango by enable asan flag.
```bash
meson build -Dprefix=/usr -Dasan=true
``
2.run mango in tty.
```bash
export ASAN_OPTIONS="detect_leaks=1:halt_on_error=0:log_path=/home/xxx/asan.log"
mango
```
3.after mango crash,paste the log file `/home/xxx/asan.log` here.
## Description
<!--

View file

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

21
LICENSE.wlroots Normal file
View file

@ -0,0 +1,21 @@
Copyright (c) 2017, 2018 Drew DeVault
Copyright (c) 2014 Jari Vetoniemi
Copyright (c) 2023 The wlroots contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

170
README.md
View file

@ -1,7 +1,7 @@
# MangoWC
<img width="255" height="256" alt="mango-transparency-256" src="https://github.com/user-attachments/assets/54caff2c-932f-4998-a090-2a5292ebbfa4" />
# Mango Wayland Compositor
<div>
<img src="https://github.com/mangowm/mango/blob/main/assets/mango-transparency-256.png" alt="MangoWM Logo" width="120"/>
</div>
This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
@ -16,70 +16,68 @@ This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
- Base tags not workspaces (supports separate window layouts for each tag)
- Smooth and customizable complete animations (window open/move/close, tag enter/leave,layer open/close/move)
- Excellent input method support (text input v2/v3)
- Flexible window layouts with easy switching (scroller, master, monocle, spiral, etc.)
- Flexible window layouts with easy switching (scroller, master-stack, monocle,center-master, etc.)
- Rich window states (swallow, minimize, maximize, unglobal, global, fakefullscreen, overlay, etc.)
- Simple yet powerful external configuration
- Simple yet powerful external configuration(support shortcuts hot-reload)
- Sway-like scratchpad and named scratchpad
- Minimize window to scratchpad
- Ipc support(get/send message from/to compositor by external program)
- Hycov-like overview
- Window effects from scenefx (blur, shadow, corner radius, opacity)
- Zero flickering - every frame is perfect.
3. **Some disadvantages**
- Since it uses the fully automatic layout like dwm style, it does not allow you to manually adjust the window size when the window is in tiled state. It only allows you to change the layout parameters to adjust the window ratio.
https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
# Mango's Vision
Master-Stack 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/a9d4776e-b50b-48fb-94ce-651d8a749b8a
**Mango's preference is practicality**: I tend to add features that genuinely help with daily workflows—things that make our work more convenient.
Scroller Layout
https://github.com/user-attachments/assets/c9bf9415-fad1-4400-bcdc-3ad2d76de85a
Layer animaiton
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
[mangowm](https://discord.gg/CPjbDxesh5)
# Supported layouts
- Tile
- Scroller
- Monocle
- Grid
- Dwindle
- Spiral
- Deck
- tile
- scroller
- monocle
- grid
- deck
- center_tile
- vertical_tile
- vertical_grid
- vertical_scroller
- tgmix
# Installation
[![Packaging status](https://repology.org/badge/vertical-allrepos/mangowm.svg)](https://repology.org/project/mangowm/versions)
## Dependencies
- glibc
- wayland
- wayland-protocols
- libinput
- libdrm
- libxkbcommon
- pixman
- git
- meson
- ninja
- libdisplay-info
- libliftoff
- hwdata
- seatd
- pcre2
- xorg-xwayland
- libxcb
## Arch Linux
The package is in the Arch User Repository and is available for manual download [here](https://aur.archlinux.org/packages/mangowm-git) or through a AUR helper like yay:
```bash
yay -S mangowc-git
yay -S mangowm-git
```
## Gentoo Linux
The package is in the community-maintained repository called GURU.
First, add GURU repository:
@ -89,40 +87,79 @@ eselect repository enable guru
emerge --sync guru
```
Then, add `gui-libs/scenefx` and `gui-wm/mango` to the `package.accept_keywords`.
Then, add `gui-libs/scenefx` and `gui-wm/mangowm` to the `package.accept_keywords`.
Finally, install the package:
```bash
emerge --ask --verbose gui-wm/mango
emerge --ask --verbose gui-wm/mangowm
```
Patching wlroots is done by getting the patch with git from [the repository](https://github.com/DreamMaoMao/wlroots.git)
and then copying it to `/etc/portage/patches/gui-libs/wlroots/`.
## Fedora Linux
The package is in the third-party Terra repository.
First, add the [Terra Repository](https://terra.fyralabs.com/).
Then, install the package:
```bash
dnf install mangowm
```
## Guix System
The package definition is described in the source repository.
First, add `mangowm` channel to `channels.scm` file:
```scheme
;; In $HOME/.config/guix/channels.scm
(cons (channel
(name 'mangowm)
(url "https://github.com/mangowm/mango.git")
(branch "main"))
... ;; Your other channels
%default-channels)
```
Then, run `guix pull` and after update you can either run
`guix install mangowm` or add it to your configuration via:
```scheme
(use-modules (mangowm)) ;; Add mangowm module
;; Add mangowm to packages list
(packages (cons*
mangowm-git
... ;; Other packages you specified
%base-packages))
```
And then rebuild your system.
## Other
```bash
# wlroots 0.19.0 release with a fix-patch to avoid crash
git clone https://github.com/DreamMaoMao/wlroots.git
git clone -b 0.19.2 https://gitlab.freedesktop.org/wlroots/wlroots.git
cd wlroots
meson build -Dprefix=/usr
sudo ninja -C build install
git clone https://github.com/wlrfx/scenefx.git
git clone -b 0.4.1 https://github.com/wlrfx/scenefx.git
cd scenefx
meson build -Dprefix=/usr
sudo ninja -C build install
git clone https://github.com/DreamMaoMao/mangowc.git
cd mangowc
git clone https://github.com/mangowm/mango.git
cd mangowm
meson build -Dprefix=/usr
sudo ninja -C build install
```
## Suggested Tools
- Application launcher (rofi-wayland, bemenu, wmenu, fuzzel)
### Hybrid component
- [dms-shell](https://github.com/AvengeMedia/DankMaterialShell)
### Independent component
- Application launcher (rofi, bemenu, wmenu, fuzzel)
- Terminal emulator (foot, wezterm, alacritty, kitty, ghostty)
- Status bar (waybar, eww, quickshell, ags), waybar is preferred
- Wallpaper setup (swww, swaybg)
@ -135,19 +172,32 @@ sudo ninja -C build install
## Some Common Default Keybindings
- alt+return: open foot terminal
- alt+space: open rofi launcher
- alt+q: kill client
- alt+left/right/up/down: focus direction
- super+m: quit mango
## My Dotfiles
### Daily
- Dependencies
```bash
yay -S rofi-wayland foot xdg-desktop-portal-wlr swaybg waybar wl-clip-persist cliphist wl-clipboard wlsunset xfce-polkit swaync pamixer lavalauncher-mao-git wlr-dpms sway-audio-idle-inhibit-git swayidle dimland-git brightnessctl swayosd wlr-randr grim slurp satty swaylock-effects-git wlogout
yay -S rofi foot xdg-desktop-portal-wlr swaybg waybar wl-clip-persist cliphist wl-clipboard wlsunset xfce-polkit swaync pamixer wlr-dpms sway-audio-idle-inhibit-git swayidle dimland-git brightnessctl swayosd wlr-randr grim slurp satty swaylock-effects-git wlogout sox
```
- use my config
### Dms
- Dependencies
```bash
yay -S foot xdg-desktop-portal-wlr swaybg wl-clip-persist cliphist wl-clipboard sway-audio-idle-inhibit-git brightnessctl grim slurp satty matugen-bin dms-shell-git
```
- use my dms config
```bash
git clone -b dms https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
```
- use my daily config
```bash
git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
@ -156,7 +206,9 @@ git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
## Config Documentation
Refer to the [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://mangowm.github.io/)
# NixOS + Home-manager
@ -175,7 +227,10 @@ Here's an example of using the modules in a flake:
inputs.nixpkgs.follows = "nixpkgs";
};
flake-parts.url = "github:hercules-ci/flake-parts";
mango.url = "github:DreamMaoMao/mango";
mango = {
url = "github:mangowm/mango";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
inputs@{ self, flake-parts, ... }:
@ -235,12 +290,11 @@ Here's an example of using the modules in a flake:
To package mango for other distributions, you can check the reference setup for:
- [nix](https://github.com/DreamMaoMao/mango/blob/main/nix/default.nix)
- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mango-git).
- [nix](https://github.com/mangowm/mango/blob/main/nix/default.nix)
- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mangowm-git).
- [gentoo](https://data.gpo.zugaina.org/guru/gui-wm/mangowm)
Currently building mango requires a patched version of `wlroots-0.19`. If possible, the patch can be extracted from the [latest commit](https://github.com/DreamMaoMao/wlroots.git)
and applied on `prepare` step. If it is not possible, you will need to create a separate `wlroots` package and make it a build dependency.
You might also 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).
If you encounter build errors when packaging `mango`, feel free to create an issue and ask a question, but
Read The Friendly Manual on packaging software in your distribution first.
@ -256,3 +310,15 @@ Read The Friendly Manual on packaging software in your distribution first.
- https://github.com/swaywm/sway - Sample of Wayland protocol
- https://github.com/wlrfx/scenefx - Make it simple to add window effect.
# Sponsor
At present, I can only accept sponsorship through an encrypted connection.
If you find this project helpful to you, you can offer sponsorship in the following ways.
<img width="650" height="870" alt="image" src="https://github.com/user-attachments/assets/8c860317-90d2-4071-971d-f1a92b674469" />
Thanks to the following friends for their sponsorship of this project
[@tonybanters](https://github.com/tonybanters)

View file

@ -26,9 +26,9 @@ focused_opacity=1.0
unfocused_opacity=1.0
# Animation Configuration(support type:zoom,slide)
# tag_animation_direction: 0-horizontal,1-vertical
# tag_animation_direction: 1-horizontal,0-vertical
animations=1
layer_animations=0
layer_animations=1
animation_type_open=slide
animation_type_close=slide
animation_fade_in=1
@ -42,20 +42,25 @@ animation_duration_move=500
animation_duration_open=400
animation_duration_tag=350
animation_duration_close=800
animation_duration_focus=0
animation_curve_open=0.46,1.0,0.29,1
animation_curve_move=0.46,1.0,0.29,1
animation_curve_tag=0.46,1.0,0.29,1
animation_curve_close=0.08,0.92,0,1
animation_curve_focus=0.46,1.0,0.29,1
animation_curve_opafadeout=0.5,0.5,0.5,0.5
animation_curve_opafadein=0.46,1.0,0.29,1
# Scroller Layout Setting
scroller_structs=20
scroller_default_proportion=0.8
scroller_focus_center=0
scroller_prefer_center=0
edge_scroller_pointer_focus=1
scroller_default_proportion_single=1.0
scroller_proportion_preset=0.5,0.8,1.0
# Master-Stack Layout Setting (tile,spiral,dwindle)
# Master-Stack Layout Setting
new_is_master=1
default_mfact=0.55
default_nmaster=1
@ -72,7 +77,7 @@ overviewgappo=30
no_border_when_single=0
axis_bind_apply_timeout=100
focus_on_activate=1
inhibit_regardless_of_visibility=0
idleinhibit_ignore_visible=0
sloppyfocus=1
warpcursor=1
focus_cross_monitor=0
@ -85,10 +90,10 @@ drag_tile_to_tile=1
# keyboard
repeat_rate=25
repeat_delay=600
numlockon=1
numlockon=0
xkb_rules_layout=us
# Trackpad
# Trackpad
# need relogin to make it apply
disable_trackpad=0
tap_to_click=1
@ -98,7 +103,7 @@ trackpad_natural_scrolling=0
disable_while_typing=1
left_handed=0
middle_button_emulation=0
swipe_min_threshold=20
swipe_min_threshold=1
# mouse
# need relogin to make it apply
@ -115,13 +120,14 @@ borderpx=4
rootcolor=0x201b14ff
bordercolor=0x444444ff
focuscolor=0xc9b890ff
maxmizescreencolor=0x89aa61ff
maximizescreencolor=0x89aa61ff
urgentcolor=0xad401fff
scratchpadcolor=0x516c93ff
globalcolor=0xb153a7ff
overlaycolor=0x14a57cff
# layout support: tile,scroller,grid,monocle,spiral,dwindle
# layout support:
# tile,scroller,grid,deck,monocle,center_tile,vertical_tile,vertical_scroller
tagrule=id:1,layout_name:tile
tagrule=id:2,layout_name:tile
tagrule=id:3,layout_name:tile
@ -133,7 +139,7 @@ tagrule=id:8,layout_name:tile
tagrule=id:9,layout_name:tile
# Key Bindings
# key name refer to `xev` or `wev` command output,
# key name refer to `xev` or `wev` command output,
# mod keys name: super,ctrl,alt,shift,none
# reload config
@ -164,12 +170,12 @@ bind=SUPER+SHIFT,Right,exchange_client,right
bind=SUPER,g,toggleglobal,
bind=ALT,Tab,toggleoverview,
bind=ALT,backslash,togglefloating,
bind=ALT,a,togglemaxmizescreen,
bind=ALT,a,togglemaximizescreen,
bind=ALT,f,togglefullscreen,
bind=ALT+SHIFT,f,togglefakefullscreen,
bind=SUPER,i,minized,
bind=SUPER,i,minimized,
bind=SUPER,o,toggleoverlay,
bind=SUPER+SHIFT,I,restore_minized
bind=SUPER+SHIFT,I,restore_minimized
bind=ALT,z,toggle_scratchpad
# scroller layout
@ -180,35 +186,35 @@ bind=ALT,x,switch_proportion_preset,
bind=SUPER,n,switch_layout
# tag switch
bind=SUPER,Left,viewtoleft,
bind=CTRL,Left,viewtoleft_have_client,
bind=SUPER,Right,viewtoright,
bind=CTRL,Right,viewtoright_have_client,
bind=CTRL+SUPER,Left,tagtoleft,
bind=CTRL+SUPER,Right,tagtoright,
bind=SUPER,Left,viewtoleft,0
bind=CTRL,Left,viewtoleft_have_client,0
bind=SUPER,Right,viewtoright,0
bind=CTRL,Right,viewtoright_have_client,0
bind=CTRL+SUPER,Left,tagtoleft,0
bind=CTRL+SUPER,Right,tagtoright,0
bind=Ctrl,1,view,1
bind=Ctrl,2,view,2
bind=Ctrl,3,view,3
bind=Ctrl,4,view,4
bind=Ctrl,5,view,5
bind=Ctrl,6,view,6
bind=Ctrl,7,view,7
bind=Ctrl,8,view,8
bind=Ctrl,9,view,9
bind=Ctrl,1,view,1,0
bind=Ctrl,2,view,2,0
bind=Ctrl,3,view,3,0
bind=Ctrl,4,view,4,0
bind=Ctrl,5,view,5,0
bind=Ctrl,6,view,6,0
bind=Ctrl,7,view,7,0
bind=Ctrl,8,view,8,0
bind=Ctrl,9,view,9,0
# tag: move client to the tag and focus it
# tagsilent: move client to the tag and not focus it
# bind=Alt,1,tagsilent,1
bind=Alt,1,tag,1
bind=Alt,2,tag,2
bind=Alt,3,tag,3
bind=Alt,4,tag,4
bind=Alt,5,tag,5
bind=Alt,6,tag,6
bind=Alt,7,tag,7
bind=Alt,8,tag,8
bind=Alt,9,tag,9
bind=Alt,1,tag,1,0
bind=Alt,2,tag,2,0
bind=Alt,3,tag,3,0
bind=Alt,4,tag,4,0
bind=Alt,5,tag,5,0
bind=Alt,6,tag,6,0
bind=Alt,7,tag,7,0
bind=Alt,8,tag,8,0
bind=Alt,9,tag,9,0
# monitor switch
bind=alt+shift,Left,focusmon,left
@ -234,14 +240,17 @@ bind=CTRL+ALT,Left,resizewin,-50,+0
bind=CTRL+ALT,Right,resizewin,+50,+0
# 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=NONE,btn_middle,togglemaxmizescreen,0
mousebind=NONE,btn_middle,togglemaximizescreen,0
mousebind=SUPER,btn_right,moveresize,curresize
mousebind=NONE,btn_left,toggleoverview,-1
mousebind=NONE,btn_right,killclient,0
# Axis Bindings
axisbind=SUPER,UP,viewtoleft_have_client
axisbind=SUPER,DOWN,viewtoright_have_client
# layer rule
layerrule=animation_type_open:zoom,layer_name:rofi
layerrule=animation_type_close:zoom,layer_name:rofi

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Before After
Before After

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

55
flake.lock generated
View file

@ -18,45 +18,6 @@
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib_2"
},
"locked": {
"lastModified": 1741352980,
"narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"mmsg": {
"inputs": {
"flake-parts": "flake-parts_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1750505787,
"narHash": "sha256-D57Vl2x9RJP+8pjYGYUf1L/q+G6bsmoVaqJX3m5PtlQ=",
"owner": "DreamMaoMao",
"repo": "mmsg",
"rev": "4dc703aa06ae40d2cf5d3c0122b4d7f7d78fe8bf",
"type": "github"
},
"original": {
"owner": "DreamMaoMao",
"repo": "mmsg",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1750386251,
@ -88,25 +49,9 @@
"type": "github"
}
},
"nixpkgs-lib_2": {
"locked": {
"lastModified": 1740877520,
"narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "147dee35aab2193b174e4c0868bd80ead5ce755c",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"mmsg": "mmsg",
"nixpkgs": "nixpkgs",
"scenefx": "scenefx"
}

View file

@ -2,10 +2,6 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
mmsg = {
url = "github:DreamMaoMao/mmsg";
inputs.nixpkgs.follows = "nixpkgs";
};
scenefx = {
url = "github:wlrfx/scenefx";
inputs.nixpkgs.follows = "nixpkgs";
@ -32,13 +28,9 @@
pkgs,
...
}: let
inherit
(pkgs)
callPackage
;
inherit (pkgs) callPackage ;
mango = callPackage ./nix {
inherit (inputs.mmsg.packages.${pkgs.system}) mmsg;
inherit (inputs.scenefx.packages.${pkgs.system}) scenefx;
inherit (inputs.scenefx.packages.${pkgs.stdenv.hostPlatform.system}) scenefx;
};
shellOverride = old: {
nativeBuildInputs = old.nativeBuildInputs ++ [];

3
format.sh Normal file
View file

@ -0,0 +1,3 @@
#!/usr/bin/bash
clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/mmsg.c -i mmsg/arg.h -i mmsg/dynarr.h

67
mangowm.scm Normal file
View file

@ -0,0 +1,67 @@
(define-module (mangowm)
#:use-module (guix download)
#:use-module (guix git-download)
#:use-module (guix gexp)
#:use-module (guix packages)
#:use-module (guix utils)
#:use-module (gnu packages wm)
#:use-module (gnu packages freedesktop)
#:use-module (gnu packages xdisorg)
#:use-module (gnu packages pciutils)
#:use-module (gnu packages admin)
#:use-module (gnu packages pcre)
#:use-module (gnu packages xorg)
#:use-module (gnu packages build-tools)
#:use-module (gnu packages ninja)
#:use-module (gnu packages pkg-config)
#:use-module (guix build-system meson)
#:use-module (guix licenses))
(define-public mangowm-git
(package
(name "mangowm")
(version "git")
(source (local-file "." "mangowm-checkout"
#:recursive? #t
#:select? (or (git-predicate (current-source-directory))
(const #t))))
(build-system meson-build-system)
(arguments
(list
#:configure-flags
#~(list (string-append "-Dsysconfdir=" #$output "/etc"))
#:phases
#~(modify-phases %standard-phases
(add-before 'configure 'patch-meson
(lambda _
(substitute* "meson.build"
(("'-DSYSCONFDIR=\\\"@0@\\\"'.format\\('/etc'\\)")
"'-DSYSCONFDIR=\"@0@\"'.format(sysconfdir)")
(("sysconfdir = sysconfdir.substring\\(prefix.length\\(\\)\\)")
"")))))))
(inputs (list wayland
libinput
libdrm
libxkbcommon
pixman
libdisplay-info
libliftoff
hwdata
seatd
pcre2
libxcb
xcb-util-wm
wlroots
scenefx))
(native-inputs (list pkg-config wayland-protocols))
(home-page "https://github.com/DreamMaoMao/mangowm")
(synopsis "Wayland compositor based on wlroots and scenefx")
(description "A Wayland compositor based on wlroots and scenefx,
inspired by dwl but aiming to be more feature-rich.")
(license gpl3)))
(define-deprecated-package mangowc
mangowm-git)
mangowm-git

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'],
version : '0.8.0'
version : '0.12.5',
)
subdir('protocols')
@ -32,27 +32,38 @@ cc = meson.get_compiler('c')
libm = cc.find_library('m')
xcb = dependency('xcb', required : get_option('xwayland'))
xlibs = dependency('xcb-icccm', required : get_option('xwayland'))
wayland_server_dep = dependency('wayland-server')
wayland_server_dep = dependency('wayland-server',version: '>=1.23.1')
wlroots_dep = dependency('wlroots-0.19',version: '>=0.19.0')
xkbcommon_dep = dependency('xkbcommon')
libinput_dep = dependency('libinput')
libinput_dep = dependency('libinput',version: '>=1.27.1')
libwayland_client_dep = dependency('wayland-client')
pcre2_dep = dependency('libpcre2-8')
libscenefx_dep = dependency('scenefx-0.4',version: '>=0.4.1')
# 获取 Git Commit Hash 和最新的 tag
# 获取版本信息
git = find_program('git', required : false)
is_git_repo = false
# 检查当前目录是否是 Git 仓库
if git.found()
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()
else
commit_hash = 'unknown'
latest_tag = meson.project_version() # 如果 Git 不可用,使用默认版本号
git_status = run_command(git, 'rev-parse', '--is-inside-work-tree', check : false)
if git_status.returncode() == 0 and git_status.stdout().strip() == 'true'
is_git_repo = true
endif
endif
# 将 Commit Hash 和最新的 tag 添加到版本信息中
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
if is_git_repo
# 如果是 Git 目录,获取 Commit Hash 和最新的 tag
commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip()
latest_tag = meson.project_version()
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
else
# 如果不是 Git 目录,使用项目版本号和 "release" 字符串
commit_hash = 'release'
latest_tag = meson.project_version()
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
endif
# 定义编译参数
c_args = [
@ -60,17 +71,33 @@ c_args = [
'-Wno-unused-function',
'-DWLR_USE_UNSTABLE',
'-D_POSIX_C_SOURCE=200809L',
'-DVERSION="@0@"'.format(version_with_hash), # 版本信息包含 Commit Hash 和最新的 tag
'-DSYSCONFDIR="@0@"'.format('/etc'), # 添加 sysconfdir
'-DVERSION="@0@"'.format(version_with_hash),
'-DSYSCONFDIR="@0@"'.format('/etc'),
]
# 仅在 debug 选项启用时添加调试参数
if get_option('asan')
c_args += [
'-fsanitize=address',
'-fno-omit-frame-pointer',
'-fno-optimize-sibling-calls'
]
endif
if xcb.found() and xlibs.found()
c_args += '-DXWAYLAND'
endif
# 链接参数(根据 debug 状态添加 ASAN
link_args = []
if get_option('asan')
link_args += '-fsanitize=address'
endif
executable('mango',
'src/mango.c',
'src/common/util.c',
'src/ext-protocol/wlr_ext_workspace_v1.c',
wayland_sources,
dependencies : [
libm,
@ -85,11 +112,42 @@ executable('mango',
pcre2_dep,
],
install : true,
c_args : c_args
c_args : c_args,
link_args : link_args,
)
# build mmsg
dwl_ipc_protocol = 'protocols/dwl-ipc-unstable-v2.xml'
wayland_scanner_client_header = generator(
wayland_scanner,
output: '@BASENAME@-protocol.h',
arguments: ['client-header', '@INPUT@', '@OUTPUT@']
)
wayland_scanner_private_code = generator(
wayland_scanner,
output: '@BASENAME@-protocol.c',
arguments: ['private-code', '@INPUT@', '@OUTPUT@']
)
# 在 mmsg 目标中使用生成器
executable('mmsg',
'mmsg/mmsg.c',
wayland_scanner_private_code.process(dwl_ipc_protocol),
wayland_scanner_client_header.process(dwl_ipc_protocol),
dependencies: [
libwayland_client_dep
],
install: true,
c_args: [
'-g',
'-Wno-unused-function',
],
)
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
install_data('mango.desktop', install_dir : desktop_install_dir)
# 安装 config.conf
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
portal_install_dir = join_paths(prefix, 'share/xdg-desktop-portal')
install_data('assets/mango.desktop', install_dir : desktop_install_dir)
install_data('assets/mango-portals.conf', install_dir : portal_install_dir)
install_data('assets/config.conf', install_dir : join_paths(sysconfdir, 'mango'))

View file

@ -1 +1,2 @@
option('xwayland', type : 'feature', value : 'enabled')
option('asan', type : 'boolean', value : false)

65
mmsg/arg.h Normal file
View file

@ -0,0 +1,65 @@
/*
* Copy me if you can.
* by 20h
*/
#ifndef ARG_H__
#define ARG_H__
extern char *argv0;
/* use main(int32_t argc, char *argv[]) */
#define ARGBEGIN \
for (argv0 = *argv, argv++, argc--; \
argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \
char argc_; \
char **argv_; \
int32_t brk_; \
if (argv[0][1] == '-' && argv[0][2] == '\0') { \
argv++; \
argc--; \
break; \
} \
for (brk_ = 0, argv[0]++, argv_ = argv; argv[0][0] && !brk_; \
argv[0]++) { \
if (argv_ != argv) \
break; \
argc_ = argv[0][0]; \
switch (argc_)
/* Handles obsolete -NUM syntax */
#define ARGNUM \
case '0': \
case '1': \
case '2': \
case '3': \
case '4': \
case '5': \
case '6': \
case '7': \
case '8': \
case '9'
#define ARGEND \
} \
}
#define ARGC() argc_
#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX))
#define EARGF(x) \
((argv[0][1] == '\0' && argv[1] == NULL) \
? ((x), abort(), (char *)0) \
: (brk_ = 1, \
(argv[0][1] != '\0') ? (&argv[0][1]) : (argc--, argv++, argv[0])))
#define ARGF() \
((argv[0][1] == '\0' && argv[1] == NULL) \
? (char *)0 \
: (brk_ = 1, \
(argv[0][1] != '\0') ? (&argv[0][1]) : (argc--, argv++, argv[0])))
#define LNGARG() &argv[0][0]
#endif

30
mmsg/dynarr.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef DYNARR_H__
#define DYNARR_H__
#define DYNARR_DEF(t) \
struct { \
t *arr; \
size_t len, cap, size; \
}
#define DYNARR_INIT(p) \
((p)->arr = reallocarray((p)->arr, ((p)->cap = 1), \
((p)->size = sizeof(((p)->arr[0])))))
#define DYNARR_FINI(p) free((p)->arr)
#define DYNARR_PUSH(p, v) \
do { \
if ((p)->len >= (p)->cap) { \
while ((p)->len >= ((p)->cap *= 2)) \
; \
(p)->arr = reallocarray((p)->arr, (p)->cap, (p)->size); \
} \
(p)->arr[(p)->len++] = (v); \
} while (0)
#define DYNARR_POP(p) ((p)->arr[(p)->len--])
#define DYNARR_CLR(p) ((p)->len = 0)
#endif

825
mmsg/mmsg.c Normal file
View file

@ -0,0 +1,825 @@
#include "arg.h"
#include "dwl-ipc-unstable-v2-protocol.h"
#include "dynarr.h"
#include <ctype.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-util.h>
#define die(fmt, ...) \
do { \
fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0)
char *argv0;
static enum {
NONE = 0,
SET = 1 << 0,
GET = 1 << 1,
WATCH = 1 << 2 | GET,
} mode = NONE;
static int32_t Oflag;
static int32_t Tflag;
static int32_t Lflag;
static int32_t oflag;
static int32_t tflag;
static int32_t lflag;
static int32_t cflag;
static int32_t vflag;
static int32_t mflag;
static int32_t fflag;
static int32_t qflag;
static int32_t dflag;
static int32_t xflag;
static int32_t eflag;
static int32_t kflag;
static int32_t bflag;
static int32_t Aflag;
static uint32_t occ, seltags, total_clients, urg;
static char *output_name;
static int32_t tagcount;
static char *tagset;
static char *layout_name;
static int32_t layoutcount, layout_idx;
static char *client_tags;
static char *dispatch_cmd;
static char *dispatch_arg1;
static char *dispatch_arg2;
static char *dispatch_arg3;
static char *dispatch_arg4;
static char *dispatch_arg5;
struct output {
char *output_name;
uint32_t name;
};
static DYNARR_DEF(struct output) outputs;
static struct wl_display *display;
static struct zdwl_ipc_manager_v2 *dwl_ipc_manager;
// 为每个回调定义专用的空函数
static void noop_geometry(void *data, struct wl_output *wl_output, int32_t x,
int32_t y, int32_t physical_width,
int32_t physical_height, int32_t subpixel,
const char *make, const char *model,
int32_t transform) {}
static void noop_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh) {}
static void noop_done(void *data, struct wl_output *wl_output) {}
static void noop_scale(void *data, struct wl_output *wl_output,
int32_t factor) {}
static void noop_description(void *data, struct wl_output *wl_output,
const char *description) {}
// 将 n 转换为 9 位二进制字符串,结果存入 buf至少长度 10
void bin_str_9bits(char *buf, uint32_t n) {
for (int32_t i = 8; i >= 0; i--) {
*buf++ = ((n >> i) & 1) ? '1' : '0';
}
*buf = '\0'; // 字符串结尾
}
static void dwl_ipc_tags(void *data,
struct zdwl_ipc_manager_v2 *dwl_ipc_manager,
uint32_t count) {
tagcount = count;
if (Tflag && mode & GET)
printf("%d\n", tagcount);
}
static void dwl_ipc_layout(void *data,
struct zdwl_ipc_manager_v2 *dwl_ipc_manager,
const char *name) {
if (lflag && mode & SET && strcmp(layout_name, name) == 0)
layout_idx = layoutcount;
if (Lflag && mode & GET)
printf("%s\n", name);
layoutcount++;
}
static const struct zdwl_ipc_manager_v2_listener dwl_ipc_listener = {
.tags = dwl_ipc_tags, .layout = dwl_ipc_layout};
static void
dwl_ipc_output_toggle_visibility(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output) {
if (!vflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("toggle\n");
}
static void dwl_ipc_output_active(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
uint32_t active) {
if (!oflag) {
if (mode & SET && !output_name && active)
output_name = strdup(data);
return;
}
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("selmon %u\n", active ? 1 : 0);
}
static void dwl_ipc_output_tag(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
uint32_t tag, uint32_t state, uint32_t clients,
uint32_t focused) {
if (!tflag)
return;
if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE)
seltags |= 1 << tag;
if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT)
urg |= 1 << tag;
if (clients > 0)
occ |= 1 << tag;
// 累计所有 tag 的 clients 总数
total_clients += clients;
if (!(mode & GET))
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("tag %u %u %u %u\n", tag + 1, state, clients, focused);
}
static void dwl_ipc_output_layout(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
uint32_t layout) {}
static void dwl_ipc_output_layout_symbol(
void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *layout) {
if (!(lflag && mode & GET))
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("layout %s\n", layout);
}
static void dwl_ipc_output_title(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
const char *title) {
if (!(cflag && mode & GET))
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("title %s\n", title);
}
static void dwl_ipc_output_appid(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
const char *appid) {
if (!(cflag && mode & GET))
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("appid %s\n", appid);
}
static void dwl_ipc_output_x(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
int32_t x) {
if (!xflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("x %d\n", x);
}
static void dwl_ipc_output_y(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
int32_t y) {
if (!xflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("y %d\n", y);
}
static void dwl_ipc_output_width(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
int32_t width) {
if (!xflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("width %d\n", width);
}
static void dwl_ipc_output_height(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
int32_t height) {
if (!xflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("height %d\n", height);
}
static void dwl_ipc_output_last_layer(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
const char *last_layer) {
if (!eflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("last_layer %s\n", last_layer);
}
static void dwl_ipc_output_kb_layout(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
const char *kb_layout) {
if (!kflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("kb_layout %s\n", kb_layout);
}
static void
dwl_ipc_output_scalefactor(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
const uint32_t scalefactor) {
if (!Aflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("scale_factor %f\n", scalefactor / 100.0f);
}
static void dwl_ipc_output_keymode(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
const char *keymode) {
if (!bflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("keymode %s\n", keymode);
}
static void dwl_ipc_output_fullscreen(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
uint32_t is_fullscreen) {
if (!mflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("fullscreen %u\n", is_fullscreen);
}
static void dwl_ipc_output_floating(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
uint32_t is_floating) {
if (!fflag)
return;
char *output_name = data;
if (output_name)
printf("%s ", output_name);
printf("floating %u\n", is_floating);
}
static void dwl_ipc_output_frame(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output) {
if (mode & SET) {
if (data && (!output_name || strcmp(output_name, (char *)data)))
return;
if (qflag) {
zdwl_ipc_output_v2_quit(dwl_ipc_output);
}
if (lflag) {
zdwl_ipc_output_v2_set_layout(dwl_ipc_output, layout_idx);
}
if (tflag) {
uint32_t mask = seltags;
char *t = tagset;
int32_t i = 0;
for (; *t && *t >= '0' && *t <= '9'; t++)
i = *t - '0' + i * 10;
if (!*t)
mask = 1 << (i - 1);
for (; *t; t++, i++) {
switch (*t) {
case '-':
mask &= ~(1 << (i - 1));
break;
case '+':
mask |= 1 << (i - 1);
break;
case '^':
mask ^= 1 << (i - 1);
break;
}
}
if ((i - 1) > tagcount)
die("bad tagset %s", tagset);
zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, 0);
}
if (cflag) {
uint32_t and = ~0, xor = 0;
char *t = client_tags;
int32_t i = 0;
for (; *t && *t >= '0' && *t <= '9'; t++)
i = *t - '0' + i * 10;
if (!*t)
t = "+";
for (; *t; t++, i++) {
switch (*t) {
case '-':
and &= ~(1 << (i - 1));
break;
case '+':
and &= ~(1 << (i - 1));
xor |= 1 << (i - 1);
break;
case '^':
xor |= 1 << (i - 1);
break;
}
}
if ((i - 1) > tagcount)
die("bad client tagset %s", client_tags);
zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor);
}
if (dflag) {
zdwl_ipc_output_v2_dispatch(
dwl_ipc_output, dispatch_cmd, dispatch_arg1, dispatch_arg2,
dispatch_arg3, dispatch_arg4, dispatch_arg5);
}
wl_display_flush(display);
usleep(1000);
exit(0);
} else {
if (tflag) {
char *output_name = data;
printf("%s clients %u\n", output_name, total_clients);
char occ_str[10], seltags_str[10], urg_str[10];
bin_str_9bits(occ_str, occ);
bin_str_9bits(seltags_str, seltags);
bin_str_9bits(urg_str, urg);
printf("%s tags %u %u %u\n", output_name, occ, seltags, urg);
printf("%s tags %s %s %s\n", output_name, occ_str, seltags_str,
urg_str);
occ = seltags = total_clients = urg = 0;
}
}
fflush(stdout);
}
static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = {
.toggle_visibility = dwl_ipc_output_toggle_visibility,
.active = dwl_ipc_output_active,
.tag = dwl_ipc_output_tag,
.layout = dwl_ipc_output_layout,
.title = dwl_ipc_output_title,
.appid = dwl_ipc_output_appid,
.layout_symbol = dwl_ipc_output_layout_symbol,
.fullscreen = dwl_ipc_output_fullscreen,
.floating = dwl_ipc_output_floating,
.x = dwl_ipc_output_x,
.y = dwl_ipc_output_y,
.width = dwl_ipc_output_width,
.height = dwl_ipc_output_height,
.last_layer = dwl_ipc_output_last_layer,
.kb_layout = dwl_ipc_output_kb_layout,
.keymode = dwl_ipc_output_keymode,
.scalefactor = dwl_ipc_output_scalefactor,
.frame = dwl_ipc_output_frame,
};
static void wl_output_name(void *data, struct wl_output *output,
const char *name) {
if (outputs.arr) {
struct output *o = (struct output *)data;
o->output_name = strdup(name);
printf("+ ");
}
if (Oflag)
printf("%s\n", name);
if (output_name && strcmp(output_name, name) != 0) {
wl_output_release(output);
return;
}
struct zdwl_ipc_output_v2 *dwl_ipc_output =
zdwl_ipc_manager_v2_get_output(dwl_ipc_manager, output);
zdwl_ipc_output_v2_add_listener(dwl_ipc_output, &dwl_ipc_output_listener,
output_name ? NULL : strdup(name));
}
static const struct wl_output_listener output_listener = {
.geometry = noop_geometry,
.mode = noop_mode,
.done = noop_done,
.scale = noop_scale,
.name = wl_output_name,
.description = noop_description,
};
static void global_add(void *data, struct wl_registry *wl_registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_output_interface.name) == 0) {
struct wl_output *o =
wl_registry_bind(wl_registry, name, &wl_output_interface,
WL_OUTPUT_NAME_SINCE_VERSION);
if (!outputs.arr) {
wl_output_add_listener(o, &output_listener, NULL);
} else {
DYNARR_PUSH(&outputs, (struct output){.name = name});
wl_output_add_listener(o, &output_listener,
&outputs.arr[outputs.len - 1]);
}
} else if (strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) {
dwl_ipc_manager = wl_registry_bind(wl_registry, name,
&zdwl_ipc_manager_v2_interface, 2);
zdwl_ipc_manager_v2_add_listener(dwl_ipc_manager, &dwl_ipc_listener,
NULL);
}
}
static void global_remove(void *data, struct wl_registry *wl_registry,
uint32_t name) {
if (!outputs.arr)
return;
struct output *o = outputs.arr;
for (size_t i = 0; i < outputs.len; i++, o++) {
if (o->name == name) {
printf("- %s\n", o->output_name);
free(o->output_name);
*o = DYNARR_POP(&outputs);
}
}
}
static const struct wl_registry_listener registry_listener = {
.global = global_add,
.global_remove = global_remove,
};
static void usage(void) {
fprintf(stderr,
"mmsg - MangoWM IPC\n"
"\n"
"SYNOPSIS:\n"
"\tmmsg [-OTLq]\n"
"\tmmsg [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d "
"<cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]\n"
"\tmmsg [-o <output>] (-g | -w) [-OotlcvmfxekbA]\n"
"\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);
}
int32_t main(int32_t argc, char *argv[]) {
ARGBEGIN {
case 'q':
qflag = 1;
if (!(mode & GET)) {
mode = SET;
}
break;
case 's':
if (mode != NONE)
usage();
mode = SET;
break;
case 'g':
if (mode != NONE)
usage();
mode = GET;
break;
case 'w':
if (mode != NONE)
usage();
mode = WATCH;
break;
case 'o':
if (mode == SET)
output_name = EARGF(usage());
else
output_name = ARGF();
if (!output_name)
oflag = 1;
break;
case 't':
tflag = 1;
if (!(mode & GET)) {
mode = SET;
tagset = EARGF(usage());
}
break;
case 'l':
lflag = 1;
if (!(mode & GET)) {
mode = SET;
layout_name = EARGF(usage());
}
break;
case 'c':
cflag = 1;
if (!(mode & GET)) {
mode = SET;
client_tags = EARGF(usage());
}
break;
case 'd':
dflag = 1;
if (!(mode & GET)) {
mode = SET;
char *arg = EARGF(usage());
// Trim leading and trailing whitespace from entire argument first
while (isspace(*arg))
arg++;
char *end = arg + strlen(arg) - 1;
while (end > arg && isspace(*end))
end--;
*(end + 1) = '\0';
dispatch_cmd = arg;
char *comma1 = strchr(arg, ',');
if (comma1) {
*comma1 = '\0';
// Trim trailing whitespace from command
end = dispatch_cmd + strlen(dispatch_cmd) - 1;
while (end > dispatch_cmd && isspace(*end))
end--;
*(end + 1) = '\0';
dispatch_arg1 = comma1 + 1;
// Trim leading whitespace from arg1
while (isspace(*dispatch_arg1))
dispatch_arg1++;
// Trim trailing whitespace from arg1 before looking for next
// comma
end = dispatch_arg1 + strlen(dispatch_arg1) - 1;
while (end > dispatch_arg1 && isspace(*end))
end--;
*(end + 1) = '\0';
char *comma2 = strchr(dispatch_arg1, ',');
if (comma2) {
*comma2 = '\0';
dispatch_arg2 = comma2 + 1;
// Trim leading whitespace from arg2
while (isspace(*dispatch_arg2))
dispatch_arg2++;
// Trim trailing whitespace from arg2 before looking for
// next comma
end = dispatch_arg2 + strlen(dispatch_arg2) - 1;
while (end > dispatch_arg2 && isspace(*end))
end--;
*(end + 1) = '\0';
char *comma3 = strchr(dispatch_arg2, ',');
if (comma3) {
*comma3 = '\0';
dispatch_arg3 = comma3 + 1;
// Trim leading whitespace from arg3
while (isspace(*dispatch_arg3))
dispatch_arg3++;
// Trim trailing whitespace from arg3 before looking for
// next comma
end = dispatch_arg3 + strlen(dispatch_arg3) - 1;
while (end > dispatch_arg3 && isspace(*end))
end--;
*(end + 1) = '\0';
char *comma4 = strchr(dispatch_arg3, ',');
if (comma4) {
*comma4 = '\0';
dispatch_arg4 = comma4 + 1;
// Trim leading whitespace from arg4
while (isspace(*dispatch_arg4))
dispatch_arg4++;
// Trim trailing whitespace from arg4 before looking
// for next comma
end = dispatch_arg4 + strlen(dispatch_arg4) - 1;
while (end > dispatch_arg4 && isspace(*end))
end--;
*(end + 1) = '\0';
char *comma5 = strchr(dispatch_arg4, ',');
if (comma5) {
*comma5 = '\0';
dispatch_arg5 = comma5 + 1;
// Trim leading whitespace from arg5
while (isspace(*dispatch_arg5))
dispatch_arg5++;
// Trim trailing whitespace from arg5
end = dispatch_arg5 + strlen(dispatch_arg5) - 1;
while (end > dispatch_arg5 && isspace(*end))
end--;
*(end + 1) = '\0';
} else {
dispatch_arg5 = "";
}
} else {
dispatch_arg4 = "";
dispatch_arg5 = "";
}
} else {
dispatch_arg3 = "";
dispatch_arg4 = "";
dispatch_arg5 = "";
}
} else {
dispatch_arg2 = "";
dispatch_arg3 = "";
dispatch_arg4 = "";
dispatch_arg5 = "";
}
} else {
dispatch_arg1 = "";
dispatch_arg2 = "";
dispatch_arg3 = "";
dispatch_arg4 = "";
dispatch_arg5 = "";
}
}
break;
case 'O':
Oflag = 1;
if (mode && !(mode & GET))
usage();
if (mode & WATCH)
DYNARR_INIT(&outputs);
else
mode = GET;
break;
case 'T':
Tflag = 1;
if (mode && mode != GET)
usage();
mode = GET;
break;
case 'L':
Lflag = 1;
if (mode && mode != GET)
usage();
mode = GET;
break;
case 'v':
vflag = 1;
if (mode == SET)
usage();
mode |= GET;
break;
case 'm':
mflag = 1;
if (mode == SET)
usage();
mode |= GET;
break;
case 'f':
fflag = 1;
if (mode == SET)
usage();
mode |= GET;
break;
case 'x':
xflag = 1;
if (mode == SET)
usage();
mode |= GET;
break;
case 'e':
eflag = 1;
if (mode == SET)
usage();
mode |= GET;
break;
case 'k':
kflag = 1;
if (mode == SET)
usage();
mode |= GET;
break;
case 'b':
bflag = 1;
if (mode == SET)
usage();
mode |= GET;
break;
case 'A':
Aflag = 1;
if (mode == SET)
usage();
mode |= GET;
break;
default:
fprintf(stderr, "bad option %c\n", ARGC());
usage();
}
ARGEND
if (mode == NONE)
usage();
if (mode & GET && !output_name &&
!(oflag || tflag || lflag || Oflag || Tflag || Lflag || cflag ||
vflag || mflag || fflag || xflag || eflag || kflag || bflag ||
Aflag || dflag))
oflag = tflag = lflag = cflag = vflag = mflag = fflag = xflag = eflag =
kflag = bflag = Aflag = 1;
display = wl_display_connect(NULL);
if (!display)
die("bad display");
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (!dwl_ipc_manager)
die("bad dwl-ipc protocol");
wl_display_roundtrip(display);
if (mode == WATCH)
while (wl_display_dispatch(display) != -1)
;
return 0;
}

View file

@ -1,6 +1,5 @@
{
lib,
fetchFromGitHub,
libX11,
libinput,
libxcb,
@ -12,75 +11,66 @@
wayland,
wayland-protocols,
wayland-scanner,
xcbutilwm,
libxcb-wm,
xwayland,
enableXWayland ? true,
meson,
ninja,
mmsg,
scenefx,
wlroots_0_19,
libGL,
}: let
enableXWayland ? true,
debug ? false,
}:
stdenv.mkDerivation {
pname = "mango";
# Use patched wlroots from github.com/DreamMaoMao/wlroots
wlroots-git = wlroots_0_19.overrideAttrs (
final: prev: {
src = fetchFromGitHub {
owner = "DreamMaoMao";
repo = "wlroots";
rev = "afbb5b7c2b14152730b57aa11119b1b16a299d5b";
sha256 = "sha256-pVU+CuiqvduMTpsnDHX/+EWY2qxHX2lXKiVzdGtcnYY=";
};
}
);
in
stdenv.mkDerivation {
inherit pname;
version = "nightly";
version = "nightly";
src = builtins.path {
path = ../.;
name = "source";
};
src = builtins.path {
path = ../.;
name = "source";
};
nativeBuildInputs = [
meson
ninja
pkg-config
wayland-scanner
mesonFlags = [
(lib.mesonEnable "xwayland" enableXWayland)
(lib.mesonBool "asan" debug)
];
nativeBuildInputs = [
meson
ninja
pkg-config
wayland-scanner
];
buildInputs =
[
libinput
libxcb
libxkbcommon
pcre2
pixman
wayland
wayland-protocols
wlroots_0_19
scenefx
libGL
]
++ lib.optionals enableXWayland [
libX11
libxcb-wm
xwayland
];
buildInputs =
[
libinput
libxcb
libxkbcommon
pcre2
pixman
wayland
wayland-protocols
wlroots-git
scenefx
libGL
]
++ lib.optionals enableXWayland [
libX11
xcbutilwm
xwayland
];
passthru = {
providedSessions = ["mango"];
};
passthru = {
providedSessions = ["mango"];
inherit mmsg;
};
meta = {
mainProgram = "mango";
description = "A streamlined but feature-rich Wayland compositor";
homepage = "https://github.com/DreamMaoMao/mango";
license = lib.licenses.gpl3Plus;
maintainers = [];
platforms = lib.platforms.unix;
};
}
meta = {
mainProgram = "mango";
description = "A streamlined but feature-rich Wayland compositor";
homepage = "https://github.com/DreamMaoMao/mango";
license = lib.licenses.gpl3Plus;
maintainers = [];
platforms = lib.platforms.unix;
};
}

View file

@ -4,7 +4,6 @@ self: {
pkgs,
...
}: let
inherit (self.packages.${pkgs.system}) mango;
cfg = config.wayland.windowManager.mango;
variables = lib.concatStringsSep " " cfg.systemd.variables;
extraCommands = lib.concatStringsSep " && " cfg.systemd.extraCommands;
@ -20,6 +19,11 @@ in {
type = types.bool;
default = false;
};
package = lib.mkOption {
type = lib.types.package;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mango;
description = "The mango package to use";
};
systemd = {
enable = mkOption {
type = types.bool;
@ -72,7 +76,7 @@ in {
};
settings = mkOption {
description = "mango config content";
type = types.str;
type = types.lines;
default = "";
example = ''
# menu and terminal
@ -82,7 +86,7 @@ in {
};
autostart_sh = mkOption {
description = "WARRNING: This is a shell script, but no need to add shebang";
type = types.str;
type = types.lines;
default = "";
example = ''
waybar &
@ -92,27 +96,16 @@ in {
};
config = lib.mkIf cfg.enable {
home.packages = [mango];
home.activation =
lib.optionalAttrs (cfg.autostart_sh != "") {
createMangoScript = lib.hm.dag.entryAfter ["clearMangoConfig"] ''
cat ${autostart_sh} > $HOME/.config/mango/autostart.sh
chmod +x $HOME/.config/mango/autostart.sh
'';
}
// lib.optionalAttrs (cfg.settings != "") {
createMangoConfig = lib.hm.dag.entryAfter ["clearMangoConfig"] ''
cat > $HOME/.config/mango/config.conf <<EOF
${cfg.settings}
EOF
'';
}
// {
clearMangoConfig = lib.hm.dag.entryAfter ["writeBoundary"] ''
rm -rf $HOME/.config/mango
mkdir -p $HOME/.config/mango
'';
home.packages = [cfg.package];
xdg.configFile = {
"mango/config.conf" = lib.mkIf (cfg.settings != "") {
text = cfg.settings;
};
"mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") {
source = autostart_sh;
executable = true;
};
};
systemd.user.targets.mango-session = lib.mkIf cfg.systemd.enable {
Unit = {
Description = "mango compositor session";

View file

@ -11,7 +11,7 @@ in {
enable = lib.mkEnableOption "mango, a wayland compositor based on dwl";
package = lib.mkOption {
type = lib.types.package;
default = self.packages.${pkgs.system}.mango;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mango;
description = "The mango package to use";
};
};
@ -21,16 +21,30 @@ in {
environment.systemPackages =
[
cfg.package
]
++ (
if (builtins.hasAttr "mmsg" cfg.package)
then [cfg.package.mmsg]
else []
);
];
xdg.portal = {
enable = lib.mkDefault true;
config = {
mango = {
default = [
"gtk"
];
# except those
"org.freedesktop.impl.portal.Secret" = ["gnome-keyring"];
"org.freedesktop.impl.portal.ScreenCast" = ["wlr"];
"org.freedesktop.impl.portal.ScreenShot" = ["wlr"];
# wlr does not have this interface
"org.freedesktop.impl.portal.Inhibit" = [];
};
};
extraPortals = with pkgs; [
xdg-desktop-portal-wlr
xdg-desktop-portal-gtk
];
wlr.enable = lib.mkDefault true;
configPackages = [cfg.package];

View file

@ -227,6 +227,27 @@ I would probably just submit raphi's patchset but I don't think that would be po
<arg name="last_layer" type="string" summary="last map layer."/>
</event>
<event name="kb_layout" since="2">
<description summary="current keyboard layout.">
current keyboard layout.
</description>
<arg name="kb_layout" type="string" summary="current keyboard layout."/>
</event>
<event name="keymode" since="2">
<description summary="current keybind mode.">
current keybind mode.
</description>
<arg name="keymode" type="string" summary="current keybind mode."/>
</event>
<event name="scalefactor" since="2">
<description summary="scale factor of monitor.">
scale factor of monitor.
</description>
<arg name="scalefactor" type="uint" summary="scale factor of monitor."/>
</event>
</interface>
</protocol>

View file

@ -1,6 +1,6 @@
wayland_scanner = find_program('wayland-scanner')
wayland_protos_dep = dependency('wayland-protocols')
wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir')
wl_protocol_dir = wayland_protos_dep.get_variable(pkgconfig:'pkgdatadir')
wayland_scanner_code = generator(
wayland_scanner,
output: '@BASENAME@-protocol.c',
@ -18,6 +18,8 @@ wayland_xmls = [
wl_protocol_dir + '/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml',
wl_protocol_dir + '/staging/ext-image-capture-source/ext-image-capture-source-v1.xml',
wl_protocol_dir + '/staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
wl_protocol_dir + '/staging/ext-workspace/ext-workspace-v1.xml',
wl_protocol_dir + '/staging/tearing-control/tearing-control-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
'dwl-ipc-unstable-v2.xml',
'wlr-layer-shell-unstable-v1.xml',

View file

@ -1,10 +1,10 @@
void client_actual_size(Client *c, unsigned int *width, unsigned int *height) {
*width = c->animation.current.width - c->bw;
void client_actual_size(Client *c, int32_t *width, int32_t *height) {
*width = c->animation.current.width - 2 * (int32_t)c->bw;
*height = c->animation.current.height - c->bw;
*height = c->animation.current.height - 2 * (int32_t)c->bw;
}
void set_rect_size(struct wlr_scene_rect *rect, int width, int height) {
void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) {
wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height));
}
@ -28,35 +28,54 @@ enum corner_location set_client_corner_location(Client *c) {
return current_corner_location;
}
int is_special_animaiton_rule(Client *c) {
int visible_client_number = 0;
Client *count_c;
wl_list_for_each(count_c, &clients, link) {
if (count_c && VISIBLEON(count_c, selmon) && !count_c->isminied &&
!count_c->iskilling && !count_c->isfloating) {
visible_client_number++;
}
}
bool is_horizontal_stack_layout(Monitor *m) {
if (is_scroller_layout(selmon) && !c->isfloating) {
if (m->pertag->curtag &&
(m->pertag->ltidxs[m->pertag->curtag]->id == TILE ||
m->pertag->ltidxs[m->pertag->curtag]->id == DECK))
return true;
return false;
}
bool is_horizontal_right_stack_layout(Monitor *m) {
if (m->pertag->curtag &&
(m->pertag->ltidxs[m->pertag->curtag]->id == RIGHT_TILE))
return true;
return false;
}
int32_t is_special_animation_rule(Client *c) {
if (is_scroller_layout(c->mon) && !c->isfloating) {
return DOWN;
} else if (visible_client_number < 2 && !c->isfloating) {
} else if (c->mon->visible_tiling_clients == 1 && !c->isfloating) {
return DOWN;
} else if (visible_client_number == 2 && !c->isfloating && !new_is_master) {
} else if (c->mon->visible_tiling_clients == 2 && !c->isfloating &&
!new_is_master && is_horizontal_stack_layout(c->mon)) {
return RIGHT;
} else if (!c->isfloating && new_is_master) {
} else if (!c->isfloating && new_is_master &&
is_horizontal_stack_layout(c->mon)) {
return LEFT;
} else if (c->mon->visible_tiling_clients == 2 && !c->isfloating &&
!new_is_master && is_horizontal_right_stack_layout(c->mon)) {
return LEFT;
} else if (!c->isfloating && new_is_master &&
is_horizontal_right_stack_layout(c->mon)) {
return RIGHT;
} else {
return UNDIR;
}
}
void set_client_open_animaiton(Client *c, struct wlr_box geo) {
int slide_direction;
int horizontal, horizontal_value;
int vertical, vertical_value;
int special_direction;
int center_x, center_y;
void set_client_open_animation(Client *c, struct wlr_box geo) {
int32_t slide_direction;
int32_t horizontal, horizontal_value;
int32_t vertical, vertical_value;
int32_t special_direction;
int32_t center_x, center_y;
if ((!c->animation_type_open && strcmp(animation_type_open, "fade") == 0) ||
(c->animation_type_open &&
@ -77,7 +96,7 @@ void set_client_open_animaiton(Client *c, struct wlr_box geo) {
geo.y + (geo.height - c->animainit_geom.height) / 2;
return;
} 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_y = c->geom.y + c->geom.height / 2;
if (special_direction == UNDIR) {
@ -128,15 +147,15 @@ void set_client_open_animaiton(Client *c, struct wlr_box geo) {
}
}
void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx,
int sy, void *data) {
void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
int32_t sy, void *data) {
BufferData *buffer_data = (BufferData *)data;
wlr_scene_buffer_set_dest_size(buffer, buffer_data->width,
buffer_data->height);
}
void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy,
void *data) {
void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
int32_t sy, void *data) {
BufferData *buffer_data = (BufferData *)data;
if (buffer_data->should_scale && buffer_data->height_scale < 1 &&
@ -164,8 +183,8 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy,
if (buffer_data->should_scale) {
unsigned int surface_width = surface->current.width;
unsigned int surface_height = surface->current.height;
int32_t surface_width = surface->current.width;
int32_t surface_height = surface->current.height;
surface_width = buffer_data->width_scale < 1
? surface_width
@ -206,12 +225,6 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy,
wlr_scene_buffer_set_corner_radius(buffer, border_radius,
buffer_data->corner_location);
float target_opacity = buffer_data->percent + fadein_begin_opacity;
if (target_opacity > buffer_data->opacity) {
target_opacity = buffer_data->opacity;
}
wlr_scene_buffer_set_opacity(buffer, target_opacity);
}
void buffer_set_effect(Client *c, BufferData data) {
@ -227,8 +240,9 @@ void buffer_set_effect(Client *c, BufferData data) {
if (c == grabc)
data.should_scale = false;
if (c->isfullscreen || (no_radius_when_single && c->mon &&
c->mon->visible_tiling_clients == 1)) {
if (c->isnoradius || c->isfullscreen ||
(no_radius_when_single && c->mon &&
c->mon->visible_tiling_clients == 1)) {
data.corner_location = CORNER_LOCATION_NONE;
}
@ -238,12 +252,16 @@ void buffer_set_effect(Client *c, BufferData data) {
void client_draw_shadow(Client *c) {
if (c->iskilling || !client_surface(c)->mapped)
if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow)
return;
if (!shadows || (!c->isfloating && shadow_only_floating)) {
wlr_scene_shadow_set_size(c->shadow, 0, 0);
if (c->shadow->node.enabled)
wlr_scene_node_set_enabled(&c->shadow->node, false);
return;
} else {
if (c->scene_surface->node.enabled && !c->shadow->node.enabled)
wlr_scene_node_set_enabled(&c->shadow->node, true);
}
bool hit_no_border = check_hit_no_border(c);
@ -253,24 +271,24 @@ void client_draw_shadow(Client *c) {
? CORNER_LOCATION_NONE
: CORNER_LOCATION_ALL;
unsigned int bwoffset = c->bw != 0 && hit_no_border ? c->bw : 0;
int32_t bwoffset = c->bw != 0 && hit_no_border ? (int32_t)c->bw : 0;
uint32_t width, height;
int32_t width, height;
client_actual_size(c, &width, &height);
uint32_t delta = shadows_size + c->bw - bwoffset;
int32_t delta = shadows_size + (int32_t)c->bw - bwoffset;
/* we calculate where to clip the shadow */
struct wlr_box client_box = {
.x = bwoffset,
.y = bwoffset,
.width = width - 2 * bwoffset,
.height = height - 2 * bwoffset,
.width = width + (int32_t)c->bw - bwoffset,
.height = height + (int32_t)c->bw - bwoffset,
};
struct wlr_box shadow_box = {
.x = shadows_position_x,
.y = shadows_position_y,
.x = shadows_position_x + bwoffset,
.y = shadows_position_y + bwoffset,
.width = width + 2 * delta,
.height = height + 2 * delta,
};
@ -279,8 +297,8 @@ void client_draw_shadow(Client *c) {
wlr_box_intersection(&intersection_box, &client_box, &shadow_box);
/* clipped region takes shadow relative coords, so we translate everything
* by its position */
intersection_box.x -= shadows_position_x;
intersection_box.y -= shadows_position_y;
intersection_box.x -= shadows_position_x + bwoffset;
intersection_box.y -= shadows_position_y + bwoffset;
struct clipped_region clipped_region = {
.area = intersection_box,
@ -295,7 +313,7 @@ void client_draw_shadow(Client *c) {
.height = shadow_box.height,
};
int right_offset, bottom_offset, left_offset, top_offset;
int32_t right_offset, bottom_offset, left_offset, top_offset;
if (c == grabc) {
right_offset = 0;
@ -337,13 +355,14 @@ void apply_border(Client *c) {
return;
bool hit_no_border = check_hit_no_border(c);
enum corner_location current_corner_location =
c->isfullscreen || (no_radius_when_single && c->mon &&
c->mon->visible_tiling_clients == 1)
? CORNER_LOCATION_NONE
: CORNER_LOCATION_ALL;
enum corner_location current_corner_location;
if (c->isfullscreen || (no_radius_when_single && c->mon &&
c->mon->visible_tiling_clients == 1)) {
current_corner_location = CORNER_LOCATION_NONE;
} else {
current_corner_location = set_client_corner_location(c);
}
// Handle no-border cases
if (hit_no_border && smartgaps) {
c->bw = 0;
c->fake_no_border = true;
@ -359,9 +378,9 @@ void apply_border(Client *c) {
struct wlr_box clip_box = c->animation.current;
// 一但在GEZERO如果使用无符号那么其他数据也会转换为无符号导致没有负数出错
int bw = (int)c->bw;
int32_t bw = (int32_t)c->bw;
int right_offset, bottom_offset, left_offset, top_offset;
int32_t right_offset, bottom_offset, left_offset, top_offset;
if (c == grabc) {
right_offset = 0;
@ -380,25 +399,27 @@ void apply_border(Client *c) {
top_offset = GEZERO(c->mon->m.y - c->animation.current.y);
}
int inner_surface_width = GEZERO(clip_box.width - 2 * bw);
int inner_surface_height = GEZERO(clip_box.height - 2 * bw);
int32_t inner_surface_width = GEZERO(clip_box.width - 2 * bw);
int32_t inner_surface_height = GEZERO(clip_box.height - 2 * bw);
int inner_surface_x = GEZERO(bw - left_offset);
int inner_surface_y = GEZERO(bw - top_offset);
int32_t inner_surface_x = GEZERO(bw - left_offset);
int32_t inner_surface_y = GEZERO(bw - top_offset);
int rect_x = left_offset;
int rect_y = top_offset;
int32_t rect_x = left_offset;
int32_t rect_y = top_offset;
int rect_width =
int32_t rect_width =
GEZERO(c->animation.current.width - left_offset - right_offset);
int rect_height =
int32_t rect_height =
GEZERO(c->animation.current.height - top_offset - bottom_offset);
if (left_offset > c->bw)
inner_surface_width = inner_surface_width - left_offset + c->bw;
inner_surface_width =
inner_surface_width - left_offset + (int32_t)c->bw;
if (top_offset > c->bw)
inner_surface_height = inner_surface_height - top_offset + c->bw;
inner_surface_height =
inner_surface_height - top_offset + (int32_t)c->bw;
if (right_offset > 0) {
inner_surface_width =
@ -426,31 +447,31 @@ void apply_border(Client *c) {
}
struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) {
int offsetx = 0, offsety = 0, offsetw = 0, offseth = 0;
int32_t offsetx = 0, offsety = 0, offsetw = 0, offseth = 0;
struct ivec2 offset = {0, 0, 0, 0};
if (!ISTILED(c) && !c->animation.tagining && !c->animation.tagouted &&
if (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouted &&
!c->animation.tagouting)
return offset;
int bottom_out_offset =
int32_t bottom_out_offset =
GEZERO(c->animation.current.y + c->animation.current.height -
c->mon->m.y - c->mon->m.height);
int right_out_offset =
int32_t right_out_offset =
GEZERO(c->animation.current.x + c->animation.current.width -
c->mon->m.x - c->mon->m.width);
int left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x);
int top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y);
int32_t left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x);
int32_t top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y);
// 必须转换为int否计算会没有负数导致判断错误
int bw = (int)c->bw;
int32_t bw = (int32_t)c->bw;
/*
border超出屏幕的时候不计算如偏差之内而是
*/
if (ISTILED(c) || c->animation.tagining || c->animation.tagouted ||
if (ISSCROLLTILED(c) || c->animation.tagining || c->animation.tagouted ||
c->animation.tagouting) {
if (left_out_offset > 0) {
offsetx = GEZERO(left_out_offset - bw);
@ -478,7 +499,7 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) {
offset.height = offseth;
if ((clip_box->width + bw <= 0 || clip_box->height + bw <= 0) &&
(ISTILED(c) || c->animation.tagouting || c->animation.tagining)) {
(ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) {
c->is_clip_to_hide = true;
wlr_scene_node_set_enabled(&c->scene->node, false);
} else if (c->is_clip_to_hide && VISIBLEON(c, c->mon)) {
@ -498,13 +519,10 @@ void client_apply_clip(Client *c, float factor) {
bool should_render_client_surface = false;
struct ivec2 offset;
BufferData buffer_data;
float opacity, percent;
enum corner_location current_corner_location =
set_client_corner_location(c);
int bw = (int)c->bw;
if (!animations) {
c->animation.running = false;
c->need_output_flush = false;
@ -518,31 +536,19 @@ void client_apply_clip(Client *c, float factor) {
apply_border(c);
client_draw_shadow(c);
opacity = c->isfullscreen ? 1
: c == selmon->sel ? c->focused_opacity
: c->unfocused_opacity;
if (clip_box.width <= 0 || clip_box.height <= 0) {
return;
}
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box);
buffer_set_effect(c, (BufferData){1.0f, 1.0f, clip_box.width,
clip_box.height, opacity, opacity,
clip_box.height,
current_corner_location, true});
return;
}
percent =
c->animation.action == OPEN && animation_fade_in && !c->nofadein
? (double)c->animation.passed_frames / c->animation.total_frames
: 1.0;
opacity = c->isfullscreen ? 1
: c == selmon->sel ? c->focused_opacity
: c->unfocused_opacity;
// 获取窗口动画实时位置矩形
unsigned int width, height;
int32_t width, height;
client_actual_size(c, &width, &height);
// 计算出除了边框的窗口实际剪切大小
@ -551,8 +557,8 @@ void client_apply_clip(Client *c, float factor) {
clip_box = (struct wlr_box){
.x = geometry.x,
.y = geometry.y,
.width = width - bw,
.height = height - bw,
.width = width,
.height = height,
};
if (client_is_x11(c)) {
@ -585,8 +591,8 @@ void client_apply_clip(Client *c, float factor) {
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box);
// 获取剪切后的表面的实际大小用于计算缩放
int acutal_surface_width = geometry.width - offset.x - offset.width;
int acutal_surface_height = geometry.height - offset.y - offset.height;
int32_t acutal_surface_width = geometry.width - offset.x - offset.width;
int32_t acutal_surface_height = geometry.height - offset.y - offset.height;
if (acutal_surface_width <= 0 || acutal_surface_height <= 0)
return;
@ -595,8 +601,6 @@ void client_apply_clip(Client *c, float factor) {
buffer_data.width = clip_box.width;
buffer_data.height = clip_box.height;
buffer_data.corner_location = current_corner_location;
buffer_data.percent = percent;
buffer_data.opacity = opacity;
if (factor == 1.0) {
buffer_data.width_scale = 1.0;
@ -617,21 +621,27 @@ void fadeout_client_animation_next_tick(Client *c) {
BufferData buffer_data;
double animation_passed =
(double)c->animation.passed_frames / c->animation.total_frames;
int type = c->animation.action = c->animation.action;
double factor = find_animation_curve_at(animation_passed, type);
unsigned int width =
c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor;
unsigned int height =
c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
unsigned int x = c->animation.initial.x +
(c->current.x - c->animation.initial.x) * factor;
unsigned int y = c->animation.initial.y +
(c->current.y - c->animation.initial.y) * factor;
int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
double animation_passed =
c->animation.duration
? (double)passed_time / (double)c->animation.duration
: 1.0;
int32_t type = c->animation.action = c->animation.action;
double factor = find_animation_curve_at(animation_passed, type);
int32_t width = c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor;
int32_t height = c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor;
int32_t x = c->animation.initial.x +
(c->current.x - c->animation.initial.x) * factor;
int32_t y = c->animation.initial.y +
(c->current.y - c->animation.initial.y) * factor;
wlr_scene_node_set_position(&c->scene->node, x, y);
@ -642,7 +652,13 @@ void fadeout_client_animation_next_tick(Client *c) {
.height = height,
};
double opacity = MAX(fadeout_begin_opacity - animation_passed, 0);
double opacity_eased_progress =
find_animation_curve_at(animation_passed, OPAFADEOUT);
double percent = fadeout_begin_opacity -
(opacity_eased_progress * fadeout_begin_opacity);
double opacity = MAX(percent, 0);
if (animation_fade_out && !c->nofadeout)
wlr_scene_node_for_each_buffer(&c->scene->node,
@ -662,38 +678,40 @@ void fadeout_client_animation_next_tick(Client *c) {
&c->scene->node, snap_scene_buffer_apply_effect, &buffer_data);
}
if (animation_passed == 1.0) {
if (animation_passed >= 1.0) {
wl_list_remove(&c->fadeout_link);
wlr_scene_node_destroy(&c->scene->node);
free(c);
c = NULL;
} else {
c->animation.passed_frames++;
}
}
void client_animation_next_tick(Client *c) {
double animation_passed =
(double)c->animation.passed_frames / c->animation.total_frames;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int type = c->animation.action == NONE ? MOVE : c->animation.action;
int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
double animation_passed =
c->animation.duration
? (double)passed_time / (double)c->animation.duration
: 1.0;
int32_t type = c->animation.action == NONE ? MOVE : c->animation.action;
double factor = find_animation_curve_at(animation_passed, type);
Client *pointer_c = NULL;
double sx = 0, sy = 0;
struct wlr_surface *surface = NULL;
unsigned int width =
c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor;
unsigned int height =
c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor;
int32_t width = c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor;
int32_t height = c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor;
unsigned int x = c->animation.initial.x +
(c->current.x - c->animation.initial.x) * factor;
unsigned int y = c->animation.initial.y +
(c->current.y - c->animation.initial.y) * factor;
int32_t x = c->animation.initial.x +
(c->current.x - c->animation.initial.x) * factor;
int32_t y = c->animation.initial.y +
(c->current.y - c->animation.initial.y) * factor;
wlr_scene_node_set_position(&c->scene->node, x, y);
c->animation.current = (struct wlr_box){
@ -705,7 +723,7 @@ void client_animation_next_tick(Client *c) {
c->is_pending_open_animation = false;
if (animation_passed == 1.0) {
if (animation_passed >= 1.0) {
// clear the open action state
// To prevent him from being mistaken that
@ -733,8 +751,6 @@ void client_animation_next_tick(Client *c) {
// end flush in next frame, not the current frame
c->need_output_flush = false;
} else {
c->animation.passed_frames++;
}
client_apply_clip(c, factor);
@ -756,76 +772,74 @@ void init_fadeout_client(Client *c) {
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);
client_set_border_color(c, bordercolor);
fadeout_cient->scene =
fadeout_client->scene =
wlr_scene_tree_snapshot(&c->scene->node, layers[LyrFadeOut]);
wlr_scene_node_set_enabled(&c->scene->node, false);
if (!fadeout_cient->scene) {
free(fadeout_cient);
if (!fadeout_client->scene) {
free(fadeout_client);
return;
}
fadeout_cient->animation.duration = animation_duration_close;
fadeout_cient->geom = fadeout_cient->current =
fadeout_cient->animainit_geom = fadeout_cient->animation.initial =
fadeout_client->animation.duration = animation_duration_close;
fadeout_client->geom = fadeout_client->current =
fadeout_client->animainit_geom = fadeout_client->animation.initial =
c->animation.current;
fadeout_cient->mon = c->mon;
fadeout_cient->animation_type_close = c->animation_type_close;
fadeout_cient->animation.action = CLOSE;
fadeout_cient->bw = c->bw;
fadeout_cient->nofadeout = c->nofadeout;
fadeout_client->mon = c->mon;
fadeout_client->animation_type_close = c->animation_type_close;
fadeout_client->animation.action = CLOSE;
fadeout_client->bw = c->bw;
fadeout_client->nofadeout = c->nofadeout;
// 这里snap节点的坐标设置是使用的相对坐标所以不能加上原来坐标
// 这跟普通node有区别
fadeout_cient->animation.initial.x = 0;
fadeout_cient->animation.initial.y = 0;
fadeout_client->animation.initial.x = 0;
fadeout_client->animation.initial.y = 0;
if ((!c->animation_type_close &&
strcmp(animation_type_close, "fade") == 0) ||
(c->animation_type_close &&
strcmp(c->animation_type_close, "fade") == 0)) {
fadeout_cient->current.x = 0;
fadeout_cient->current.y = 0;
fadeout_cient->current.width = 0;
fadeout_cient->current.height = 0;
fadeout_client->current.x = 0;
fadeout_client->current.y = 0;
fadeout_client->current.width = 0;
fadeout_client->current.height = 0;
} else if ((c->animation_type_close &&
strcmp(c->animation_type_close, "slide") == 0) ||
(!c->animation_type_close &&
strcmp(animation_type_close, "slide") == 0)) {
fadeout_cient->current.y =
fadeout_client->current.y =
c->geom.y + c->geom.height / 2 > c->mon->m.y + c->mon->m.height / 2
? c->mon->m.height -
(c->animation.current.y - c->mon->m.y) // down out
: c->mon->m.y - c->geom.height; // up out
fadeout_cient->current.x = 0; // x无偏差垂直划出
fadeout_client->current.x = 0; // x无偏差垂直划出
} else {
fadeout_cient->current.y =
(fadeout_cient->geom.height -
fadeout_cient->geom.height * zoom_end_ratio) /
fadeout_client->current.y =
(fadeout_client->geom.height -
fadeout_client->geom.height * zoom_end_ratio) /
2;
fadeout_cient->current.x =
(fadeout_cient->geom.width -
fadeout_cient->geom.width * zoom_end_ratio) /
fadeout_client->current.x =
(fadeout_client->geom.width -
fadeout_client->geom.width * zoom_end_ratio) /
2;
fadeout_cient->current.width =
fadeout_cient->geom.width * zoom_end_ratio;
fadeout_cient->current.height =
fadeout_cient->geom.height * zoom_end_ratio;
fadeout_client->current.width =
fadeout_client->geom.width * zoom_end_ratio;
fadeout_client->current.height =
fadeout_client->geom.height * zoom_end_ratio;
}
fadeout_cient->animation.passed_frames = 0;
fadeout_cient->animation.total_frames =
fadeout_cient->animation.duration / output_frame_duration_ms();
wlr_scene_node_set_enabled(&fadeout_cient->scene->node, true);
wl_list_insert(&fadeout_clients, &fadeout_cient->fadeout_link);
fadeout_client->animation.time_started = get_now_in_ms();
wlr_scene_node_set_enabled(&fadeout_client->scene->node, true);
wl_list_insert(&fadeout_clients, &fadeout_client->fadeout_link);
// 请求刷新屏幕
wlr_output_schedule_frame(c->mon->wlr_output);
request_fresh_all_monitors();
}
void client_commit(Client *c) {
@ -837,30 +851,21 @@ void client_commit(Client *c) {
}
c->animation.initial = c->animainit_geom;
// 设置动画速度
c->animation.passed_frames = 0;
c->animation.total_frames =
c->animation.duration / output_frame_duration_ms();
c->animation.time_started = get_now_in_ms();
// 标记动画开始
c->animation.running = true;
c->animation.should_animate = false;
} else {
// 如果动画没有开始,且被判定为不应该动画,
// 则设置总帧数为1,不然其他地方一旦获取动画
// 进度,总帧数作为分母会造成除零
// 比如动画类型为none的时候
if (!c->animation.running) {
c->animation.passed_frames = 1;
c->animation.total_frames = 1;
}
}
// 请求刷新屏幕
wlr_output_schedule_frame(c->mon->wlr_output);
request_fresh_all_monitors();
}
void client_set_pending_state(Client *c) {
if (!c || c->iskilling)
return;
// 判断是否需要动画
if (!animations) {
c->animation.should_animate = false;
@ -879,7 +884,22 @@ void client_set_pending_state(Client *c) {
(!c->animation_type_open &&
strcmp(animation_type_open, "none") == 0)) &&
c->animation.action == OPEN) {
c->animation.duration = 0;
}
if (c->istagswitching) {
c->animation.duration = 0;
c->istagswitching = 0;
}
if (start_drag_window) {
c->animation.should_animate = false;
c->animation.duration = 0;
}
if (c->isnoanimation) {
c->animation.should_animate = false;
c->animation.duration = 0;
}
// 开始动画
@ -887,7 +907,7 @@ void client_set_pending_state(Client *c) {
c->dirty = true;
}
void resize(Client *c, struct wlr_box geo, int interact) {
void resize(Client *c, struct wlr_box geo, int32_t interact) {
// 动画设置的起始函数,这里用来计算一些动画的起始值
// 动画起始位置大小是由于c->animainit_geom确定的
@ -902,24 +922,27 @@ void resize(Client *c, struct wlr_box geo, int interact) {
return;
c->need_output_flush = true;
c->dirty = true;
// oldgeom = c->geom;
// float_geom = c->geom;
bbox = (interact || c->isfloating || c->isfullscreen) ? &sgeom : &c->mon->w;
if (is_scroller_layout(c->mon) && (!c->isfloating || c == grabc)) {
c->geom = geo;
c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width);
c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height);
c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width);
c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height);
} else { // 这里会限制不允许窗口划出屏幕
client_set_bounds(
c, geo.width,
geo.height); // 去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常
c->geom = geo;
applybounds(
c,
bbox); // 去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常
}
if (!c->isnosizehint && !c->ismaximizescreen && !c->isfullscreen &&
c->isfloating) {
client_set_size_bound(c);
}
if (!c->is_pending_open_animation) {
c->animation.begin_fade_in = false;
}
@ -948,7 +971,7 @@ void resize(Client *c, struct wlr_box geo, int interact) {
c->animainit_geom.height = c->animation.current.height;
c->animainit_geom.width = c->animation.current.width;
} else if (c->is_pending_open_animation) {
set_client_open_animaiton(c, c->geom);
set_client_open_animation(c, c->geom);
} else {
c->animainit_geom = c->animation.current;
}
@ -957,13 +980,24 @@ void resize(Client *c, struct wlr_box geo, int interact) {
c->bw = 0;
}
bool hit_no_border = check_hit_no_border(c);
if (hit_no_border && smartgaps) {
c->bw = 0;
c->fake_no_border = true;
}
// c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局
c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
c->geom.height - 2 * c->bw);
if (c->configure_serial != 0) {
c->mon->resizing_count_pending++;
}
if (c == grabc) {
c->animation.running = false;
c->need_output_flush = false;
c->animainit_geom = c->current = c->pending = c->animation.current =
c->geom;
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
@ -993,6 +1027,10 @@ void resize(Client *c, struct wlr_box geo, int interact) {
c->animainit_geom = c->geom;
}
if (c->scratchpad_switching_mon && c->isfloating) {
c->animainit_geom = c->geom;
}
// 开始应用动画设置
client_set_pending_state(c);
@ -1007,22 +1045,148 @@ bool client_draw_fadeout_frame(Client *c) {
return true;
}
void client_set_focused_opacity_animation(Client *c) {
float *border_color = get_border_color(c);
if (!animations) {
setborder_color(c);
return;
}
c->opacity_animation.duration = animation_duration_focus;
memcpy(c->opacity_animation.target_border_color, border_color,
sizeof(c->opacity_animation.target_border_color));
c->opacity_animation.target_opacity = c->focused_opacity;
c->opacity_animation.time_started = get_now_in_ms();
memcpy(c->opacity_animation.initial_border_color,
c->opacity_animation.current_border_color,
sizeof(c->opacity_animation.initial_border_color));
c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity;
c->opacity_animation.running = true;
}
void client_set_unfocused_opacity_animation(Client *c) {
// Start border color animation to unfocused
float *border_color = get_border_color(c);
if (!animations) {
setborder_color(c);
return;
}
c->opacity_animation.duration = animation_duration_focus;
memcpy(c->opacity_animation.target_border_color, border_color,
sizeof(c->opacity_animation.target_border_color));
// Start opacity animation to unfocused
c->opacity_animation.target_opacity = c->unfocused_opacity;
c->opacity_animation.time_started = get_now_in_ms();
memcpy(c->opacity_animation.initial_border_color,
c->opacity_animation.current_border_color,
sizeof(c->opacity_animation.initial_border_color));
c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity;
c->opacity_animation.running = true;
}
bool client_apply_focus_opacity(Client *c) {
// Animate focus transitions (opacity + border color)
float *border_color = get_border_color(c);
if (c->isfullscreen) {
c->opacity_animation.running = false;
client_set_opacity(c, 1);
} else if (c->animation.running && c->animation.action == OPEN) {
c->opacity_animation.running = false;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
double linear_progress =
c->animation.duration
? (double)passed_time / (double)c->animation.duration
: 1.0;
double opacity_eased_progress =
find_animation_curve_at(linear_progress, OPAFADEIN);
float percent =
animation_fade_in && !c->nofadein ? opacity_eased_progress : 1.0;
float opacity =
c == selmon->sel ? c->focused_opacity : c->unfocused_opacity;
float target_opacity =
percent * (1.0 - fadein_begin_opacity) + fadein_begin_opacity;
if (target_opacity > opacity) {
target_opacity = opacity;
}
memcpy(c->opacity_animation.current_border_color,
c->opacity_animation.target_border_color,
sizeof(c->opacity_animation.current_border_color));
c->opacity_animation.current_opacity = target_opacity;
client_set_opacity(c, target_opacity);
client_set_border_color(c, c->opacity_animation.target_border_color);
} else if (animations && c->opacity_animation.running) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int32_t passed_time =
timespec_to_ms(&now) - c->opacity_animation.time_started;
double linear_progress =
c->opacity_animation.duration
? (double)passed_time / (double)c->opacity_animation.duration
: 1.0;
float eased_progress = find_animation_curve_at(linear_progress, FOCUS);
c->opacity_animation.current_opacity =
c->opacity_animation.initial_opacity +
(c->opacity_animation.target_opacity -
c->opacity_animation.initial_opacity) *
eased_progress;
client_set_opacity(c, c->opacity_animation.current_opacity);
// Animate border color
for (int32_t i = 0; i < 4; i++) {
c->opacity_animation.current_border_color[i] =
c->opacity_animation.initial_border_color[i] +
(c->opacity_animation.target_border_color[i] -
c->opacity_animation.initial_border_color[i]) *
eased_progress;
}
client_set_border_color(c, c->opacity_animation.current_border_color);
if (linear_progress >= 1.0f) {
c->opacity_animation.running = false;
} else {
return true;
}
} else if (c == selmon->sel) {
c->opacity_animation.running = false;
c->opacity_animation.current_opacity = c->focused_opacity;
memcpy(c->opacity_animation.current_border_color, border_color,
sizeof(c->opacity_animation.current_border_color));
client_set_opacity(c, c->focused_opacity);
} else {
c->opacity_animation.running = false;
c->opacity_animation.current_opacity = c->unfocused_opacity;
memcpy(c->opacity_animation.current_border_color, border_color,
sizeof(c->opacity_animation.current_border_color));
client_set_opacity(c, c->unfocused_opacity);
}
return false;
}
bool client_draw_frame(Client *c) {
if (!c || !client_surface(c)->mapped)
return false;
if (c->isfullscreen) {
client_set_opacity(c, 1);
} else if (c == selmon->sel && !c->animation.running) {
client_set_opacity(c, c->focused_opacity);
} else if (!c->animation.running) {
client_set_opacity(c, c->unfocused_opacity);
if (!c->need_output_flush) {
return client_apply_focus_opacity(c);
}
if (!c->need_output_flush)
return false;
if (animations && c->animation.running) {
client_animation_next_tick(c);
} else {
@ -1033,5 +1197,6 @@ bool client_draw_frame(Client *c) {
client_apply_clip(c, 1.0);
c->need_output_flush = false;
}
client_apply_focus_opacity(c);
return true;
}

View file

@ -1,4 +1,4 @@
struct dvec2 calculate_animation_curve_at(double t, int type) {
struct dvec2 calculate_animation_curve_at(double t, int32_t type) {
struct dvec2 point;
double *animation_curve;
if (type == MOVE) {
@ -9,6 +9,12 @@ struct dvec2 calculate_animation_curve_at(double t, int type) {
animation_curve = animation_curve_tag;
} else if (type == CLOSE) {
animation_curve = animation_curve_close;
} else if (type == FOCUS) {
animation_curve = animation_curve_focus;
} else if (type == OPAFADEIN) {
animation_curve = animation_curve_opafadein;
} else if (type == OPAFADEOUT) {
animation_curve = animation_curve_opafadeout;
} else {
animation_curve = animation_curve_move;
}
@ -28,30 +34,48 @@ void init_baked_points(void) {
baked_points_tag = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_tag));
baked_points_close =
calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_close));
baked_points_focus =
calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_focus));
baked_points_opafadein =
calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_opafadein));
baked_points_opafadeout =
calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_opafadeout));
for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) {
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_move[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), MOVE);
}
for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) {
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_open[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), OPEN);
}
for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) {
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_tag[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), TAG);
}
for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) {
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_close[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), CLOSE);
}
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_focus[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), FOCUS);
}
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_opafadein[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), OPAFADEIN);
}
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_opafadeout[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), OPAFADEOUT);
}
}
double find_animation_curve_at(double t, int type) {
unsigned int down = 0;
unsigned int up = BAKED_POINTS_COUNT - 1;
double find_animation_curve_at(double t, int32_t type) {
int32_t down = 0;
int32_t up = BAKED_POINTS_COUNT - 1;
unsigned int middle = (up + down) / 2;
int32_t middle = (up + down) / 2;
struct dvec2 *baked_points;
if (type == MOVE) {
baked_points = baked_points_move;
@ -61,6 +85,12 @@ double find_animation_curve_at(double t, int type) {
baked_points = baked_points_tag;
} else if (type == CLOSE) {
baked_points = baked_points_close;
} else if (type == FOCUS) {
baked_points = baked_points_focus;
} else if (type == OPAFADEIN) {
baked_points = baked_points_opafadein;
} else if (type == OPAFADEOUT) {
baked_points = baked_points_opafadeout;
} else {
baked_points = baked_points_move;
}
@ -76,19 +106,8 @@ double find_animation_curve_at(double t, int type) {
return baked_points[up].y;
}
double output_frame_duration_ms() {
int32_t refresh_total = 0;
Monitor *m;
wl_list_for_each(m, &mons, link) {
if (!m->wlr_output->enabled) {
continue;
}
refresh_total += m->wlr_output->refresh;
}
return 1000000.0 / refresh_total;
}
static bool scene_node_snapshot(struct wlr_scene_node *node, int lx, int ly,
static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx,
int32_t ly,
struct wlr_scene_tree *snapshot_tree) {
if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) {
return true;
@ -230,4 +249,14 @@ struct wlr_scene_tree *wlr_scene_tree_snapshot(struct wlr_scene_node *node,
wlr_scene_node_set_enabled(&snapshot->node, true);
return snapshot;
}
void request_fresh_all_monitors(void) {
Monitor *m = NULL;
wl_list_for_each(m, &mons, link) {
if (!m->wlr_output->enabled) {
continue;
}
wlr_output_schedule_frame(m->wlr_output);
}
}

View file

@ -1,5 +1,4 @@
void layer_actual_size(LayerSurface *l, unsigned int *width,
unsigned int *height) {
void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height) {
struct wlr_box box;
if (l->animation.running) {
@ -43,7 +42,7 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) {
.height = state->desired_height};
// 水平方向定位
const uint32_t both_horiz =
const int32_t both_horiz =
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
if (box.width == 0) {
box.x = bounds.x;
@ -58,7 +57,7 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) {
}
// 垂直方向定位
const uint32_t both_vert =
const int32_t both_vert =
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
if (box.height == 0) {
box.y = bounds.y;
@ -102,10 +101,10 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) {
}
void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) {
int slide_direction;
int horizontal, horizontal_value;
int vertical, vertical_value;
int center_x, center_y;
int32_t slide_direction;
int32_t horizontal, horizontal_value;
int32_t vertical, vertical_value;
int32_t center_x, center_y;
if (!l)
return;
@ -162,10 +161,10 @@ void layer_draw_shadow(LayerSurface *l) {
return;
}
uint32_t width, height;
int32_t width, height;
layer_actual_size(l, &width, &height);
uint32_t delta = shadows_size;
int32_t delta = shadows_size;
/* we calculate where to clip the shadow */
struct wlr_box layer_box = {
@ -201,8 +200,8 @@ void layer_draw_shadow(LayerSurface *l) {
wlr_scene_shadow_set_clipped_region(l->shadow, clipped_region);
}
void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx,
int sy, void *data) {
void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer,
int32_t sx, int32_t sy, void *data) {
BufferData *buffer_data = (BufferData *)data;
struct wlr_scene_surface *scene_surface =
@ -213,9 +212,8 @@ void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx,
struct wlr_surface *surface = scene_surface->surface;
unsigned int surface_width =
surface->current.width * buffer_data->width_scale;
unsigned int surface_height =
int32_t surface_width = surface->current.width * buffer_data->width_scale;
int32_t surface_height =
surface->current.height * buffer_data->height_scale;
if (surface_height > 0 && surface_width > 0) {
@ -224,7 +222,8 @@ void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx,
}
void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer,
int sx, int sy, void *data) {
int32_t sx, int32_t sy,
void *data) {
BufferData *buffer_data = (BufferData *)data;
wlr_scene_buffer_set_dest_size(buffer, buffer_data->width,
buffer_data->height);
@ -234,21 +233,26 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) {
if (!l)
return;
double animation_passed =
(double)l->animation.passed_frames / l->animation.total_frames;
int type = l->animation.action = l->animation.action;
double factor = find_animation_curve_at(animation_passed, type);
unsigned int width =
l->animation.initial.width +
(l->current.width - l->animation.initial.width) * factor;
unsigned int height =
l->animation.initial.height +
(l->current.height - l->animation.initial.height) * factor;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
unsigned int x = l->animation.initial.x +
(l->current.x - l->animation.initial.x) * factor;
unsigned int y = l->animation.initial.y +
(l->current.y - l->animation.initial.y) * factor;
int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started;
double animation_passed =
l->animation.duration
? (double)passed_time / (double)l->animation.duration
: 1.0;
int32_t type = l->animation.action = l->animation.action;
double factor = find_animation_curve_at(animation_passed, type);
int32_t width = l->animation.initial.width +
(l->current.width - l->animation.initial.width) * factor;
int32_t height = l->animation.initial.height +
(l->current.height - l->animation.initial.height) * factor;
int32_t x = l->animation.initial.x +
(l->current.x - l->animation.initial.x) * factor;
int32_t y = l->animation.initial.y +
(l->current.y - l->animation.initial.y) * factor;
wlr_scene_node_set_position(&l->scene->node, x, y);
@ -272,19 +276,23 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) {
.height = height,
};
double opacity = MAX(fadeout_begin_opacity - animation_passed, 0.0f);
double opacity_eased_progress =
find_animation_curve_at(animation_passed, OPAFADEOUT);
double percent = fadeout_begin_opacity -
(opacity_eased_progress * fadeout_begin_opacity);
double opacity = MAX(percent, 0.0f);
if (animation_fade_out)
wlr_scene_node_for_each_buffer(&l->scene->node,
scene_buffer_apply_opacity, &opacity);
if (animation_passed == 1.0) {
if (animation_passed >= 1.0) {
wl_list_remove(&l->fadeout_link);
wlr_scene_node_destroy(&l->scene->node);
free(l);
l = NULL;
} else {
l->animation.passed_frames++;
}
}
@ -293,25 +301,35 @@ void layer_animation_next_tick(LayerSurface *l) {
if (!l || !l->mapped)
return;
double animation_passed =
(double)l->animation.passed_frames / l->animation.total_frames;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int type = l->animation.action == NONE ? MOVE : l->animation.action;
int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started;
double animation_passed =
l->animation.duration
? (double)passed_time / (double)l->animation.duration
: 1.0;
int32_t type = l->animation.action == NONE ? MOVE : l->animation.action;
double factor = find_animation_curve_at(animation_passed, type);
unsigned int width =
l->animation.initial.width +
(l->current.width - l->animation.initial.width) * factor;
unsigned int height =
l->animation.initial.height +
(l->current.height - l->animation.initial.height) * factor;
int32_t width = l->animation.initial.width +
(l->current.width - l->animation.initial.width) * factor;
int32_t height = l->animation.initial.height +
(l->current.height - l->animation.initial.height) * factor;
unsigned int x = l->animation.initial.x +
(l->current.x - l->animation.initial.x) * factor;
unsigned int y = l->animation.initial.y +
(l->current.y - l->animation.initial.y) * factor;
int32_t x = l->animation.initial.x +
(l->current.x - l->animation.initial.x) * factor;
int32_t y = l->animation.initial.y +
(l->current.y - l->animation.initial.y) * factor;
double opacity = MIN(fadein_begin_opacity + animation_passed, 1.0f);
double opacity_eased_progress =
find_animation_curve_at(animation_passed, OPAFADEIN);
double opacity =
MIN(fadein_begin_opacity +
opacity_eased_progress * (1.0 - fadein_begin_opacity),
1.0f);
if (animation_fade_in)
wlr_scene_node_for_each_buffer(&l->scene->node,
@ -343,12 +361,10 @@ void layer_animation_next_tick(LayerSurface *l) {
.height = height,
};
if (animation_passed == 1.0) {
if (animation_passed >= 1.0) {
l->animation.running = false;
l->need_output_flush = false;
l->animation.action = MOVE;
} else {
l->animation.passed_frames++;
}
}
@ -442,17 +458,16 @@ void init_fadeout_layers(LayerSurface *l) {
fadeout_layer->current.height = 0;
}
// 计算动画帧数
fadeout_layer->animation.passed_frames = 0;
fadeout_layer->animation.total_frames =
fadeout_layer->animation.duration / output_frame_duration_ms();
// 动画开始时间
fadeout_layer->animation.time_started = get_now_in_ms();
// 将节点插入到关闭动画链表中,屏幕刷新哪里会检查链表中是否有节点可以应用于动画
wlr_scene_node_set_enabled(&fadeout_layer->scene->node, true);
wl_list_insert(&fadeout_layers, &fadeout_layer->fadeout_link);
// 请求刷新屏幕
wlr_output_schedule_frame(l->mon->wlr_output);
if (l->mon)
wlr_output_schedule_frame(l->mon->wlr_output);
}
void layer_set_pending_state(LayerSurface *l) {
@ -528,26 +543,15 @@ void layer_commit(LayerSurface *l) {
}
l->animation.initial = l->animainit_geom;
// 设置动画速度
l->animation.passed_frames = 0;
l->animation.total_frames =
l->animation.duration / output_frame_duration_ms();
l->animation.time_started = get_now_in_ms();
// 标记动画开始
l->animation.running = true;
l->animation.should_animate = false;
} else {
// 如果动画没有开始,且被判定为不应该动画,
// 则设置总帧数为1,不然其他地方一旦获取动画
// 进度,总帧数作为分母会造成除零
// 比如动画类型为none的时候
if (!l->animation.running) {
l->animation.passed_frames = 1;
l->animation.total_frames = 1;
}
}
// 请求刷新屏幕
wlr_output_schedule_frame(l->mon->wlr_output);
if (l->mon)
wlr_output_schedule_frame(l->mon->wlr_output);
}
bool layer_draw_frame(LayerSurface *l) {

93
src/animation/tag.h Normal file
View file

@ -0,0 +1,93 @@
void set_tagin_animation(Monitor *m, Client *c) {
if (c->animation.running) {
c->animainit_geom.x = c->animation.current.x;
c->animainit_geom.y = c->animation.current.y;
return;
}
if (m->pertag->curtag > m->pertag->prevtag) {
c->animainit_geom.x = tag_animation_direction == VERTICAL
? c->animation.current.x
: MAX(c->mon->m.x + c->mon->m.width,
c->geom.x + c->mon->m.width);
c->animainit_geom.y = tag_animation_direction == VERTICAL
? MAX(c->mon->m.y + c->mon->m.height,
c->geom.y + c->mon->m.height)
: c->animation.current.y;
} else {
c->animainit_geom.x =
tag_animation_direction == VERTICAL
? c->animation.current.x
: MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width);
c->animainit_geom.y =
tag_animation_direction == VERTICAL
? MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height)
: c->animation.current.y;
}
}
void set_arrange_visible(Monitor *m, Client *c, bool want_animation) {
if (!c->is_clip_to_hide || !ISTILED(c) || !is_scroller_layout(c->mon)) {
c->is_clip_to_hide = false;
wlr_scene_node_set_enabled(&c->scene->node, true);
wlr_scene_node_set_enabled(&c->scene_surface->node, true);
}
client_set_suspended(c, false);
if (!c->animation.tag_from_rule && want_animation &&
m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) {
c->animation.tagining = true;
set_tagin_animation(m, c);
} else {
c->animainit_geom.x = c->animation.current.x;
c->animainit_geom.y = c->animation.current.y;
}
c->animation.tag_from_rule = false;
c->animation.tagouting = false;
c->animation.tagouted = false;
resize(c, c->geom, 0);
}
void set_tagout_animation(Monitor *m, Client *c) {
if (m->pertag->curtag > m->pertag->prevtag) {
c->pending = c->geom;
c->pending.x =
tag_animation_direction == VERTICAL
? c->animation.current.x
: MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width);
c->pending.y = tag_animation_direction == VERTICAL
? MIN(c->mon->m.y - c->geom.height,
c->geom.y - c->mon->m.height)
: c->animation.current.y;
resize(c, c->geom, 0);
} else {
c->pending = c->geom;
c->pending.x = tag_animation_direction == VERTICAL
? c->animation.current.x
: MAX(c->mon->m.x + c->mon->m.width,
c->geom.x + c->mon->m.width);
c->pending.y = tag_animation_direction == VERTICAL
? MAX(c->mon->m.y + c->mon->m.height,
c->geom.y + c->mon->m.height)
: c->animation.current.y;
resize(c, c->geom, 0);
}
}
void set_arrange_hidden(Monitor *m, Client *c, bool want_animation) {
if ((c->tags & (1 << (m->pertag->prevtag - 1))) &&
m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) {
c->animation.tagouting = true;
c->animation.tagining = false;
set_tagout_animation(m, c);
} else {
wlr_scene_node_set_enabled(&c->scene->node, false);
client_set_suspended(c, true);
}
}

View file

@ -6,7 +6,7 @@
*/
/* Leave these functions first; they're used in the others */
static inline int client_is_x11(Client *c) {
static inline int32_t client_is_x11(Client *c) {
#ifdef XWAYLAND
return c->type == X11;
#endif
@ -21,14 +21,15 @@ static inline struct wlr_surface *client_surface(Client *c) {
return c->surface.xdg->surface;
}
static inline int toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc,
LayerSurface **pl) {
static inline int32_t toplevel_from_wlr_surface(struct wlr_surface *s,
Client **pc,
LayerSurface **pl) {
struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface;
struct wlr_surface *root_surface;
struct wlr_layer_surface_v1 *layer_surface;
Client *c = NULL;
LayerSurface *l = NULL;
int type = -1;
int32_t type = -1;
#ifdef XWAYLAND
struct wlr_xwayland_surface *xsurface;
#endif
@ -88,11 +89,13 @@ end:
/* The others */
static inline void client_activate_surface(struct wlr_surface *s,
int activated) {
int32_t activated) {
struct wlr_xdg_toplevel *toplevel;
#ifdef XWAYLAND
struct wlr_xwayland_surface *xsurface;
if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
if (activated && xsurface->minimized)
wlr_xwayland_surface_set_minimized(xsurface, false);
wlr_xwayland_surface_activate(xsurface, activated);
return;
}
@ -101,24 +104,6 @@ static inline void client_activate_surface(struct wlr_surface *s,
wlr_xdg_toplevel_set_activated(toplevel, activated);
}
static inline uint32_t client_set_bounds(Client *c, int32_t width,
int32_t height) {
#ifdef XWAYLAND
if (client_is_x11(c))
return 0;
#endif
if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION &&
width >= 0 && height >= 0 &&
(c->bounds.width != width || c->bounds.height != height)) {
c->bounds.width = width;
c->bounds.height = height;
return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width,
height);
}
return 0;
}
static inline const char *client_get_appid(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c))
@ -129,7 +114,7 @@ static inline const char *client_get_appid(Client *c) {
: "broken";
}
static inline int client_get_pid(Client *c) {
static inline int32_t client_get_pid(Client *c) {
pid_t pid;
#ifdef XWAYLAND
if (client_is_x11(c))
@ -143,8 +128,8 @@ static inline void client_get_clip(Client *c, struct wlr_box *clip) {
*clip = (struct wlr_box){
.x = 0,
.y = 0,
.width = c->geom.width - c->bw,
.height = c->geom.height - c->bw,
.width = c->geom.width - 2 * c->bw,
.height = c->geom.height - 2 * c->bw,
};
#ifdef XWAYLAND
@ -185,7 +170,7 @@ static inline Client *client_get_parent(Client *c) {
return p;
}
static inline int client_has_children(Client *c) {
static inline int32_t client_has_children(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c))
return !wl_list_empty(&c->surface.xwayland->children);
@ -205,7 +190,7 @@ static inline const char *client_get_title(Client *c) {
: "broken";
}
static inline int client_is_float_type(Client *c) {
static inline int32_t client_is_float_type(Client *c) {
struct wlr_xdg_toplevel *toplevel;
struct wlr_xdg_toplevel_state state;
@ -213,6 +198,10 @@ static inline int client_is_float_type(Client *c) {
if (client_is_x11(c)) {
struct wlr_xwayland_surface *surface = c->surface.xwayland;
xcb_size_hints_t *size_hints = surface->size_hints;
if (!size_hints)
return 0;
if (surface->modal)
return 1;
@ -241,12 +230,12 @@ static inline int client_is_float_type(Client *c) {
state.min_height == state.max_height));
}
static inline int client_is_rendered_on_mon(Client *c, Monitor *m) {
static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) {
/* This is needed for when you don't want to check formal assignment,
* but rather actual displaying of the pixels.
* Usually VISIBLEON suffices and is also faster. */
struct wlr_surface_output *s;
int unused_lx, unused_ly;
int32_t unused_lx, unused_ly;
if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly))
return 0;
wl_list_for_each(s, &client_surface(c)->current_outputs,
@ -254,32 +243,7 @@ static inline int client_is_rendered_on_mon(Client *c, Monitor *m) {
return 0;
}
static inline int client_is_stopped(Client *c) {
int 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 int client_is_unmanaged(Client *c) {
static inline int32_t client_is_unmanaged(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c))
return c->surface.xwayland->override_redirect;
@ -311,7 +275,7 @@ static inline void client_set_border_color(Client *c,
wlr_scene_rect_set_color(c->border, color);
}
static inline void client_set_fullscreen(Client *c, int fullscreen) {
static inline void client_set_fullscreen(Client *c, int32_t fullscreen) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
@ -330,9 +294,24 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
uint32_t height) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
struct wlr_surface_state *state =
&c->surface.xwayland->surface->current;
if ((int32_t)c->geom.width - 2 * (int32_t)c->bw ==
(int32_t)state->width &&
(int32_t)c->geom.height - 2 * (int32_t)c->bw ==
(int32_t)state->height &&
(int32_t)c->surface.xwayland->x ==
(int32_t)c->geom.x + (int32_t)c->bw &&
(int32_t)c->surface.xwayland->y ==
(int32_t)c->geom.y + (int32_t)c->bw) {
return 0;
}
wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw,
c->geom.y + c->bw, width, height);
return 0;
return 1;
}
#endif
if ((int32_t)width == c->surface.xdg->toplevel->current.width &&
@ -342,25 +321,56 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
(int32_t)height);
}
static inline void client_set_tiled(Client *c, uint32_t edges) {
static inline void client_set_minimized(Client *c, bool minimized) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
wlr_xwayland_surface_set_minimized(c->surface.xwayland, minimized);
return;
}
#endif
return;
}
static inline void client_set_maximized(Client *c, bool maximized) {
struct wlr_xdg_toplevel *toplevel;
#ifdef XWAYLAND
if (client_is_x11(c)) {
wlr_xwayland_surface_set_maximized(c->surface.xwayland, maximized,
maximized);
return;
}
#endif
toplevel = c->surface.xdg->toplevel;
wlr_xdg_toplevel_set_maximized(toplevel, maximized);
return;
}
static inline void client_set_tiled(Client *c, uint32_t edges) {
struct wlr_xdg_toplevel *toplevel;
#ifdef XWAYLAND
if (client_is_x11(c) && c->force_maximize) {
wlr_xwayland_surface_set_maximized(c->surface.xwayland,
edges != WLR_EDGE_NONE,
edges != WLR_EDGE_NONE);
return;
}
#endif
toplevel = c->surface.xdg->toplevel;
if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) {
wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges);
} else {
wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel,
edges != WLR_EDGE_NONE);
}
if (c->force_maximize) {
wlr_xdg_toplevel_set_maximized(toplevel, edges != WLR_EDGE_NONE);
}
}
static inline void client_set_suspended(Client *c, int suspended) {
static inline void client_set_suspended(Client *c, int32_t suspended) {
#ifdef XWAYLAND
if (client_is_x11(c))
return;
@ -369,7 +379,22 @@ static inline void client_set_suspended(Client *c, int suspended) {
wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended);
}
static inline int client_should_ignore_focus(Client *c) {
static inline int32_t client_should_ignore_focus(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
struct wlr_xwayland_surface *surface = c->surface.xwayland;
if (!surface->hints)
return 0;
return !surface->hints->input;
}
#endif
return 0;
}
static inline int32_t client_is_x11_popup(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
@ -398,7 +423,7 @@ static inline int client_should_ignore_focus(Client *c) {
return 0;
}
static inline int client_should_global(Client *c) {
static inline int32_t client_should_global(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
@ -411,7 +436,7 @@ static inline int client_should_global(Client *c) {
return 0;
}
static inline int client_should_overtop(Client *c) {
static inline int32_t client_should_overtop(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
@ -423,7 +448,7 @@ static inline int client_should_overtop(Client *c) {
return 0;
}
static inline int client_wants_focus(Client *c) {
static inline int32_t client_wants_focus(Client *c) {
#ifdef XWAYLAND
return client_is_unmanaged(c) &&
wlr_xwayland_surface_override_redirect_wants_focus(
@ -434,10 +459,82 @@ static inline int client_wants_focus(Client *c) {
return 0;
}
static inline int client_wants_fullscreen(Client *c) {
static inline int32_t client_wants_fullscreen(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c))
return c->surface.xwayland->fullscreen;
#endif
return c->surface.xdg->toplevel->requested.fullscreen;
}
static inline bool client_request_minimize(Client *c, void *data) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
struct wlr_xwayland_minimize_event *event = data;
return event->minimize;
}
#endif
return c->surface.xdg->toplevel->requested.minimized;
}
static inline bool client_request_maximize(Client *c, void *data) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
struct wlr_xwayland_surface *surface = c->surface.xwayland;
return surface->maximized_vert || surface->maximized_horz;
}
#endif
return c->surface.xdg->toplevel->requested.maximized;
}
static inline void client_set_size_bound(Client *c) {
struct wlr_xdg_toplevel *toplevel;
struct wlr_xdg_toplevel_state state;
#ifdef XWAYLAND
if (client_is_x11(c)) {
struct wlr_xwayland_surface *surface = c->surface.xwayland;
xcb_size_hints_t *size_hints = surface->size_hints;
if (!size_hints)
return;
if ((uint32_t)c->geom.width - 2 * c->bw < size_hints->min_width &&
size_hints->min_width > 0)
c->geom.width = size_hints->min_width + 2 * c->bw;
if ((uint32_t)c->geom.height - 2 * c->bw < size_hints->min_height &&
size_hints->min_height > 0)
c->geom.height = size_hints->min_height + 2 * c->bw;
if ((uint32_t)c->geom.width - 2 * c->bw > size_hints->max_width &&
size_hints->max_width > 0)
c->geom.width = size_hints->max_width + 2 * c->bw;
if ((uint32_t)c->geom.height - 2 * c->bw > size_hints->max_height &&
size_hints->max_height > 0)
c->geom.height = size_hints->max_height + 2 * c->bw;
return;
}
#endif
toplevel = c->surface.xdg->toplevel;
state = toplevel->current;
if ((uint32_t)c->geom.width - 2 * c->bw < state.min_width &&
state.min_width > 0) {
c->geom.width = state.min_width + 2 * c->bw;
}
if ((uint32_t)c->geom.height - 2 * c->bw < state.min_height &&
state.min_height > 0) {
c->geom.height = state.min_height + 2 * c->bw;
}
if ((uint32_t)c->geom.width - 2 * c->bw > state.max_width &&
state.max_width > 0) {
c->geom.width = state.max_width + 2 * c->bw;
}
if ((uint32_t)c->geom.height - 2 * c->bw > state.max_height &&
state.max_height > 0) {
c->geom.height = state.max_height + 2 * c->bw;
}
}

View file

@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "util.h"
@ -35,8 +36,8 @@ void *ecalloc(size_t nmemb, size_t size) {
return p;
}
int fd_set_nonblock(int fd) {
int flags = fcntl(fd, F_GETFL);
int32_t fd_set_nonblock(int32_t fd) {
int32_t flags = fcntl(fd, F_GETFL);
if (flags < 0) {
perror("fcntl(F_GETFL):");
return -1;
@ -49,8 +50,8 @@ int fd_set_nonblock(int fd) {
return 0;
}
int regex_match(const char *pattern, const char *str) {
int errnum;
int32_t regex_match(const char *pattern, const char *str) {
int32_t errnum;
PCRE2_SIZE erroffset;
if (!pattern || !str) {
@ -69,10 +70,107 @@ int regex_match(const char *pattern, const char *str) {
pcre2_match_data *match_data =
pcre2_match_data_create_from_pattern(re, NULL);
int ret =
int32_t ret =
pcre2_match(re, (PCRE2_SPTR)str, strlen(str), 0, 0, match_data, NULL);
pcre2_match_data_free(match_data);
pcre2_code_free(re);
return ret >= 0;
}
void wl_list_append(struct wl_list *list, struct wl_list *object) {
wl_list_insert(list->prev, object);
}
uint32_t get_now_in_ms(void) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return timespec_to_ms(&now);
}
uint32_t timespec_to_ms(struct timespec *ts) {
return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000;
}
char *join_strings(char *arr[], const char *sep) {
if (!arr || !arr[0]) {
char *empty = malloc(1);
if (empty)
empty[0] = '\0';
return empty;
}
size_t total_len = 0;
int count = 0;
for (int i = 0; arr[i] != NULL; i++) {
total_len += strlen(arr[i]);
count++;
}
if (count > 0) {
total_len += strlen(sep) * (count - 1);
}
char *result = malloc(total_len + 1);
if (!result)
return NULL;
result[0] = '\0';
for (int i = 0; arr[i] != NULL; i++) {
if (i > 0)
strcat(result, sep);
strcat(result, arr[i]);
}
return result;
}
char *join_strings_with_suffix(char *arr[], const char *suffix,
const char *sep) {
if (!arr || !arr[0]) {
char *empty = malloc(1);
if (empty)
empty[0] = '\0';
return empty;
}
size_t total_len = 0;
int count = 0;
for (int i = 0; arr[i] != NULL; i++) {
total_len += strlen(arr[i]) + strlen(suffix);
count++;
}
if (count > 0) {
total_len += strlen(sep) * (count - 1);
}
char *result = malloc(total_len + 1);
if (!result)
return NULL;
result[0] = '\0';
for (int i = 0; arr[i] != NULL; i++) {
if (i > 0)
strcat(result, sep);
strcat(result, arr[i]);
strcat(result, suffix);
}
return result;
}
char *string_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
if (len < 0)
return NULL;
char *str = malloc(len + 1);
if (!str)
return NULL;
va_start(args, fmt);
vsnprintf(str, len + 1, fmt, args);
va_end(args);
return str;
}

View file

@ -1,6 +1,14 @@
/* See LICENSE.dwm file for copyright and license details. */
#include <wayland-util.h>
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);
int fd_set_nonblock(int fd);
int regex_match(const char *pattern_mb, const char *str_mb);
int32_t fd_set_nonblock(int32_t fd);
int32_t regex_match(const char *pattern_mb, const char *str_mb);
void wl_list_append(struct wl_list *list, struct wl_list *object);
uint32_t get_now_in_ms(void);
uint32_t timespec_to_ms(struct timespec *ts);
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

@ -8,93 +8,111 @@
((hex >> 8) & 0xFF) / 255.0f, (hex & 0xFF) / 255.0f}
/* animaion */
char *animation_type_open = "slide"; // 是否启用动画 //slide,zoom
char *animation_type_close = "slide"; // 是否启用动画 //slide,zoom
char *layer_animation_type_open = "slide"; // 是否启用layer动画 //slide,zoom
char *layer_animation_type_close = "slide"; // 是否启用layer动画 //slide,zoom
int animations = 1; // 是否启用动画
int layer_animations = 0; // 是否启用layer动画
int tag_animation_direction = HORIZONTAL; // 标签动画方向
int animation_fade_in = 1; // Enable animation fade in
int animation_fade_out = 1; // Enable animation fade out
float zoom_initial_ratio = 0.3; // 动画起始窗口比例
float zoom_end_ratio = 0.8; // 动画结束窗口比例
char *animation_type_open = "slide"; // 是否启用动画 //slide,zoom
char *animation_type_close = "slide"; // 是否启用动画 //slide,zoom
char *layer_animation_type_open = "slide"; // 是否启用layer动画 //slide,zoom
char *layer_animation_type_close = "slide"; // 是否启用layer动画 //slide,zoom
int32_t animations = 1; // 是否启用动画
int32_t layer_animations = 0; // 是否启用layer动画
int32_t tag_animation_direction = HORIZONTAL; // 标签动画方向
int32_t animation_fade_in = 1; // Enable animation fade in
int32_t animation_fade_out = 1; // Enable animation fade out
float zoom_initial_ratio = 0.3; // 动画起始窗口比例
float zoom_end_ratio = 0.8; // 动画结束窗口比例
float fadein_begin_opacity = 0.5; // Begin opac window ratio for animations
float fadeout_begin_opacity = 0.5; // Begin opac window ratio for animations
uint32_t animation_duration_move = 500; // Animation move speed
uint32_t animation_duration_open = 400; // Animation open speed
uint32_t animation_duration_tag = 300; // Animation tag speed
uint32_t animation_duration_close = 300; // Animation close speed
double animation_curve_move[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_open[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_tag[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_close[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
uint32_t animation_duration_focus = 0; // Animation focus opacity speed
double animation_curve_move[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_open[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_tag[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_close[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_focus[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_opafadein[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_opafadeout[4] = {0.5, 0.5, 0.5, 0.5}; // 动画曲线
/* appearance */
unsigned int axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔
unsigned int focus_on_activate = 1; // 收到窗口激活请求是否自动跳转聚焦
unsigned int new_is_master = 1; // 新窗口是否插在头部
double default_mfact = 0.55f; // master 窗口比例
double default_smfact = 0.5f; // 第一个stack窗口比例
unsigned int default_nmaster = 1; // 默认master数量
uint32_t axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔
uint32_t focus_on_activate = 1; // 收到窗口激活请求是否自动跳转聚焦
uint32_t new_is_master = 1; // 新窗口是否插在头部
double default_mfact = 0.55f; // master 窗口比例
uint32_t default_nmaster = 1; // 默认master数量
int32_t center_master_overspread = 0; // 中心master时是否铺满
int32_t center_when_single_stack = 1; // 单个stack时是否居中
/* logging */
int log_level = WLR_ERROR;
unsigned int numlockon = 1; // 是否打开右边小键盘
unsigned int capslock = 0; // 是否启用快捷键
int32_t log_level = WLR_ERROR;
uint32_t numlockon = 0; // 是否打开右边小键盘
uint32_t capslock = 0; // 是否启用快捷键
unsigned int ov_tab_mode = 0; // alt tab切换模式
unsigned int hotarea_size = 10; // 热区大小,10x10
unsigned int enable_hotarea = 1; // 是否启用鼠标热区
int smartgaps = 0; /* 1 means no outer gap when there is only one window */
int sloppyfocus = 1; /* focus follows mouse */
unsigned int gappih = 5; /* horiz inner gap between windows */
unsigned int gappiv = 5; /* vert inner gap between windows */
unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */
unsigned int gappov = 10; /* vert outer gap between windows and screen edge */
uint32_t ov_tab_mode = 0; // alt tab切换模式
uint32_t hotarea_size = 10; // 热区大小,10x10
uint32_t hotarea_corner = BOTTOM_LEFT;
uint32_t enable_hotarea = 1; // 是否启用鼠标热区
int32_t smartgaps = 0; /* 1 means no outer gap when there is only one window */
int32_t sloppyfocus = 1; /* focus follows mouse */
uint32_t gappih = 5; /* horiz inner gap between windows */
uint32_t gappiv = 5; /* vert inner gap between windows */
uint32_t gappoh = 10; /* horiz outer gap between windows and screen edge */
uint32_t gappov = 10; /* vert outer gap between windows and screen edge */
float scratchpad_width_ratio = 0.8;
float scratchpad_height_ratio = 0.9;
int scroller_structs = 20;
int32_t scroller_structs = 20;
float scroller_default_proportion = 0.9;
float scroller_default_proportion_single = 1.0;
int scroller_focus_center = 0;
int scroller_prefer_center = 0;
int focus_cross_monitor = 0;
int focus_cross_tag = 0;
int no_border_when_single = 0;
int no_radius_when_single = 0;
int snap_distance = 30;
int enable_floating_snap = 0;
int drag_tile_to_tile = 0;
unsigned int cursor_size = 24;
unsigned int cursor_hide_timeout = 0;
int32_t scroller_ignore_proportion_single = 1;
int32_t scroller_focus_center = 0;
int32_t scroller_prefer_center = 0;
int32_t scroller_prefer_overspread = 1;
int32_t focus_cross_monitor = 0;
int32_t focus_cross_tag = 0;
int32_t exchange_cross_monitor = 0;
int32_t scratchpad_cross_monitor = 0;
int32_t view_current_to_back = 0;
int32_t no_border_when_single = 0;
int32_t no_radius_when_single = 0;
int32_t snap_distance = 30;
int32_t enable_floating_snap = 0;
int32_t drag_tile_to_tile = 0;
uint32_t cursor_size = 24;
uint32_t cursor_hide_timeout = 0;
unsigned int swipe_min_threshold = 20;
uint32_t swipe_min_threshold = 1;
int inhibit_regardless_of_visibility =
int32_t idleinhibit_ignore_visible =
0; /* 1 means idle inhibitors will disable idle tracking even if it's
surface isn't visible */
unsigned int borderpx = 4; /* border pixel of windows */
uint32_t borderpx = 4; /* border pixel of windows */
float rootcolor[] = COLOR(0x323232ff);
float bordercolor[] = COLOR(0x444444ff);
float focuscolor[] = COLOR(0xc66b25ff);
float maxmizescreencolor[] = COLOR(0x89aa61ff);
float maximizescreencolor[] = COLOR(0x89aa61ff);
float urgentcolor[] = COLOR(0xad401fff);
float scratchpadcolor[] = COLOR(0x516c93ff);
float globalcolor[] = COLOR(0xb153a7ff);
float overlaycolor[] = COLOR(0x14a57cff);
// char *cursor_theme = "Bibata-Modern-Ice";
int overviewgappi = 5; /* overview时 窗口与边缘 缝隙大小 */
int overviewgappo = 30; /* overview时 窗口与窗口 缝隙大小 */
int32_t overviewgappi = 5; /* overview时 窗口与边缘 缝隙大小 */
int32_t overviewgappo = 30; /* overview时 窗口与窗口 缝隙大小 */
/* To conform the xdg-protocol, set the alpha to zero to restore the old
* behavior */
float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0};
int warpcursor = 1; /* Warp cursor to focused client */
int xwayland_persistence = 1; /* xwayland persistence */
int syncobj_enable = 0;
int32_t warpcursor = 1;
int32_t drag_corner = 3;
int32_t drag_warp_cursor = 1;
int32_t xwayland_persistence = 1; /* xwayland persistence */
int32_t syncobj_enable = 0;
int32_t allow_lock_transparent = 0;
double drag_tile_refresh_interval = 8.0;
double drag_floating_refresh_interval = 8.0;
int32_t allow_tearing = TEARING_DISABLED;
int32_t allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;
/* keyboard */
@ -108,6 +126,19 @@ char xkb_rules_layout[256];
char xkb_rules_variant[256];
char xkb_rules_options[256];
/* keyboard */
static const struct xkb_rule_names xkb_fallback_rules = {
.layout = "us",
.variant = NULL,
.model = NULL,
.rules = NULL,
.options = NULL,
};
static const struct xkb_rule_names xkb_default_rules = {
.options = NULL,
};
struct xkb_rule_names xkb_rules = {
/* can specify fields: rules, model, layout, variant, options */
/* example:
@ -118,20 +149,21 @@ struct xkb_rule_names xkb_rules = {
.options = xkb_rules_options,
};
int repeat_rate = 25;
int repeat_delay = 600;
int32_t repeat_rate = 25;
int32_t repeat_delay = 600;
/* Trackpad */
int disable_trackpad = 0;
int tap_to_click = 1;
int tap_and_drag = 1;
int drag_lock = 1;
int mouse_natural_scrolling = 0;
int trackpad_natural_scrolling = 0;
int disable_while_typing = 1;
int left_handed = 0;
int middle_button_emulation = 0;
int single_scratchpad = 1;
int32_t disable_trackpad = 0;
int32_t tap_to_click = 1;
int32_t tap_and_drag = 1;
int32_t drag_lock = 1;
int32_t mouse_natural_scrolling = 0;
int32_t trackpad_natural_scrolling = 0;
int32_t disable_while_typing = 1;
int32_t left_handed = 0;
int32_t middle_button_emulation = 0;
int32_t single_scratchpad = 1;
int32_t edge_scroller_pointer_focus = 1;
/* You can choose between:
LIBINPUT_CONFIG_SCROLL_NO_SCROLL
@ -140,7 +172,7 @@ LIBINPUT_CONFIG_SCROLL_EDGE
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
*/
enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
unsigned int scroll_button = 274;
uint32_t scroll_button = 274;
/* You can choose between:
LIBINPUT_CONFIG_CLICK_METHOD_NONE
@ -150,6 +182,8 @@ LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
enum libinput_config_click_method click_method =
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
double axis_scroll_factor = 1.0;
/* You can choose between:
LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
@ -180,27 +214,26 @@ static const char *tags[] = {
float focused_opacity = 1.0;
float unfocused_opacity = 1.0;
int border_radius = 0;
int border_radius_location_default = CORNER_LOCATION_ALL;
int blur = 0;
int blur_layer = 0;
int blur_optimized = 1;
int32_t border_radius = 0;
int32_t border_radius_location_default = CORNER_LOCATION_ALL;
int32_t blur = 0;
int32_t blur_layer = 0;
int32_t blur_optimized = 1;
struct blur_data blur_params;
int blur_params_num_passes = 1;
int blur_params_radius = 5;
int32_t blur_params_num_passes = 1;
int32_t blur_params_radius = 5;
float blur_params_noise = 0.02;
float blur_params_brightness = 0.9;
float blur_params_contrast = 0.9;
float blur_params_saturation = 1.2;
int shadows = 0;
int shadow_only_floating = 1;
int layer_shadows = 0;
unsigned int shadows_size = 10;
int32_t shadows = 0;
int32_t shadow_only_floating = 1;
int32_t layer_shadows = 0;
uint32_t shadows_size = 10;
double shadows_blur = 15;
int shadows_position_x = 0;
int shadows_position_y = 0;
int32_t shadows_position_x = 0;
int32_t shadows_position_y = 0;
float shadowscolor[] = COLOR(0x000000ff);
;

View file

@ -5,7 +5,7 @@ typedef struct {
static const LayoutMapping layout_mappings[] = {
{"English (US)", "us"},
{"English (UK)", "uk"},
{"English (UK)", "gb"},
{"Russian", "ru"},
{"German", "de"},
{"French", "fr"},
@ -76,4 +76,4 @@ static const LayoutMapping layout_mappings[] = {
{"Kannada", "kn"},
{"Malayalam", "ml"},
{NULL, NULL} // 结束标记
};
};

View file

@ -1,61 +1,72 @@
void minized(const Arg *arg);
void restore_minized(const Arg *arg);
void toggle_scratchpad(const Arg *arg);
void focusdir(const Arg *arg);
void toggleoverview(const Arg *arg);
void set_proportion(const Arg *arg);
void increase_proportion(const Arg *arg);
void switch_proportion_preset(const Arg *arg);
void zoom(const Arg *arg);
void tagsilent(const Arg *arg);
void tagtoleft(const Arg *arg);
void tagtoright(const Arg *arg);
void viewtoleft(const Arg *arg);
void viewtoright(const Arg *arg);
void viewtoleft_have_client(const Arg *arg);
void viewtoright_have_client(const Arg *arg);
void togglefloating(const Arg *arg);
void togglefullscreen(const Arg *arg);
void togglemaxmizescreen(const Arg *arg);
void togglegaps(const Arg *arg);
void tagmon(const Arg *arg);
void spawn(const Arg *arg);
void spawn_on_empty(const Arg *arg);
void setlayout(const Arg *arg);
void switch_layout(const Arg *arg);
void switch_keyboard_layout(const Arg *arg);
void setmfact(const Arg *arg);
void setsmfact(const Arg *arg);
void quit(const Arg *arg);
void moveresize(const Arg *arg);
void exchange_client(const Arg *arg);
void killclient(const Arg *arg);
void toggleglobal(const Arg *arg);
void incnmaster(const Arg *arg);
void focusmon(const Arg *arg);
void focusstack(const Arg *arg);
void chvt(const Arg *arg);
void reload_config(const Arg *arg);
void smartmovewin(const Arg *arg);
void smartresizewin(const Arg *arg);
void bind_to_view(const Arg *arg);
void toggletag(const Arg *arg);
void toggleview(const Arg *arg);
void tag(const Arg *arg);
void incgaps(const Arg *arg);
void incigaps(const Arg *arg);
void incihgaps(const Arg *arg);
void incivgaps(const Arg *arg);
void incogaps(const Arg *arg);
void incohgaps(const Arg *arg);
void incovgaps(const Arg *arg);
void defaultgaps(const Arg *arg);
void togglefakefullscreen(const Arg *arg);
void toggleoverlay(const Arg *arg);
void movewin(const Arg *arg);
void resizewin(const Arg *arg);
void toggle_named_scratchpad(const Arg *arg);
void toggle_render_border(const Arg *arg);
void create_virtual_output(const Arg *arg);
void destroy_all_virtual_output(const Arg *arg);
void focuslast(const Arg *arg);
int32_t minimized(const Arg *arg);
int32_t restore_minimized(const Arg *arg);
int32_t toggle_scratchpad(const Arg *arg);
int32_t focusdir(const Arg *arg);
int32_t toggleoverview(const Arg *arg);
int32_t set_proportion(const Arg *arg);
int32_t switch_proportion_preset(const Arg *arg);
int32_t zoom(const Arg *arg);
int32_t tagsilent(const Arg *arg);
int32_t tagtoleft(const Arg *arg);
int32_t tagtoright(const Arg *arg);
int32_t tagcrossmon(const Arg *arg);
int32_t viewtoleft(const Arg *arg);
int32_t viewtoright(const Arg *arg);
int32_t viewtoleft_have_client(const Arg *arg);
int32_t viewtoright_have_client(const Arg *arg);
int32_t viewcrossmon(const Arg *arg);
int32_t togglefloating(const Arg *arg);
int32_t togglefullscreen(const Arg *arg);
int32_t togglemaximizescreen(const Arg *arg);
int32_t togglegaps(const Arg *arg);
int32_t tagmon(const Arg *arg);
int32_t spawn(const Arg *arg);
int32_t spawn_shell(const Arg *arg);
int32_t spawn_on_empty(const Arg *arg);
int32_t setkeymode(const Arg *arg);
int32_t switch_keyboard_layout(const Arg *arg);
int32_t setlayout(const Arg *arg);
int32_t switch_layout(const Arg *arg);
int32_t setmfact(const Arg *arg);
int32_t quit(const Arg *arg);
int32_t moveresize(const Arg *arg);
int32_t exchange_client(const Arg *arg);
int32_t exchange_stack_client(const Arg *arg);
int32_t killclient(const Arg *arg);
int32_t toggleglobal(const Arg *arg);
int32_t incnmaster(const Arg *arg);
int32_t focusmon(const Arg *arg);
int32_t focusstack(const Arg *arg);
int32_t chvt(const Arg *arg);
int32_t reload_config(const Arg *arg);
int32_t smartmovewin(const Arg *arg);
int32_t smartresizewin(const Arg *arg);
int32_t centerwin(const Arg *arg);
int32_t bind_to_view(const Arg *arg);
int32_t toggletag(const Arg *arg);
int32_t toggleview(const Arg *arg);
int32_t tag(const Arg *arg);
int32_t comboview(const Arg *arg);
int32_t incgaps(const Arg *arg);
int32_t incigaps(const Arg *arg);
int32_t incihgaps(const Arg *arg);
int32_t incivgaps(const Arg *arg);
int32_t incogaps(const Arg *arg);
int32_t incohgaps(const Arg *arg);
int32_t incovgaps(const Arg *arg);
int32_t defaultgaps(const Arg *arg);
int32_t togglefakefullscreen(const Arg *arg);
int32_t toggleoverlay(const Arg *arg);
int32_t movewin(const Arg *arg);
int32_t resizewin(const Arg *arg);
int32_t toggle_named_scratchpad(const Arg *arg);
int32_t toggle_render_border(const Arg *arg);
int32_t create_virtual_output(const Arg *arg);
int32_t destroy_all_virtual_output(const Arg *arg);
int32_t focuslast(const Arg *arg);
int32_t toggle_trackpad_enable(const Arg *arg);
int32_t setoption(const Arg *arg);
int32_t disable_monitor(const Arg *arg);
int32_t enable_monitor(const Arg *arg);
int32_t toggle_monitor(const Arg *arg);
int32_t scroller_stack(const Arg *arg);

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,5 @@
#include "dwl-ipc.h"
#include "ext-workspace.h"
#include "foreign-toplevel.h"
#include "tearing.h"
#include "text-input.h"

View file

@ -1,12 +1,11 @@
#include "dwl-ipc-unstable-v2-protocol.h"
static void dwl_ipc_manager_bind(struct wl_client *client, void *data,
unsigned int version, unsigned int id);
uint32_t version, uint32_t id);
static void dwl_ipc_manager_destroy(struct wl_resource *resource);
static void dwl_ipc_manager_get_output(struct wl_client *client,
struct wl_resource *resource,
unsigned int id,
struct wl_resource *output);
uint32_t id, struct wl_resource *output);
static void dwl_ipc_manager_release(struct wl_client *client,
struct wl_resource *resource);
static void dwl_ipc_output_destroy(struct wl_resource *resource);
@ -14,15 +13,14 @@ static void dwl_ipc_output_printstatus(Monitor *monitor);
static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output);
static void dwl_ipc_output_set_client_tags(struct wl_client *client,
struct wl_resource *resource,
unsigned int and_tags,
unsigned int xor_tags);
uint32_t and_tags,
uint32_t xor_tags);
static void dwl_ipc_output_set_layout(struct wl_client *client,
struct wl_resource *resource,
unsigned int index);
uint32_t index);
static void dwl_ipc_output_set_tags(struct wl_client *client,
struct wl_resource *resource,
unsigned int tagmask,
unsigned int toggle_tagset);
uint32_t tagmask, uint32_t toggle_tagset);
static void dwl_ipc_output_quit(struct wl_client *client,
struct wl_resource *resource);
static void dwl_ipc_output_dispatch(struct wl_client *client,
@ -46,7 +44,7 @@ static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {
.set_client_tags = dwl_ipc_output_set_client_tags};
void dwl_ipc_manager_bind(struct wl_client *client, void *data,
unsigned int version, unsigned int id) {
uint32_t version, uint32_t id) {
struct wl_resource *manager_resource =
wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id);
if (!manager_resource) {
@ -59,7 +57,7 @@ void dwl_ipc_manager_bind(struct wl_client *client, void *data,
zdwl_ipc_manager_v2_send_tags(manager_resource, LENGTH(tags));
for (unsigned int i = 0; i < LENGTH(layouts); i++)
for (uint32_t i = 0; i < LENGTH(layouts); i++)
zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol);
}
@ -68,7 +66,7 @@ void dwl_ipc_manager_destroy(struct wl_resource *resource) {
}
void dwl_ipc_manager_get_output(struct wl_client *client,
struct wl_resource *resource, unsigned int id,
struct wl_resource *resource, uint32_t id,
struct wl_resource *output) {
DwlIpcOutput *ipc_output;
struct wlr_output *op = wlr_output_from_resource(output);
@ -101,17 +99,22 @@ static void dwl_ipc_output_destroy(struct wl_resource *resource) {
free(ipc_output);
}
// 修改IPC输出函数接受掩码参数
void dwl_ipc_output_printstatus(Monitor *monitor) {
DwlIpcOutput *ipc_output;
wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link)
dwl_ipc_output_printstatus_to(ipc_output);
}
// 修改主IPC输出函数根据掩码发送相应事件
void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
Monitor *monitor = ipc_output->mon;
Client *c, *focused;
int tagmask, state, numclients, focused_client, tag;
Client *c = NULL, *focused = NULL;
struct wlr_keyboard *keyboard;
xkb_layout_index_t current;
int32_t tagmask, state, numclients, focused_client, tag;
const char *title, *appid, *symbol;
char kb_layout[32];
focused = focustop(monitor);
zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon);
@ -137,7 +140,18 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
title = focused ? client_get_title(focused) : "";
appid = focused ? client_get_appid(focused) : "";
symbol = monitor->pertag->ltidxs[monitor->pertag->curtag]->symbol;
if (monitor->isoverview) {
symbol = overviewlayout.symbol;
} else {
symbol = monitor->pertag->ltidxs[monitor->pertag->curtag]->symbol;
}
keyboard = &kb_group->wlr_group->keyboard;
current = xkb_state_serialize_layout(keyboard->xkb_state,
XKB_STATE_LAYOUT_EFFECTIVE);
get_layout_abbr(kb_layout,
xkb_keymap_layout_get_name(keyboard->keymap, current));
zdwl_ipc_output_v2_send_layout(
ipc_output->resource,
@ -180,17 +194,33 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
zdwl_ipc_output_v2_send_last_layer(ipc_output->resource,
monitor->last_surface_ws_name);
}
if (wl_resource_get_version(ipc_output->resource) >=
ZDWL_IPC_OUTPUT_V2_KB_LAYOUT_SINCE_VERSION) {
zdwl_ipc_output_v2_send_kb_layout(ipc_output->resource, kb_layout);
}
if (wl_resource_get_version(ipc_output->resource) >=
ZDWL_IPC_OUTPUT_V2_KEYMODE_SINCE_VERSION) {
zdwl_ipc_output_v2_send_keymode(ipc_output->resource, keymode.mode);
}
if (wl_resource_get_version(ipc_output->resource) >=
ZDWL_IPC_OUTPUT_V2_SCALEFACTOR_SINCE_VERSION) {
zdwl_ipc_output_v2_send_scalefactor(ipc_output->resource,
monitor->wlr_output->scale * 100);
}
zdwl_ipc_output_v2_send_frame(ipc_output->resource);
}
void dwl_ipc_output_set_client_tags(struct wl_client *client,
struct wl_resource *resource,
unsigned int and_tags,
unsigned int xor_tags) {
uint32_t and_tags, uint32_t xor_tags) {
DwlIpcOutput *ipc_output;
Monitor *monitor;
Client *selected_client;
unsigned int newtags = 0;
Monitor *monitor = NULL;
Client *selected_client = NULL;
uint32_t newtags = 0;
ipc_output = wl_resource_get_user_data(resource);
if (!ipc_output)
@ -208,15 +238,14 @@ void dwl_ipc_output_set_client_tags(struct wl_client *client,
selected_client->tags = newtags;
if (selmon == monitor)
focusclient(focustop(monitor), 1);
arrange(selmon, false);
arrange(selmon, false, false);
printstatus();
}
void dwl_ipc_output_set_layout(struct wl_client *client,
struct wl_resource *resource,
unsigned int index) {
struct wl_resource *resource, uint32_t index) {
DwlIpcOutput *ipc_output;
Monitor *monitor;
Monitor *monitor = NULL;
ipc_output = wl_resource_get_user_data(resource);
if (!ipc_output)
@ -227,23 +256,24 @@ void dwl_ipc_output_set_layout(struct wl_client *client,
index = 0;
monitor->pertag->ltidxs[monitor->pertag->curtag] = &layouts[index];
arrange(monitor, false);
clear_fullscreen_and_maximized_state(monitor);
arrange(monitor, false, false);
printstatus();
}
void dwl_ipc_output_set_tags(struct wl_client *client,
struct wl_resource *resource, unsigned int tagmask,
unsigned int toggle_tagset) {
struct wl_resource *resource, uint32_t tagmask,
uint32_t toggle_tagset) {
DwlIpcOutput *ipc_output;
Monitor *monitor;
unsigned int newtags = tagmask & TAGMASK;
Monitor *monitor = NULL;
uint32_t newtags = tagmask & TAGMASK;
ipc_output = wl_resource_get_user_data(resource);
if (!ipc_output)
return;
monitor = ipc_output->mon;
view_in_mon(&(Arg){.ui = newtags}, true, monitor);
view_in_mon(&(Arg){.ui = newtags}, true, monitor, true);
}
void dwl_ipc_output_quit(struct wl_client *client,
@ -257,13 +287,20 @@ void dwl_ipc_output_dispatch(struct wl_client *client,
const char *arg3, const char *arg4,
const char *arg5) {
void (*func)(const Arg *);
int32_t (*func)(const Arg *);
Arg arg;
func = parse_func_name((char *)dispatch, &arg, (char *)arg1, (char *)arg2,
(char *)arg3, (char *)arg4, (char *)arg5);
if (func) {
func(&arg);
}
if (arg.v)
free(arg.v);
if (arg.v2)
free(arg.v2);
if (arg.v3)
free(arg.v3);
}
void dwl_ipc_output_release(struct wl_client *client,

View file

@ -0,0 +1,187 @@
#include "wlr_ext_workspace_v1.h"
#define EXT_WORKSPACE_ENABLE_CAPS \
EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE | \
EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE
typedef struct Monitor Monitor;
struct workspace {
struct wl_list link; // Link in global workspaces list
uint32_t tag; // Numeric identifier (1-9, 0=overview)
Monitor *m; // Associated monitor
struct wlr_ext_workspace_handle_v1 *ext_workspace; // Protocol object
/* Event listeners */
struct wl_listener activate;
struct wl_listener deactivate;
struct wl_listener assign;
struct wl_listener remove;
};
struct wlr_ext_workspace_manager_v1 *ext_manager;
struct wl_list workspaces;
void goto_workspace(struct workspace *target) {
uint32_t tag;
tag = 1 << (target->tag - 1);
if (target->tag == 0) {
toggleoverview(&(Arg){.i = -1});
return;
} else {
view(&(Arg){.ui = tag}, true);
}
}
void toggle_workspace(struct workspace *target) {
uint32_t tag;
tag = 1 << (target->tag - 1);
if (target->tag == 0) {
toggleview(&(Arg){.i = -1});
return;
} else {
toggleview(&(Arg){.ui = tag});
}
}
static void handle_ext_workspace_activate(struct wl_listener *listener,
void *data) {
struct workspace *workspace =
wl_container_of(listener, workspace, activate);
if (workspace->m->isoverview) {
return;
}
goto_workspace(workspace);
wlr_log(WLR_INFO, "ext activating workspace %d", workspace->tag);
}
static void handle_ext_workspace_deactivate(struct wl_listener *listener,
void *data) {
struct workspace *workspace =
wl_container_of(listener, workspace, deactivate);
if (workspace->m->isoverview) {
return;
}
toggle_workspace(workspace);
wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag);
}
static const char *get_name_from_tag(uint32_t tag) {
static const char *names[] = {"overview", "1", "2", "3", "4",
"5", "6", "7", "8", "9"};
return (tag < sizeof(names) / sizeof(names[0])) ? names[tag] : NULL;
}
void destroy_workspace(struct workspace *workspace) {
wl_list_remove(&workspace->activate.link);
wl_list_remove(&workspace->deactivate.link);
wlr_ext_workspace_handle_v1_destroy(workspace->ext_workspace);
wl_list_remove(&workspace->link);
free(workspace);
}
void cleanup_workspaces_by_monitor(Monitor *m) {
struct workspace *workspace, *tmp;
wl_list_for_each_safe(workspace, tmp, &workspaces, link) {
if (workspace->m == m) {
destroy_workspace(workspace);
}
}
}
static void remove_workspace_by_tag(uint32_t tag, Monitor *m) {
struct workspace *workspace, *tmp;
wl_list_for_each_safe(workspace, tmp, &workspaces, link) {
if (workspace->tag == tag && workspace->m == m) {
destroy_workspace(workspace);
return;
}
}
}
static void add_workspace_by_tag(int32_t tag, Monitor *m) {
const char *name = get_name_from_tag(tag);
struct workspace *workspace = ecalloc(1, sizeof(*workspace));
wl_list_append(&workspaces, &workspace->link);
workspace->tag = tag;
workspace->m = m;
workspace->ext_workspace = wlr_ext_workspace_handle_v1_create(
ext_manager, name, EXT_WORKSPACE_ENABLE_CAPS);
wlr_ext_workspace_handle_v1_set_group(workspace->ext_workspace,
m->ext_group);
wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name);
workspace->activate.notify = handle_ext_workspace_activate;
wl_signal_add(&workspace->ext_workspace->events.activate,
&workspace->activate);
workspace->deactivate.notify = handle_ext_workspace_deactivate;
wl_signal_add(&workspace->ext_workspace->events.deactivate,
&workspace->deactivate);
}
void dwl_ext_workspace_printstatus(Monitor *m) {
struct workspace *w;
uint32_t tag_status = 0;
wl_list_for_each(w, &workspaces, link) {
if (w && w->m == m) {
tag_status = get_tag_status(w->tag, m);
if (tag_status == 2) {
wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false);
wlr_ext_workspace_handle_v1_set_urgent(w->ext_workspace, true);
} else if (tag_status == 1) {
wlr_ext_workspace_handle_v1_set_urgent(w->ext_workspace, false);
wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false);
} else {
wlr_ext_workspace_handle_v1_set_urgent(w->ext_workspace, false);
if (!w->m->pertag->no_hide[w->tag])
wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace,
true);
else {
wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace,
false);
}
}
if ((m->tagset[m->seltags] & (1 << (w->tag - 1)) & TAGMASK) ||
m->isoverview) {
wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false);
wlr_ext_workspace_handle_v1_set_active(w->ext_workspace, true);
} else {
wlr_ext_workspace_handle_v1_set_active(w->ext_workspace, false);
}
}
}
}
void refresh_monitors_workspaces_status(Monitor *m) {
int32_t i;
if (m->isoverview) {
for (i = 1; i <= LENGTH(tags); i++) {
remove_workspace_by_tag(i, m);
}
add_workspace_by_tag(0, m);
} else {
remove_workspace_by_tag(0, m);
for (i = 1; i <= LENGTH(tags); i++) {
add_workspace_by_tag(i, m);
}
}
dwl_ext_workspace_printstatus(m);
}
void workspaces_init() {
/* Create the global workspace manager with activation capability */
ext_manager = wlr_ext_workspace_manager_v1_create(dpy, 1);
/* Initialize the global workspaces list */
wl_list_init(&workspaces);
}

View file

@ -4,52 +4,100 @@ static struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
void handle_foreign_activate_request(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, foreign_activate_request);
unsigned int target;
uint32_t target;
if (c && c->swallowing)
if (c->swallowing)
return;
if (c && !c->isminied && c == selmon->sel) {
set_minized(c);
return;
}
if (c->isminied) {
if (c->isminimized) {
c->is_in_scratchpad = 0;
c->isnamedscratchpad = 0;
c->is_scratchpad_show = 0;
setborder_color(c);
show_hide_client(c);
arrange(c->mon, true);
arrange(c->mon, true, false);
return;
}
target = get_tags_first_tag(c->tags);
view(&(Arg){.ui = target}, true);
view_in_mon(&(Arg){.ui = target}, true, c->mon, true);
focusclient(c, 1);
wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, true);
}
void handle_foreign_maximize_request(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, foreign_maximize_request);
struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
if (c->swallowing)
return;
if (c->ismaximizescreen && !event->maximized) {
setmaximizescreen(c, 0);
return;
}
if (!c->ismaximizescreen && event->maximized) {
setmaximizescreen(c, 1);
return;
}
}
void handle_foreign_minimize_request(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, foreign_minimize_request);
struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data;
if (c->swallowing)
return;
if (!c->isminimized && event->minimized) {
set_minimized(c);
return;
}
if (c->isminimized && !event->minimized) {
c->is_in_scratchpad = 0;
c->isnamedscratchpad = 0;
c->is_scratchpad_show = 0;
setborder_color(c);
show_hide_client(c);
arrange(c->mon, true, false);
return;
}
}
void handle_foreign_fullscreen_request(struct wl_listener *listener,
void *data) {
return;
Client *c = wl_container_of(listener, c, foreign_fullscreen_request);
struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
if (c->swallowing)
return;
if (c->isfullscreen && !event->fullscreen) {
setfullscreen(c, 0);
return;
}
if (!c->isfullscreen && event->fullscreen) {
setfullscreen(c, 1);
return;
}
}
void handle_foreign_close_request(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, foreign_close_request);
if (c) {
pending_kill_client(c);
}
pending_kill_client(c);
}
void handle_foreign_destroy(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, foreign_destroy);
if (c) {
wl_list_remove(&c->foreign_activate_request.link);
wl_list_remove(&c->foreign_fullscreen_request.link);
wl_list_remove(&c->foreign_close_request.link);
wl_list_remove(&c->foreign_destroy.link);
}
wl_list_remove(&c->foreign_activate_request.link);
wl_list_remove(&c->foreign_minimize_request.link);
wl_list_remove(&c->foreign_maximize_request.link);
wl_list_remove(&c->foreign_fullscreen_request.link);
wl_list_remove(&c->foreign_close_request.link);
wl_list_remove(&c->foreign_destroy.link);
}
void remove_foreign_topleve(Client *c) {
@ -67,6 +115,10 @@ void add_foreign_toplevel(Client *c) {
if (c->foreign_toplevel) {
LISTEN(&(c->foreign_toplevel->events.request_activate),
&c->foreign_activate_request, handle_foreign_activate_request);
LISTEN(&(c->foreign_toplevel->events.request_minimize),
&c->foreign_minimize_request, handle_foreign_minimize_request);
LISTEN(&(c->foreign_toplevel->events.request_maximize),
&c->foreign_maximize_request, handle_foreign_maximize_request);
LISTEN(&(c->foreign_toplevel->events.request_fullscreen),
&c->foreign_fullscreen_request,
handle_foreign_fullscreen_request);

171
src/ext-protocol/tearing.h Normal file
View file

@ -0,0 +1,171 @@
#include <wlr/types/wlr_tearing_control_v1.h>
struct tearing_controller {
struct wlr_tearing_control_v1 *tearing_control;
struct wl_listener set_hint;
struct wl_listener destroy;
};
struct wlr_tearing_control_manager_v1 *tearing_control;
struct wl_listener tearing_new_object;
static void handle_controller_set_hint(struct wl_listener *listener,
void *data) {
struct tearing_controller *controller =
wl_container_of(listener, controller, set_hint);
Client *c = NULL;
toplevel_from_wlr_surface(controller->tearing_control->surface, &c, NULL);
if (c) {
/*
* tearing_control->current is actually an enum:
* WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC = 0
* WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC = 1
*
* Using it as a bool here allows us to not ship the XML.
*/
c->tearing_hint = controller->tearing_control->current;
}
}
static void handle_controller_destroy(struct wl_listener *listener,
void *data) {
struct tearing_controller *controller =
wl_container_of(listener, controller, destroy);
wl_list_remove(&controller->set_hint.link);
wl_list_remove(&controller->destroy.link);
free(controller);
}
void handle_tearing_new_object(struct wl_listener *listener, void *data) {
struct wlr_tearing_control_v1 *new_tearing_control = data;
enum wp_tearing_control_v1_presentation_hint hint =
wlr_tearing_control_manager_v1_surface_hint_from_surface(
tearing_control, new_tearing_control->surface);
wlr_log(WLR_DEBUG, "New presentation hint %d received for surface %p", hint,
new_tearing_control->surface);
struct tearing_controller *controller =
ecalloc(1, sizeof(struct tearing_controller));
controller->tearing_control = new_tearing_control;
controller->set_hint.notify = handle_controller_set_hint;
wl_signal_add(&new_tearing_control->events.set_hint, &controller->set_hint);
controller->destroy.notify = handle_controller_destroy;
wl_signal_add(&new_tearing_control->events.destroy, &controller->destroy);
}
bool check_tearing_frame_allow(Monitor *m) {
/* never allow tearing when disabled */
if (!allow_tearing) {
return false;
}
Client *c = selmon->sel;
/* tearing is only allowed for the output with the active client */
if (!c || c->mon != m) {
return false;
}
/* allow tearing for any window when requested or forced */
if (allow_tearing == TEARING_ENABLED) {
if (c->force_tearing == STATE_UNSPECIFIED) {
return c->tearing_hint;
} else {
return c->force_tearing == STATE_ENABLED;
}
}
/* remaining tearing options apply only to full-screen windows */
if (!c->isfullscreen) {
return false;
}
if (c->force_tearing == STATE_UNSPECIFIED) {
/* honor the tearing hint or the fullscreen-force preference */
return c->tearing_hint || allow_tearing == TEARING_FULLSCREEN_ONLY;
}
/* honor tearing as requested by action */
return c->force_tearing == STATE_ENABLED;
}
bool custom_wlr_scene_output_commit(struct wlr_scene_output *scene_output,
struct wlr_output_state *state) {
struct wlr_output *wlr_output = scene_output->output;
Monitor *m = wlr_output->data;
// 检查是否需要帧
if (!wlr_scene_output_needs_frame(scene_output)) {
wlr_log(WLR_DEBUG, "No frame needed for output %s", wlr_output->name);
return true;
}
// 构建输出状态
if (!wlr_scene_output_build_state(scene_output, state, NULL)) {
wlr_log(WLR_ERROR, "Failed to build output state for %s",
wlr_output->name);
return false;
}
// 测试撕裂翻页
if (state->tearing_page_flip) {
if (!wlr_output_test_state(wlr_output, state)) {
state->tearing_page_flip = false;
}
}
// 尝试提交
bool committed = wlr_output_commit_state(wlr_output, state);
// 如果启用撕裂翻页但提交失败,重试禁用撕裂翻页
if (!committed && state->tearing_page_flip) {
wlr_log(WLR_DEBUG, "Retrying commit without tearing for %s",
wlr_output->name);
state->tearing_page_flip = false;
committed = wlr_output_commit_state(wlr_output, state);
}
// 处理状态清理
if (committed) {
wlr_log(WLR_DEBUG, "Successfully committed output %s",
wlr_output->name);
if (state == &m->pending) {
wlr_output_state_finish(&m->pending);
wlr_output_state_init(&m->pending);
}
} else {
wlr_log(WLR_ERROR, "Failed to commit output %s", wlr_output->name);
// 即使提交失败,也清理状态避免积累
if (state == &m->pending) {
wlr_output_state_finish(&m->pending);
wlr_output_state_init(&m->pending);
}
return false;
}
return true;
}
void apply_tear_state(Monitor *m) {
if (wlr_scene_output_needs_frame(m->scene_output)) {
wlr_output_state_init(&m->pending);
if (wlr_scene_output_build_state(m->scene_output, &m->pending, NULL)) {
struct wlr_output_state *pending = &m->pending;
pending->tearing_page_flip = true;
if (!custom_wlr_scene_output_commit(m->scene_output, pending)) {
wlr_log(WLR_ERROR, "Failed to commit output %s",
m->scene_output->output->name);
}
} else {
wlr_log(WLR_ERROR, "Failed to build state for output %s",
m->scene_output->output->name);
wlr_output_state_finish(&m->pending);
}
}
}

View file

@ -68,7 +68,7 @@ void dwl_im_relay_set_focus(struct dwl_input_method_relay *relay,
/*------------------协议内部代码------------------------------*/
Monitor *output_from_wlr_output(struct wlr_output *wlr_output) {
Monitor *m;
Monitor *m = NULL;
wl_list_for_each(m, &mons, link) {
if (m->wlr_output == wlr_output) {
return m;
@ -77,15 +77,6 @@ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) {
return NULL;
}
Monitor *output_nearest_to(int lx, int ly) {
double closest_x, closest_y;
wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
&closest_y);
return output_from_wlr_output(
wlr_output_layout_output_at(output_layout, closest_x, closest_y));
}
bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; }
static bool
@ -111,6 +102,7 @@ get_keyboard_grab(KeyboardGroup *keyboard) {
return NULL;
}
// kb_group是一个物理键盘组它不应该被过滤掉
if (keyboard != kb_group)
return NULL;
@ -221,10 +213,10 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
struct wlr_xdg_surface *xdg_surface;
struct wlr_layer_surface_v1 *layer_surface;
struct wlr_scene_tree *tree;
Monitor *output;
Monitor *output = NULL;
struct wlr_xdg_positioner_rules pointer_rules;
struct wlr_box output_box;
int lx, ly;
int32_t lx, ly;
struct wlr_box popup_box;
if (!text_input || !relay->focused_surface ||
@ -254,7 +246,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
cursor_rect = (struct wlr_box){0};
}
output = output_nearest_to(cursor_rect.x, cursor_rect.y);
output = get_monitor_nearest_to(cursor_rect.x, cursor_rect.y);
if (!output_is_usable(output)) {
return;
}
@ -489,6 +481,7 @@ static void handle_text_input_enable(struct wl_listener *listener, void *data) {
update_popups_position(relay);
send_state_to_input_method(relay);
}
wlr_text_input_v3_send_done(text_input->input);
}
static void handle_text_input_disable(struct wl_listener *listener,

View file

@ -0,0 +1,997 @@
// bash on: https://gitlab.freedesktop.org/tokyo4j/wlroots/-/tree/ext-workspace
// TODO: remove this file
// refer: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5115
#include "wlr_ext_workspace_v1.h"
#include "ext-workspace-v1-protocol.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_output.h>
#define EXT_WORKSPACE_V1_VERSION 1
enum wlr_ext_workspace_v1_request_type {
WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE,
WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE,
WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE,
WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN,
WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE,
};
struct wlr_ext_workspace_v1_request {
enum wlr_ext_workspace_v1_request_type type;
// CREATE_WORKSPACE
char *name;
// CREATE_WORKSPACE / ASSIGN
struct wlr_ext_workspace_group_handle_v1 *group;
// ACTIVATE / DEACTIVATE / ASSIGN / REMOVE
struct wlr_ext_workspace_handle_v1 *workspace;
struct wl_list link; // wlr_ext_workspace_manager_v1_resource.requests
};
struct wlr_ext_workspace_v1_group_output {
struct wlr_output *output;
struct wlr_ext_workspace_group_handle_v1 *group;
struct wl_listener output_bind;
struct wl_listener output_destroy;
struct wl_list link;
};
// These structs wrap wl_resource of each interface to access the request queue
// (wlr_ext_workspace_manager_v1_resource.requests) assigned per manager
// resource
struct wlr_ext_workspace_manager_v1_resource {
struct wl_resource *resource;
struct wlr_ext_workspace_manager_v1 *manager;
struct wl_list requests; // wlr_ext_workspace_v1_request.link
struct wl_list workspace_resources; // wlr_ext_workspace_v1_resource.link
struct wl_list group_resources; // wlr_ext_workspace_group_v1_resource.link
struct wl_list link; // wlr_ext_workspace_manager_v1.resources
};
struct wlr_ext_workspace_group_v1_resource {
struct wl_resource *resource;
struct wlr_ext_workspace_group_handle_v1 *group;
struct wlr_ext_workspace_manager_v1_resource *manager;
struct wl_list link; // wlr_ext_workspace_group_v1.resources
struct wl_list
manager_resource_link; // wlr_ext_workspace_manager_v1_resource.group_resources
};
struct wlr_ext_workspace_v1_resource {
struct wl_resource *resource;
struct wlr_ext_workspace_handle_v1 *workspace;
struct wlr_ext_workspace_manager_v1_resource *manager;
struct wl_list link; // wlr_ext_workspace_v1.resources
struct wl_list
manager_resource_link; // wlr_ext_workspace_manager_v1_resource.workspace_resources
};
static const struct ext_workspace_group_handle_v1_interface group_impl;
static struct wlr_ext_workspace_group_v1_resource *
group_resource_from_resource(struct wl_resource *resource) {
assert(wl_resource_instance_of(
resource, &ext_workspace_group_handle_v1_interface, &group_impl));
return wl_resource_get_user_data(resource);
}
static const struct ext_workspace_handle_v1_interface workspace_impl;
static struct wlr_ext_workspace_v1_resource *
workspace_resource_from_resource(struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &ext_workspace_handle_v1_interface,
&workspace_impl));
return wl_resource_get_user_data(resource);
}
static const struct ext_workspace_manager_v1_interface manager_impl;
static struct wlr_ext_workspace_manager_v1_resource *
manager_resource_from_resource(struct wl_resource *resource) {
assert(wl_resource_instance_of(
resource, &ext_workspace_manager_v1_interface, &manager_impl));
return wl_resource_get_user_data(resource);
}
static void workspace_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void workspace_handle_activate(struct wl_client *client,
struct wl_resource *workspace_resource) {
struct wlr_ext_workspace_v1_resource *workspace_res =
workspace_resource_from_resource(workspace_resource);
if (!workspace_res) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(workspace_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE;
req->workspace = workspace_res->workspace;
wl_list_insert(workspace_res->manager->requests.prev, &req->link);
}
static void
workspace_handle_deactivate(struct wl_client *client,
struct wl_resource *workspace_resource) {
struct wlr_ext_workspace_v1_resource *workspace_res =
workspace_resource_from_resource(workspace_resource);
if (!workspace_res) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(workspace_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE;
req->workspace = workspace_res->workspace;
wl_list_insert(workspace_res->manager->requests.prev, &req->link);
}
static void workspace_handle_assign(struct wl_client *client,
struct wl_resource *workspace_resource,
struct wl_resource *group_resource) {
struct wlr_ext_workspace_v1_resource *workspace_res =
workspace_resource_from_resource(workspace_resource);
struct wlr_ext_workspace_group_v1_resource *group_res =
group_resource_from_resource(group_resource);
if (!workspace_res || !group_res) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(workspace_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN;
req->group = group_res->group;
req->workspace = workspace_res->workspace;
wl_list_insert(workspace_res->manager->requests.prev, &req->link);
}
static void workspace_handle_remove(struct wl_client *client,
struct wl_resource *workspace_resource) {
struct wlr_ext_workspace_v1_resource *workspace_res =
workspace_resource_from_resource(workspace_resource);
if (!workspace_res) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(workspace_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE;
req->workspace = workspace_res->workspace;
wl_list_insert(workspace_res->manager->requests.prev, &req->link);
}
static const struct ext_workspace_handle_v1_interface workspace_impl = {
.destroy = workspace_handle_destroy,
.activate = workspace_handle_activate,
.deactivate = workspace_handle_deactivate,
.assign = workspace_handle_assign,
.remove = workspace_handle_remove,
};
static void group_handle_create_workspace(struct wl_client *client,
struct wl_resource *group_resource,
const char *name) {
struct wlr_ext_workspace_group_v1_resource *group_res =
group_resource_from_resource(group_resource);
if (!group_res) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(group_resource);
return;
}
req->name = strdup(name);
if (!req->name) {
free(req);
wl_resource_post_no_memory(group_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE;
req->group = group_res->group;
wl_list_insert(group_res->manager->requests.prev, &req->link);
}
static void group_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct ext_workspace_group_handle_v1_interface group_impl = {
.create_workspace = group_handle_create_workspace,
.destroy = group_handle_destroy,
};
static void destroy_workspace_resource(
struct wlr_ext_workspace_v1_resource *workspace_res) {
wl_list_remove(&workspace_res->link);
wl_list_remove(&workspace_res->manager_resource_link);
wl_resource_set_user_data(workspace_res->resource, NULL);
free(workspace_res);
}
static void workspace_resource_destroy(struct wl_resource *resource) {
struct wlr_ext_workspace_v1_resource *workspace_res =
workspace_resource_from_resource(resource);
if (workspace_res) {
destroy_workspace_resource(workspace_res);
}
}
static struct wlr_ext_workspace_v1_resource *create_workspace_resource(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wlr_ext_workspace_manager_v1_resource *manager_res) {
struct wlr_ext_workspace_v1_resource *workspace_res =
calloc(1, sizeof(*workspace_res));
if (!workspace_res) {
return NULL;
}
struct wl_client *client = wl_resource_get_client(manager_res->resource);
workspace_res->resource =
wl_resource_create(client, &ext_workspace_handle_v1_interface,
wl_resource_get_version(manager_res->resource), 0);
if (!workspace_res->resource) {
free(workspace_res);
return NULL;
}
wl_resource_set_implementation(workspace_res->resource, &workspace_impl,
workspace_res, workspace_resource_destroy);
workspace_res->workspace = workspace;
workspace_res->manager = manager_res;
wl_list_insert(&workspace->resources, &workspace_res->link);
wl_list_insert(&manager_res->workspace_resources,
&workspace_res->manager_resource_link);
return workspace_res;
}
static void
destroy_group_resource(struct wlr_ext_workspace_group_v1_resource *group_res) {
wl_list_remove(&group_res->link);
wl_list_remove(&group_res->manager_resource_link);
wl_resource_set_user_data(group_res->resource, NULL);
free(group_res);
}
static void group_handle_resource_destroy(struct wl_resource *resource) {
struct wlr_ext_workspace_group_v1_resource *group_res =
group_resource_from_resource(resource);
if (group_res) {
destroy_group_resource(group_res);
}
}
static struct wlr_ext_workspace_group_v1_resource *create_group_resource(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_ext_workspace_manager_v1_resource *manager_res) {
struct wlr_ext_workspace_group_v1_resource *group_res =
calloc(1, sizeof(*group_res));
if (!group_res) {
return NULL;
}
struct wl_client *client = wl_resource_get_client(manager_res->resource);
uint32_t version = wl_resource_get_version(manager_res->resource);
group_res->resource = wl_resource_create(
client, &ext_workspace_group_handle_v1_interface, version, 0);
if (group_res->resource == NULL) {
free(group_res);
return NULL;
}
wl_resource_set_implementation(group_res->resource, &group_impl, group_res,
group_handle_resource_destroy);
group_res->group = group;
group_res->manager = manager_res;
wl_list_insert(&group->resources, &group_res->link);
wl_list_insert(&manager_res->group_resources,
&group_res->manager_resource_link);
return group_res;
}
static void destroy_request(struct wlr_ext_workspace_v1_request *req) {
wl_list_remove(&req->link);
free(req->name);
free(req);
}
static void manager_handle_commit(struct wl_client *client,
struct wl_resource *resource) {
struct wlr_ext_workspace_manager_v1_resource *manager_res =
manager_resource_from_resource(resource);
if (!manager_res) {
return;
}
struct wlr_ext_workspace_v1_request *req, *tmp;
wl_list_for_each_safe(req, tmp, &manager_res->requests, link) {
switch (req->type) {
case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE:;
struct wlr_ext_workspace_group_handle_v1_create_workspace_event
event = {
.name = req->name,
};
wl_signal_emit_mutable(&req->group->events.create_workspace,
&event);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE:
wl_signal_emit_mutable(&req->workspace->events.activate, NULL);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE:
wl_signal_emit_mutable(&req->workspace->events.deactivate, NULL);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN:
wl_signal_emit_mutable(&req->workspace->events.assign, req->group);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE:
wl_signal_emit_mutable(&req->workspace->events.remove, NULL);
break;
}
destroy_request(req);
}
}
static void handle_idle(void *data) {
struct wlr_ext_workspace_manager_v1 *manager = data;
struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &manager->resources, link) {
ext_workspace_manager_v1_send_done(manager_res->resource);
}
manager->idle_source = NULL;
}
static void
manager_schedule_done(struct wlr_ext_workspace_manager_v1 *manager) {
if (!manager->idle_source) {
manager->idle_source =
wl_event_loop_add_idle(manager->event_loop, handle_idle, manager);
}
}
static void
workspace_send_details(struct wlr_ext_workspace_v1_resource *workspace_res) {
struct wlr_ext_workspace_handle_v1 *workspace = workspace_res->workspace;
struct wl_resource *resource = workspace_res->resource;
ext_workspace_handle_v1_send_capabilities(resource, workspace->caps);
if (workspace->coordinates.size > 0) {
ext_workspace_handle_v1_send_coordinates(resource,
&workspace->coordinates);
}
if (workspace->name) {
ext_workspace_handle_v1_send_name(resource, workspace->name);
}
if (workspace->id) {
ext_workspace_handle_v1_send_id(resource, workspace->id);
}
ext_workspace_handle_v1_send_state(resource, workspace->state);
manager_schedule_done(workspace->manager);
}
static void manager_handle_stop(struct wl_client *client,
struct wl_resource *resource) {
ext_workspace_manager_v1_send_finished(resource);
wl_resource_destroy(resource);
}
static const struct ext_workspace_manager_v1_interface manager_impl = {
.commit = manager_handle_commit,
.stop = manager_handle_stop,
};
static void destroy_manager_resource(
struct wlr_ext_workspace_manager_v1_resource *manager_res) {
struct wlr_ext_workspace_v1_request *req, *tmp;
wl_list_for_each_safe(req, tmp, &manager_res->requests, link) {
destroy_request(req);
}
struct wlr_ext_workspace_v1_resource *workspace_res, *tmp2;
wl_list_for_each_safe(workspace_res, tmp2,
&manager_res->workspace_resources,
manager_resource_link) {
destroy_workspace_resource(workspace_res);
}
struct wlr_ext_workspace_group_v1_resource *group_res, *tmp3;
wl_list_for_each_safe(group_res, tmp3, &manager_res->group_resources,
manager_resource_link) {
destroy_group_resource(group_res);
}
wl_list_remove(&manager_res->link);
wl_resource_set_user_data(manager_res->resource, NULL);
free(manager_res);
}
static void manager_resource_destroy(struct wl_resource *resource) {
struct wlr_ext_workspace_manager_v1_resource *manager_res =
manager_resource_from_resource(resource);
if (manager_res) {
destroy_manager_resource(manager_res);
}
}
static void
group_send_details(struct wlr_ext_workspace_group_v1_resource *group_res) {
struct wlr_ext_workspace_group_handle_v1 *group = group_res->group;
struct wl_resource *resource = group_res->resource;
struct wl_client *client = wl_resource_get_client(resource);
ext_workspace_group_handle_v1_send_capabilities(resource, group->caps);
struct wlr_ext_workspace_v1_group_output *group_output;
wl_list_for_each(group_output, &group->outputs, link) {
struct wl_resource *output_resource;
wl_resource_for_each(output_resource,
&group_output->output->resources) {
if (wl_resource_get_client(output_resource) == client) {
ext_workspace_group_handle_v1_send_output_enter(
resource, output_resource);
}
}
}
manager_schedule_done(group->manager);
}
static void manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id) {
struct wlr_ext_workspace_manager_v1 *manager = data;
struct wlr_ext_workspace_manager_v1_resource *manager_res =
calloc(1, sizeof(*manager_res));
if (!manager_res) {
wl_client_post_no_memory(client);
return;
}
manager_res->manager = manager;
wl_list_init(&manager_res->requests);
wl_list_init(&manager_res->workspace_resources);
wl_list_init(&manager_res->group_resources);
manager_res->resource = wl_resource_create(
client, &ext_workspace_manager_v1_interface, version, id);
if (!manager_res->resource) {
free(manager_res);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(manager_res->resource, &manager_impl,
manager_res, manager_resource_destroy);
wl_list_insert(&manager->resources, &manager_res->link);
struct wlr_ext_workspace_group_handle_v1 *group;
wl_list_for_each(group, &manager->groups, link) {
struct wlr_ext_workspace_group_v1_resource *group_res =
create_group_resource(group, manager_res);
if (!group_res) {
wl_resource_post_no_memory(manager_res->resource);
continue;
}
ext_workspace_manager_v1_send_workspace_group(manager_res->resource,
group_res->resource);
group_send_details(group_res);
}
struct wlr_ext_workspace_handle_v1 *workspace;
wl_list_for_each(workspace, &manager->workspaces, link) {
struct wlr_ext_workspace_v1_resource *workspace_res =
create_workspace_resource(workspace, manager_res);
if (!workspace_res) {
wl_resource_post_no_memory(manager_res->resource);
continue;
}
ext_workspace_manager_v1_send_workspace(manager_res->resource,
workspace_res->resource);
workspace_send_details(workspace_res);
if (!workspace->group) {
continue;
}
struct wlr_ext_workspace_group_v1_resource *group_res;
wl_list_for_each(group_res, &workspace->group->resources, link) {
if (group_res->manager == manager_res) {
ext_workspace_group_handle_v1_send_workspace_enter(
group_res->resource, workspace_res->resource);
}
}
}
ext_workspace_manager_v1_send_done(manager_res->resource);
}
static void manager_handle_display_destroy(struct wl_listener *listener,
void *data) {
struct wlr_ext_workspace_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.destroy.listener_list));
struct wlr_ext_workspace_group_handle_v1 *group, *tmp;
wl_list_for_each_safe(group, tmp, &manager->groups, link) {
wlr_ext_workspace_group_handle_v1_destroy(group);
}
struct wlr_ext_workspace_handle_v1 *workspace, *tmp2;
wl_list_for_each_safe(workspace, tmp2, &manager->workspaces, link) {
wlr_ext_workspace_handle_v1_destroy(workspace);
}
struct wlr_ext_workspace_manager_v1_resource *manager_res, *tmp3;
wl_list_for_each_safe(manager_res, tmp3, &manager->resources, link) {
destroy_manager_resource(manager_res);
}
if (manager->idle_source) {
wl_event_source_remove(manager->idle_source);
}
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
struct wlr_ext_workspace_manager_v1 *
wlr_ext_workspace_manager_v1_create(struct wl_display *display,
uint32_t version) {
assert(version <= EXT_WORKSPACE_V1_VERSION);
struct wlr_ext_workspace_manager_v1 *manager = calloc(1, sizeof(*manager));
if (!manager) {
return NULL;
}
manager->global =
wl_global_create(display, &ext_workspace_manager_v1_interface, version,
manager, manager_bind);
if (!manager->global) {
free(manager);
return NULL;
}
manager->event_loop = wl_display_get_event_loop(display);
manager->display_destroy.notify = manager_handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
wl_list_init(&manager->groups);
wl_list_init(&manager->workspaces);
wl_list_init(&manager->resources);
wl_signal_init(&manager->events.destroy);
return manager;
}
struct wlr_ext_workspace_group_handle_v1 *
wlr_ext_workspace_group_handle_v1_create(
struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps) {
struct wlr_ext_workspace_group_handle_v1 *group = calloc(1, sizeof(*group));
if (!group) {
return NULL;
}
group->manager = manager;
group->caps = caps;
wl_list_init(&group->outputs);
wl_list_init(&group->resources);
wl_signal_init(&group->events.create_workspace);
wl_signal_init(&group->events.destroy);
wl_list_insert(manager->groups.prev, &group->link);
struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &manager->resources, link) {
struct wlr_ext_workspace_group_v1_resource *group_res =
create_group_resource(group, manager_res);
if (!group_res) {
continue;
}
ext_workspace_manager_v1_send_workspace_group(manager_res->resource,
group_res->resource);
group_send_details(group_res);
}
manager_schedule_done(manager);
return group;
}
static void
workspace_send_group(struct wlr_ext_workspace_handle_v1 *workspace,
struct wlr_ext_workspace_group_handle_v1 *group,
bool enter) {
struct wlr_ext_workspace_v1_resource *workspace_res;
wl_list_for_each(workspace_res, &workspace->resources, link) {
struct wlr_ext_workspace_group_v1_resource *group_res;
wl_list_for_each(group_res, &group->resources, link) {
if (group_res->manager != workspace_res->manager) {
continue;
}
if (enter) {
ext_workspace_group_handle_v1_send_workspace_enter(
group_res->resource, workspace_res->resource);
} else {
ext_workspace_group_handle_v1_send_workspace_leave(
group_res->resource, workspace_res->resource);
}
}
}
manager_schedule_done(workspace->manager);
}
static void
destroy_group_output(struct wlr_ext_workspace_v1_group_output *group_output) {
wl_list_remove(&group_output->output_bind.link);
wl_list_remove(&group_output->output_destroy.link);
wl_list_remove(&group_output->link);
free(group_output);
}
static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output, bool enter) {
struct wlr_ext_workspace_group_v1_resource *group_res;
wl_list_for_each(group_res, &group->resources, link) {
struct wl_client *client = wl_resource_get_client(group_res->resource);
struct wl_resource *output_resource;
wl_resource_for_each(output_resource, &output->resources) {
if (wl_resource_get_client(output_resource) != client) {
continue;
}
if (enter) {
ext_workspace_group_handle_v1_send_output_enter(
group_res->resource, output_resource);
} else {
ext_workspace_group_handle_v1_send_output_leave(
group_res->resource, output_resource);
}
}
}
manager_schedule_done(group->manager);
}
void wlr_ext_workspace_group_handle_v1_destroy(
struct wlr_ext_workspace_group_handle_v1 *group) {
if (!group) {
return;
}
wl_signal_emit_mutable(&group->events.destroy, NULL);
assert(wl_list_empty(&group->events.create_workspace.listener_list));
assert(wl_list_empty(&group->events.destroy.listener_list));
struct wlr_ext_workspace_handle_v1 *workspace;
wl_list_for_each(workspace, &group->manager->workspaces, link) {
if (workspace->group == group) {
workspace_send_group(workspace, group, false);
workspace->group = NULL;
}
}
struct wlr_ext_workspace_group_v1_resource *group_res, *tmp;
wl_list_for_each_safe(group_res, tmp, &group->resources, link) {
ext_workspace_group_handle_v1_send_removed(group_res->resource);
destroy_group_resource(group_res);
}
struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &group->manager->resources, link) {
struct wlr_ext_workspace_v1_request *req, *tmp2;
wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) {
if (req->group == group) {
destroy_request(req);
}
}
}
struct wlr_ext_workspace_v1_group_output *group_output, *tmp3;
wl_list_for_each_safe(group_output, tmp3, &group->outputs, link) {
group_send_output(group, group_output->output, false);
destroy_group_output(group_output);
}
manager_schedule_done(group->manager);
wl_list_remove(&group->link);
free(group);
}
static void handle_output_bind(struct wl_listener *listener, void *data) {
struct wlr_ext_workspace_v1_group_output *group_output =
wl_container_of(listener, group_output, output_bind);
struct wlr_output_event_bind *event = data;
struct wl_client *client = wl_resource_get_client(event->resource);
struct wlr_ext_workspace_group_v1_resource *group_res;
wl_list_for_each(group_res, &group_output->group->resources, link) {
if (wl_resource_get_client(group_res->resource) == client) {
ext_workspace_group_handle_v1_send_output_enter(group_res->resource,
event->resource);
}
}
manager_schedule_done(group_output->group->manager);
}
static void handle_output_destroy(struct wl_listener *listener, void *data) {
struct wlr_ext_workspace_v1_group_output *group_output =
wl_container_of(listener, group_output, output_destroy);
group_send_output(group_output->group, group_output->output, false);
destroy_group_output(group_output);
}
static struct wlr_ext_workspace_v1_group_output *
get_group_output(struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output) {
struct wlr_ext_workspace_v1_group_output *group_output;
wl_list_for_each(group_output, &group->outputs, link) {
if (group_output->output == output) {
return group_output;
}
}
return NULL;
}
void wlr_ext_workspace_group_handle_v1_output_enter(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output) {
if (get_group_output(group, output)) {
return;
}
struct wlr_ext_workspace_v1_group_output *group_output =
calloc(1, sizeof(*group_output));
if (!group_output) {
return;
}
group_output->output = output;
group_output->group = group;
wl_list_insert(&group->outputs, &group_output->link);
group_output->output_bind.notify = handle_output_bind;
wl_signal_add(&output->events.bind, &group_output->output_bind);
group_output->output_destroy.notify = handle_output_destroy;
wl_signal_add(&output->events.destroy, &group_output->output_destroy);
group_send_output(group, output, true);
}
void wlr_ext_workspace_group_handle_v1_output_leave(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output) {
struct wlr_ext_workspace_v1_group_output *group_output =
get_group_output(group, output);
if (!group_output) {
return;
}
group_send_output(group, output, false);
destroy_group_output(group_output);
}
struct wlr_ext_workspace_handle_v1 *
wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager,
const char *id, uint32_t caps) {
struct wlr_ext_workspace_handle_v1 *workspace =
calloc(1, sizeof(*workspace));
if (!workspace) {
return NULL;
}
workspace->manager = manager;
workspace->caps = caps;
if (id) {
workspace->id = strdup(id);
if (!workspace->id) {
free(workspace);
return NULL;
}
}
wl_list_init(&workspace->resources);
wl_array_init(&workspace->coordinates);
wl_signal_init(&workspace->events.activate);
wl_signal_init(&workspace->events.deactivate);
wl_signal_init(&workspace->events.remove);
wl_signal_init(&workspace->events.assign);
wl_signal_init(&workspace->events.destroy);
wl_list_insert(&manager->workspaces, &workspace->link);
struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &manager->resources, link) {
struct wlr_ext_workspace_v1_resource *workspace_res =
create_workspace_resource(workspace, manager_res);
if (!workspace_res) {
continue;
}
ext_workspace_manager_v1_send_workspace(manager_res->resource,
workspace_res->resource);
workspace_send_details(workspace_res);
}
manager_schedule_done(manager);
return workspace;
}
void wlr_ext_workspace_handle_v1_destroy(
struct wlr_ext_workspace_handle_v1 *workspace) {
if (!workspace) {
return;
}
wl_signal_emit_mutable(&workspace->events.destroy, NULL);
assert(wl_list_empty(&workspace->events.activate.listener_list));
assert(wl_list_empty(&workspace->events.deactivate.listener_list));
assert(wl_list_empty(&workspace->events.remove.listener_list));
assert(wl_list_empty(&workspace->events.assign.listener_list));
assert(wl_list_empty(&workspace->events.destroy.listener_list));
if (workspace->group) {
workspace_send_group(workspace, workspace->group, false);
}
struct wlr_ext_workspace_v1_resource *workspace_res, *tmp;
wl_list_for_each_safe(workspace_res, tmp, &workspace->resources, link) {
ext_workspace_handle_v1_send_removed(workspace_res->resource);
destroy_workspace_resource(workspace_res);
}
struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &workspace->manager->resources, link) {
struct wlr_ext_workspace_v1_request *req, *tmp2;
wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) {
if (req->workspace == workspace) {
destroy_request(req);
}
}
}
manager_schedule_done(workspace->manager);
wl_list_remove(&workspace->link);
wl_array_release(&workspace->coordinates);
free(workspace->id);
free(workspace->name);
free(workspace);
}
void wlr_ext_workspace_handle_v1_set_group(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wlr_ext_workspace_group_handle_v1 *group) {
if (workspace->group == group) {
return;
}
if (workspace->group) {
workspace_send_group(workspace, workspace->group, false);
}
workspace->group = group;
if (group) {
workspace_send_group(workspace, group, true);
}
}
void wlr_ext_workspace_handle_v1_set_name(
struct wlr_ext_workspace_handle_v1 *workspace, const char *name) {
assert(name);
if (workspace->name && strcmp(workspace->name, name) == 0) {
return;
}
free(workspace->name);
workspace->name = strdup(name);
if (workspace->name == NULL) {
return;
}
struct wlr_ext_workspace_v1_resource *workspace_res;
wl_list_for_each(workspace_res, &workspace->resources, link) {
ext_workspace_handle_v1_send_name(workspace_res->resource,
workspace->name);
}
manager_schedule_done(workspace->manager);
}
static bool array_equal(struct wl_array *a, struct wl_array *b) {
return (a->size == b->size) &&
(a->size == 0 || memcmp(a->data, b->data, a->size) == 0);
}
void wlr_ext_workspace_handle_v1_set_coordinates(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wl_array *coordinates) {
assert(coordinates);
if (array_equal(&workspace->coordinates, coordinates)) {
return;
}
wl_array_release(&workspace->coordinates);
wl_array_init(&workspace->coordinates);
wl_array_copy(&workspace->coordinates, coordinates);
struct wlr_ext_workspace_v1_resource *workspace_res;
wl_list_for_each(workspace_res, &workspace->resources, link) {
ext_workspace_handle_v1_send_coordinates(workspace_res->resource,
&workspace->coordinates);
}
manager_schedule_done(workspace->manager);
}
static void workspace_set_state(struct wlr_ext_workspace_handle_v1 *workspace,
enum ext_workspace_handle_v1_state state,
bool enabled) {
uint32_t old_state = workspace->state;
if (enabled) {
workspace->state |= state;
} else {
workspace->state &= ~state;
}
if (old_state == workspace->state) {
return;
}
struct wlr_ext_workspace_v1_resource *workspace_res;
wl_list_for_each(workspace_res, &workspace->resources, link) {
ext_workspace_handle_v1_send_state(workspace_res->resource,
workspace->state);
}
manager_schedule_done(workspace->manager);
}
void wlr_ext_workspace_handle_v1_set_active(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE,
enabled);
}
void wlr_ext_workspace_handle_v1_set_urgent(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_URGENT,
enabled);
}
void wlr_ext_workspace_handle_v1_set_hidden(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN,
enabled);
}

View file

@ -0,0 +1,106 @@
// bash on: https://gitlab.freedesktop.org/tokyo4j/wlroots/-/tree/ext-workspace
// TODO: remove this file
// refer: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5115
#include <wayland-protocols/ext-workspace-v1-enum.h>
#include <wayland-server-core.h>
struct wlr_output;
struct wlr_ext_workspace_manager_v1 {
struct wl_global *global;
struct wl_list groups; // wlr_ext_workspace_group_handle_v1.link
struct wl_list workspaces; // wlr_ext_workspace_handle_v1.link
struct {
struct wl_signal destroy;
} events;
struct {
struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link
struct wl_event_source *idle_source;
struct wl_event_loop *event_loop;
struct wl_listener display_destroy;
};
};
struct wlr_ext_workspace_group_handle_v1_create_workspace_event {
const char *name;
};
struct wlr_ext_workspace_group_handle_v1 {
struct wlr_ext_workspace_manager_v1 *manager;
uint32_t caps; // ext_workspace_group_handle_v1_group_capabilities
struct {
struct wl_signal
create_workspace; // wlr_ext_workspace_group_handle_v1_create_workspace_event
struct wl_signal destroy;
} events;
struct wl_list link; // wlr_ext_workspace_manager_v1.groups
struct {
struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link
struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link
};
};
struct wlr_ext_workspace_handle_v1 {
struct wlr_ext_workspace_manager_v1 *manager;
struct wlr_ext_workspace_group_handle_v1 *group; // May be NULL
char *id;
char *name;
struct wl_array coordinates;
uint32_t caps; // ext_workspace_handle_v1_workspace_capabilities
uint32_t state; // ext_workspace_handle_v1_state
struct {
struct wl_signal activate;
struct wl_signal deactivate;
struct wl_signal remove;
struct wl_signal assign; // wlr_ext_workspace_group_handle_v1
struct wl_signal destroy;
} events;
struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces
struct {
struct wl_list resources; // wlr_ext_workspace_v1_resource.link
};
};
struct wlr_ext_workspace_manager_v1 *
wlr_ext_workspace_manager_v1_create(struct wl_display *display,
uint32_t version);
struct wlr_ext_workspace_group_handle_v1 *
wlr_ext_workspace_group_handle_v1_create(
struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps);
void wlr_ext_workspace_group_handle_v1_destroy(
struct wlr_ext_workspace_group_handle_v1 *group);
void wlr_ext_workspace_group_handle_v1_output_enter(
struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output);
void wlr_ext_workspace_group_handle_v1_output_leave(
struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output);
struct wlr_ext_workspace_handle_v1 *
wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager,
const char *id, uint32_t caps);
void wlr_ext_workspace_handle_v1_destroy(
struct wlr_ext_workspace_handle_v1 *workspace);
void wlr_ext_workspace_handle_v1_set_group(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wlr_ext_workspace_group_handle_v1 *group);
void wlr_ext_workspace_handle_v1_set_name(
struct wlr_ext_workspace_handle_v1 *workspace, const char *name);
void wlr_ext_workspace_handle_v1_set_coordinates(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wl_array *coordinates);
void wlr_ext_workspace_handle_v1_set_active(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
void wlr_ext_workspace_handle_v1_set_urgent(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
void wlr_ext_workspace_handle_v1_set_hidden(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);

View file

@ -1,5 +1,5 @@
bool check_hit_no_border(Client *c) {
int i;
int32_t i;
bool hit_no_border = false;
if (!render_border) {
hit_no_border = true;
@ -12,13 +12,15 @@ 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;
}
return hit_no_border;
}
Client *termforwin(Client *w) {
Client *c;
Client *c = NULL;
if (!w->pid || w->isterm || w->noswallow)
return NULL;
@ -37,14 +39,25 @@ Client *get_client_by_id_or_title(const char *arg_id, const char *arg_title) {
const char *appid, *title;
Client *c = NULL;
wl_list_for_each(c, &clients, link) {
if (c->mon != selmon) {
if (!scratchpad_cross_monitor && c->mon != selmon) {
continue;
}
if (!(appid = client_get_appid(c)))
if (c->swallowedby) {
appid = client_get_appid(c->swallowedby);
title = client_get_title(c->swallowedby);
} else {
appid = client_get_appid(c);
title = client_get_title(c);
}
if (!appid) {
appid = broken;
if (!(title = client_get_title(c)))
}
if (!title) {
title = broken;
}
if (arg_id && strncmp(arg_id, "none", 4) == 0)
arg_id = NULL;
@ -63,16 +76,19 @@ Client *get_client_by_id_or_title(const char *arg_id, const char *arg_title) {
return target_client;
}
struct wlr_box // 计算客户端居中坐标
setclient_coordinate_center(Client *c, struct wlr_box geom, int offsetx,
int offsety) {
setclient_coordinate_center(Client *c, Monitor *tm, struct wlr_box geom,
int32_t offsetx, int32_t offsety) {
struct wlr_box tempbox;
int offset = 0;
int len = 0;
Monitor *m = c->mon ? c->mon : selmon;
int32_t offset = 0;
int32_t len = 0;
Monitor *m = tm ? tm : selmon;
unsigned int cbw = check_hit_no_border(c) ? c->bw : 0;
if (!m)
return geom;
if (!c->no_force_center) {
uint32_t cbw = check_hit_no_border(c) ? c->bw : 0;
if (!c->no_force_center && m) {
tempbox.x = m->w.x + (m->w.width - geom.width) / 2;
tempbox.y = m->w.y + (m->w.height - geom.height) / 2;
} else {
@ -84,7 +100,7 @@ setclient_coordinate_center(Client *c, struct wlr_box geom, int offsetx,
tempbox.height = geom.height;
if (offsetx != 0) {
len = (m->w.width - c->geom.width - 2 * m->gappoh) / 2;
len = (m->w.width - tempbox.width - 2 * m->gappoh) / 2;
offset = len * (offsetx / 100.0);
tempbox.x += offset;
@ -97,7 +113,7 @@ setclient_coordinate_center(Client *c, struct wlr_box geom, int offsetx,
}
}
if (offsety != 0) {
len = (m->w.height - c->geom.height - 2 * m->gappov) / 2;
len = (m->w.height - tempbox.height - 2 * m->gappov) / 2;
offset = len * (offsety / 100.0);
tempbox.y += offset;
@ -121,15 +137,16 @@ static bool is_window_rule_matches(const ConfigWinRule *r, const char *appid,
regex_match(r->title, title));
}
Client *center_select(Monitor *m) {
Client *center_tiled_select(Monitor *m) {
Client *c = NULL;
Client *target_c = NULL;
long int mini_distance = -1;
int dirx, diry;
long int distance;
int64_t mini_distance = -1;
int32_t dirx, diry;
int64_t distance;
wl_list_for_each(c, &clients, link) {
if (c && VISIBLEON(c, m) && client_surface(c)->mapped &&
!c->isfloating && !client_is_unmanaged(c)) {
if (c && VISIBLEON(c, m) && ISSCROLLTILED(c) &&
client_surface(c)->mapped && !c->isfloating &&
!client_is_unmanaged(c)) {
dirx = c->geom.x + c->geom.width / 2 - (m->w.x + m->w.width / 2);
diry = c->geom.y + c->geom.height / 2 - (m->w.y + m->w.height / 2);
distance = dirx * dirx + diry * diry;
@ -142,15 +159,15 @@ Client *center_select(Monitor *m) {
return target_c;
}
Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
bool align) {
Client *c;
bool ignore_align) {
Client *c = NULL;
Client **tempClients = NULL; // 初始化为 NULL
int last = -1;
int32_t last = -1;
// 第一次遍历,计算客户端数量
wl_list_for_each(c, &clients, link) {
if (c && (findfloating || !c->isfloating) && !c->isunglobal &&
(focus_cross_monitor || c->mon == selmon) &&
(focus_cross_monitor || c->mon == tc->mon) &&
(c->tags & c->mon->tagset[c->mon->seltags])) {
last++;
}
@ -171,39 +188,30 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
last = -1;
wl_list_for_each(c, &clients, link) {
if (c && (findfloating || !c->isfloating) && !c->isunglobal &&
(focus_cross_monitor || c->mon == selmon) &&
(focus_cross_monitor || c->mon == tc->mon) &&
(c->tags & c->mon->tagset[c->mon->seltags])) {
last++;
tempClients[last] = c;
}
}
int sel_x = tc->geom.x;
int sel_y = tc->geom.y;
long long int distance = LLONG_MAX;
int32_t sel_x = tc->geom.x;
int32_t sel_y = tc->geom.y;
int64_t distance = LLONG_MAX;
int64_t same_monitor_distance = LLONG_MAX;
Client *tempFocusClients = NULL;
Client *tempSameMonitorFocusClients = NULL;
switch (arg->i) {
case UP:
for (int _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y &&
tempClients[_i]->geom.x == sel_x) {
int dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
}
}
if (!tempFocusClients && !align) {
for (int _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y) {
int dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance =
if (!ignore_align) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y &&
tempClients[_i]->geom.x == sel_x &&
tempClients[_i]->mon == tc->mon) {
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;
@ -212,27 +220,56 @@ 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) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y) {
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];
}
}
}
}
break;
case DOWN:
for (int _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y &&
tempClients[_i]->geom.x == sel_x) {
int dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
}
}
if (!tempFocusClients && !align) {
for (int _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y) {
int dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance =
if (!ignore_align) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y &&
tempClients[_i]->geom.x == sel_x &&
tempClients[_i]->mon == tc->mon) {
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;
@ -241,27 +278,56 @@ 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) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y) {
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];
}
}
}
}
break;
case LEFT:
for (int _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x &&
tempClients[_i]->geom.y == sel_y) {
int dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
}
}
if (!tempFocusClients && !align) {
for (int _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x) {
int dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance =
if (!ignore_align) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x &&
tempClients[_i]->geom.y == sel_y &&
tempClients[_i]->mon == tc->mon) {
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;
@ -270,32 +336,101 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
}
}
}
break;
case RIGHT:
for (int _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x &&
tempClients[_i]->geom.y == sel_y) {
int dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
}
}
if (!tempFocusClients && !align) {
for (int _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x) {
int dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance =
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) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x) {
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];
}
}
}
}
break;
case RIGHT:
if (!ignore_align) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x &&
tempClients[_i]->geom.y == sel_y &&
tempClients[_i]->mon == tc->mon) {
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 (!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) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x) {
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];
}
}
}
}
@ -303,7 +438,11 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
}
free(tempClients); // 释放内存
return tempFocusClients;
if (tempSameMonitorFocusClients) {
return tempSameMonitorFocusClients;
} else {
return tempFocusClients;
}
}
Client *direction_select(const Arg *arg) {
@ -313,20 +452,22 @@ Client *direction_select(const Arg *arg) {
if (!tc)
return NULL;
if (tc && (tc->isfullscreen || tc->ismaxmizescreen)) {
// 不支持全屏窗口的焦点切换
if (tc && (tc->isfullscreen || tc->ismaximizescreen) &&
(!is_scroller_layout(selmon) || tc->isfloating)) {
return NULL;
}
return find_client_by_direction(tc, arg, true, false);
return find_client_by_direction(
tc, arg, true,
(is_scroller_layout(selmon) || is_centertile_layout(selmon)) &&
!selmon->isoverview);
}
/* We probably should change the name of this, it sounds like
* will focus the topmost client of this mon, when actually will
* only return that client */
Client * // 0.5
focustop(Monitor *m) {
Client *c;
Client *focustop(Monitor *m) {
Client *c = NULL;
wl_list_for_each(c, &fstack, flink) {
if (c->iskilling || c->isunglobal)
continue;
@ -335,3 +476,159 @@ focustop(Monitor *m) {
}
return NULL;
}
Client *get_next_stack_client(Client *c, bool reverse) {
if (!c || !c->mon)
return NULL;
Client *next = NULL;
if (reverse) {
wl_list_for_each_reverse(next, &c->link, link) {
if (&next->link == &clients)
continue; /* wrap past the sentinel node */
if (next->isunglobal)
continue;
if (next != c && next->mon && VISIBLEON(next, c->mon))
return next;
}
} else {
wl_list_for_each(next, &c->link, link) {
if (&next->link == &clients)
continue; /* wrap past the sentinel node */
if (next->isunglobal)
continue;
if (next != c && next->mon && VISIBLEON(next, c->mon))
return next;
}
}
return NULL;
}
float *get_border_color(Client *c) {
if (c->mon != selmon) {
return bordercolor;
} else if (c->isurgent) {
return urgentcolor;
} else if (c->is_in_scratchpad && selmon && c == selmon->sel) {
return scratchpadcolor;
} else if (c->isglobal && selmon && c == selmon->sel) {
return globalcolor;
} else if (c->isoverlay && selmon && c == selmon->sel) {
return overlaycolor;
} else if (c->ismaximizescreen && selmon && c == selmon->sel) {
return maximizescreencolor;
} else if (selmon && c == selmon->sel) {
return focuscolor;
} else {
return bordercolor;
}
}
int32_t is_single_bit_set(uint32_t x) { return x && !(x & (x - 1)); }
bool client_only_in_one_tag(Client *c) {
uint32_t masked = c->tags & TAGMASK;
if (is_single_bit_set(masked)) {
return true;
} else {
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

@ -1,5 +1,5 @@
pid_t getparentprocess(pid_t p) {
unsigned int v = 0;
uint32_t v = 0;
FILE *f;
char buf[256];
@ -19,36 +19,22 @@ pid_t getparentprocess(pid_t p) {
return (pid_t)v;
}
int isdescprocess(pid_t p, pid_t c) {
int32_t isdescprocess(pid_t p, pid_t c) {
while (p != c && c != 0)
c = getparentprocess(c);
return (int)c;
return (int32_t)c;
}
char *get_autostart_path(char *autostart_path, unsigned int buf_size) {
const char *mangoconfig = getenv("MANGOCONFIG");
void get_layout_abbr(char *abbr, const char *full_name) {
// 清空输出缓冲区
abbr[0] = '\0';
if (mangoconfig && mangoconfig[0] != '\0') {
snprintf(autostart_path, buf_size, "%s/autostart.sh", mangoconfig);
} else {
const char *homedir = getenv("HOME");
if (!homedir) {
fprintf(stderr, "Error: HOME environment variable not set.\n");
return NULL;
}
snprintf(autostart_path, buf_size, "%s/.config/mango/autostart.sh",
homedir);
}
return autostart_path;
}
char *get_layout_abbr(const char *full_name) {
// 1. 尝试在映射表中查找
for (int i = 0; layout_mappings[i].full_name != NULL; i++) {
for (int32_t i = 0; layout_mappings[i].full_name != NULL; i++) {
if (strcmp(full_name, layout_mappings[i].full_name) == 0) {
return strdup(layout_mappings[i].abbr);
strcpy(abbr, layout_mappings[i].abbr);
return;
}
}
@ -56,48 +42,39 @@ char *get_layout_abbr(const char *full_name) {
const char *open = strrchr(full_name, '(');
const char *close = strrchr(full_name, ')');
if (open && close && close > open) {
unsigned int len = close - open - 1;
uint32_t len = close - open - 1;
if (len > 0 && len <= 4) {
char *abbr = malloc(len + 1);
if (abbr) {
// 提取并转换为小写
for (unsigned int j = 0; j < len; j++) {
abbr[j] = tolower(open[j + 1]);
}
abbr[len] = '\0';
return abbr;
// 提取并转换为小写
for (uint32_t j = 0; j < len; j++) {
abbr[j] = tolower(open[j + 1]);
}
abbr[len] = '\0';
return;
}
}
// 3. 提取前2-3个字母并转换为小写
char *abbr = malloc(4);
if (abbr) {
unsigned int j = 0;
for (unsigned int i = 0; full_name[i] != '\0' && j < 3; i++) {
if (isalpha(full_name[i])) {
abbr[j++] = tolower(full_name[i]);
}
uint32_t j = 0;
for (uint32_t i = 0; full_name[i] != '\0' && j < 3; i++) {
if (isalpha(full_name[i])) {
abbr[j++] = tolower(full_name[i]);
}
abbr[j] = '\0';
}
abbr[j] = '\0';
// 确保至少2个字符
if (j >= 2)
return abbr;
free(abbr);
// 确保至少2个字符
if (j >= 2) {
return;
}
// 4. 回退方案:使用首字母小写
char *fallback = malloc(3);
if (fallback) {
fallback[0] = tolower(full_name[0]);
fallback[1] = full_name[1] ? tolower(full_name[1]) : '\0';
fallback[2] = '\0';
return fallback;
if (j == 1) {
abbr[1] = full_name[1] ? tolower(full_name[1]) : '\0';
abbr[2] = '\0';
} else {
// 5. 最终回退:返回 "xx"
strcpy(abbr, "xx");
}
// 5. 最终回退:返回 "xx"
return strdup("xx");
}
void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
@ -106,30 +83,44 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
struct wlr_surface *surface = NULL;
Client *c = NULL;
LayerSurface *l = NULL;
int layer;
int32_t layer;
for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
// ignore text-input layer
if (layer == LyrIMPopup)
continue;
if (layer == LyrFadeOut)
continue;
if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
continue;
if (!node->enabled)
continue;
if (node->type == WLR_SCENE_NODE_BUFFER)
surface = wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node))
->surface;
/* Walk the tree to find a node that knows the client */
for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
c = pnode->data;
if (c && c->type == LayerShell) {
/* start from the topmost layer,
find a sureface that can be focused by pointer,
impopup neither a client nor a layer surface.*/
if (layer == LyrIMPopup) {
c = NULL;
l = pnode->data;
l = NULL;
} else {
for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
c = pnode->data;
if (c && c->type == LayerShell) {
c = NULL;
l = pnode->data;
}
}
if (node->type == WLR_SCENE_NODE_RECT) {
if (c) {
surface = client_surface(c);
}
break;
}
}

View file

@ -16,17 +16,63 @@ Monitor *dirtomon(enum wlr_direction dir) {
}
bool is_scroller_layout(Monitor *m) {
if (strcmp(m->pertag->ltidxs[m->pertag->curtag]->name, "scroller") == 0)
if (m->pertag->ltidxs[m->pertag->curtag]->id == SCROLLER)
return true;
if (strcmp(m->pertag->ltidxs[m->pertag->curtag]->name,
"vertical_scroller") == 0)
if (m->pertag->ltidxs[m->pertag->curtag]->id == VERTICAL_SCROLLER)
return true;
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) {
Client *c = NULL;
uint32_t status = 0;
wl_list_for_each(c, &clients, link) {
if (c->mon == m && c->tags & 1 << (tag - 1) & TAGMASK) {
if (c->isurgent) {
status = 2;
break;
}
status = 1;
}
}
return status;
}
uint32_t get_tags_first_tag_num(uint32_t source_tags) {
uint32_t i, tag;
tag = 0;
if (!source_tags) {
return selmon->pertag->curtag;
}
for (i = 0; !(tag & 1) && source_tags != 0 && i < LENGTH(tags); i++) {
tag = source_tags >> i;
}
if (i == 1) {
return 1;
} else if (i > 9) {
return 9;
} else {
return i;
}
}
// 获取tags中最前面的tag的tagmask
unsigned int get_tags_first_tag(unsigned int source_tags) {
unsigned int i, tag;
uint32_t get_tags_first_tag(uint32_t source_tags) {
uint32_t i, tag;
tag = 0;
if (!source_tags) {
@ -50,3 +96,81 @@ Monitor *xytomon(double x, double y) {
struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
return o ? o->data : NULL;
}
Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly) {
double closest_x, closest_y;
wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
&closest_y);
return output_from_wlr_output(
wlr_output_layout_output_at(output_layout, closest_x, closest_y));
}
bool match_monitor_spec(char *spec, Monitor *m) {
if (!spec || !m)
return false;
// if the spec does not contain a colon, treat it as a match on the monitor
// name
if (strchr(spec, ':') == NULL) {
return regex_match(spec, m->wlr_output->name);
}
char *spec_copy = strdup(spec);
if (!spec_copy)
return false;
char *name_rule = NULL;
char *make_rule = NULL;
char *model_rule = NULL;
char *serial_rule = NULL;
char *token = strtok(spec_copy, "&&");
while (token) {
char *colon = strchr(token, ':');
if (colon) {
*colon = '\0';
char *key = token;
char *value = colon + 1;
if (strcmp(key, "name") == 0)
name_rule = strdup(value);
else if (strcmp(key, "make") == 0)
make_rule = strdup(value);
else if (strcmp(key, "model") == 0)
model_rule = strdup(value);
else if (strcmp(key, "serial") == 0)
serial_rule = strdup(value);
}
token = strtok(NULL, "&&");
}
bool match = true;
if (name_rule) {
if (!regex_match(name_rule, m->wlr_output->name))
match = false;
}
if (make_rule) {
if (!m->wlr_output->make || strcmp(make_rule, m->wlr_output->make) != 0)
match = false;
}
if (model_rule) {
if (!m->wlr_output->model ||
strcmp(model_rule, m->wlr_output->model) != 0)
match = false;
}
if (serial_rule) {
if (!m->wlr_output->serial ||
strcmp(serial_rule, m->wlr_output->serial) != 0)
match = false;
}
free(spec_copy);
free(name_rule);
free(make_rule);
free(model_rule);
free(serial_rule);
return match;
}

929
src/layout/arrange.h Normal file
View file

@ -0,0 +1,929 @@
void save_old_size_per(Monitor *m) {
Client *c = NULL;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
}
}
}
void restore_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
if (!m || !c)
return;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc)) {
fc->old_ismaster = fc->ismaster;
}
}
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
pre_caculate_before_arrange(m, false, false, true);
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
if (current_layout->id == SCROLLER ||
current_layout->id == VERTICAL_SCROLLER || current_layout->id == GRID ||
current_layout->id == VERTICAL_GRID || current_layout->id == DECK ||
current_layout->id == VERTICAL_DECK || current_layout->id == MONOCLE) {
return;
}
if (current_layout->id == CENTER_TILE) {
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && !c->ismaster) {
set_size_per(m, fc);
}
}
return;
}
// it is possible that the current floating window is moved to another tag,
// but the tag has not executed save_old_size_per
// so it must be judged whether their old size values are initial values
if (!c->ismaster && c->old_stack_inner_per < 1.0 &&
c->old_stack_inner_per > 0.0f && c->stack_inner_per < 1.0 &&
c->stack_inner_per > 0.0f) {
c->stack_inner_per = (1.0 - c->stack_inner_per) *
c->old_stack_inner_per /
(1.0 - c->old_stack_inner_per);
}
if (c->ismaster && c->old_master_inner_per < 1.0 &&
c->old_master_inner_per > 0.0f && c->master_inner_per < 1.0 &&
c->master_inner_per > 0.0f) {
c->master_inner_per = (1.0 - c->master_inner_per) *
c->old_master_inner_per /
(1.0 - c->old_master_inner_per);
}
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c && !fc->ismaster &&
fc->old_ismaster && fc->old_stack_inner_per < 1.0 &&
fc->old_stack_inner_per > 0.0f && fc->stack_inner_per < 1.0 &&
fc->stack_inner_per > 0.0f) {
fc->stack_inner_per = (1.0 - fc->stack_inner_per) *
fc->old_stack_inner_per /
(1.0 - fc->old_stack_inner_per);
fc->old_ismaster = false;
}
}
}
void set_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
bool found = false;
if (!m || !c)
return;
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) {
if (current_layout->id == CENTER_TILE &&
(fc->isleftstack ^ c->isleftstack))
continue;
c->master_mfact_per = fc->master_mfact_per;
c->master_inner_per = fc->master_inner_per;
c->stack_inner_per = fc->stack_inner_per;
found = true;
break;
}
}
if (!found) {
c->master_mfact_per = m->pertag->mfacts[m->pertag->curtag];
c->master_inner_per = 1.0f;
c->stack_inner_per = 1.0f;
}
}
void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time,
int32_t type) {
Client *tc = NULL;
float delta_x, delta_y;
Client *next = NULL;
Client *prev = NULL;
Client *nextnext = NULL;
Client *prevprev = NULL;
struct wl_list *node;
bool begin_find_nextnext = false;
bool begin_find_prevprev = false;
// 从当前节点的下一个开始遍历
for (node = grabc->link.next; node != &clients; node = node->next) {
tc = wl_container_of(node, tc, link);
if (begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
nextnext = tc;
break;
}
if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
next = tc;
begin_find_nextnext = true;
continue;
}
}
// 从当前节点的上一个开始遍历
for (node = grabc->link.prev; node != &clients; node = node->prev) {
tc = wl_container_of(node, tc, link);
if (begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
prevprev = tc;
break;
}
if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
prev = tc;
begin_find_prevprev = true;
continue;
}
}
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
// 记录初始状态
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->cursor_in_upper_half =
cursor->y < grabc->geom.y + grabc->geom.height / 2;
grabc->cursor_in_left_half =
cursor->x < grabc->geom.x + grabc->geom.width / 2;
// 记录初始几何信息
grabc->drag_begin_geom = grabc->geom;
} else {
// 计算相对于屏幕尺寸的比例变化
if (isdrag) {
offsetx = cursor->x - drag_begin_cursorx;
offsety = cursor->y - drag_begin_cursory;
} else {
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->drag_begin_geom = grabc->geom;
grabc->cursor_in_upper_half = true;
grabc->cursor_in_left_half = false;
}
if (grabc->ismaster) {
delta_x = (float)(offsetx) * (grabc->old_master_mfact_per) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_master_inner_per) /
grabc->drag_begin_geom.height;
} else {
delta_x = (float)(offsetx) * (1 - grabc->old_master_mfact_per) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_stack_inner_per) /
grabc->drag_begin_geom.height;
}
bool moving_up;
bool moving_down;
if (!isdrag) {
moving_up = offsety < 0 ? true : false;
moving_down = offsety > 0 ? true : false;
} else {
moving_up = cursor->y < drag_begin_cursory;
moving_down = cursor->y > drag_begin_cursory;
}
if (grabc->ismaster && !prev) {
if (moving_up) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
} else if (grabc->ismaster && next && !next->ismaster) {
if (moving_up) {
delta_y = fabsf(delta_y);
} else {
delta_y = -fabsf(delta_y);
}
} else if (!grabc->ismaster && prev && prev->ismaster) {
if (moving_up) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
} else if (!grabc->ismaster && !next) {
if (moving_up) {
delta_y = fabsf(delta_y);
} else {
delta_y = -fabsf(delta_y);
}
} else if (type == CENTER_TILE && !grabc->ismaster && !nextnext) {
if (moving_up) {
delta_y = fabsf(delta_y);
} else {
delta_y = -fabsf(delta_y);
}
} else if (type == CENTER_TILE && !grabc->ismaster && prevprev &&
prevprev->ismaster) {
if (moving_up) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
} else if ((grabc->cursor_in_upper_half && moving_up) ||
(!grabc->cursor_in_upper_half && moving_down)) {
// 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度
delta_y = fabsf(delta_y);
delta_y = delta_y * 2;
} else {
// 其他情况 → 减小高度
delta_y = -fabsf(delta_y);
delta_y = delta_y * 2;
}
if (!grabc->ismaster && grabc->isleftstack && type == CENTER_TILE) {
delta_x = delta_x * -1.0f;
}
if (grabc->ismaster && type == CENTER_TILE &&
grabc->cursor_in_left_half) {
delta_x = delta_x * -1.0f;
}
if (grabc->ismaster && type == CENTER_TILE) {
delta_x = delta_x * 2;
}
if (type == RIGHT_TILE) {
delta_x = delta_x * -1.0f;
}
// 直接设置新的比例,基于初始值 + 变化量
float new_master_mfact_per = grabc->old_master_mfact_per + delta_x;
float new_master_inner_per = grabc->old_master_inner_per + delta_y;
float new_stack_inner_per = grabc->old_stack_inner_per + delta_y;
// 应用限制,确保比例在合理范围内
new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per));
new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per));
new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per));
// 应用到所有平铺窗口
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
if (!isdrag && tc != grabc) {
if (!tc->ismaster && new_stack_inner_per != 1.0f &&
grabc->old_stack_inner_per != 1.0f &&
(type != CENTER_TILE ||
!(grabc->isleftstack ^ tc->isleftstack)))
tc->stack_inner_per = (1 - new_stack_inner_per) /
(1 - grabc->old_stack_inner_per) *
tc->stack_inner_per;
if (tc->ismaster && new_master_inner_per != 1.0f &&
grabc->old_master_inner_per != 1.0f)
tc->master_inner_per =
(1.0f - new_master_inner_per) /
(1.0f - grabc->old_master_inner_per) *
tc->master_inner_per;
}
tc->master_mfact_per = new_master_mfact_per;
}
}
grabc->master_inner_per = new_master_inner_per;
grabc->stack_inner_per = new_stack_inner_per;
if (!isdrag) {
arrange(grabc->mon, false, false);
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_tile_refresh_interval) {
arrange(grabc->mon, false, false);
last_apply_drap_time = time;
}
}
}
void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, int32_t type) {
Client *tc = NULL;
float delta_x, delta_y;
Client *next = NULL;
Client *prev = NULL;
struct wl_list *node;
// 从当前节点的下一个开始遍历
for (node = grabc->link.next; node != &clients; node = node->next) {
tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
next = tc;
break;
}
}
// 从当前节点的上一个开始遍历
for (node = grabc->link.prev; node != &clients; node = node->prev) {
tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
prev = tc;
break;
}
}
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
// 记录初始状态
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->cursor_in_upper_half =
cursor->y < grabc->geom.y + grabc->geom.height / 2;
grabc->cursor_in_left_half =
cursor->x < grabc->geom.x + grabc->geom.width / 2;
// 记录初始几何信息
grabc->drag_begin_geom = grabc->geom;
} else {
// 计算相对于屏幕尺寸的比例变化
// 计算相对于屏幕尺寸的比例变化
if (isdrag) {
offsetx = cursor->x - drag_begin_cursorx;
offsety = cursor->y - drag_begin_cursory;
} else {
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->drag_begin_geom = grabc->geom;
grabc->cursor_in_upper_half = true;
grabc->cursor_in_left_half = false;
}
if (grabc->ismaster) {
// 垂直版本:左右移动调整高度比例,上下移动调整宽度比例
delta_x = (float)(offsetx) * (grabc->old_master_inner_per) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_master_mfact_per) /
grabc->drag_begin_geom.height;
} else {
delta_x = (float)(offsetx) * (grabc->old_stack_inner_per) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (1 - grabc->old_master_mfact_per) /
grabc->drag_begin_geom.height;
}
bool moving_left;
bool moving_right;
if (!isdrag) {
moving_left = offsetx < 0 ? true : false;
moving_right = offsetx > 0 ? true : false;
} else {
moving_left = cursor->x < drag_begin_cursorx;
moving_right = cursor->x > drag_begin_cursorx;
}
// 调整主区域和栈区域的高度比例(垂直分割)
if (grabc->ismaster && !prev) {
if (moving_left) {
delta_x = -fabsf(delta_x); // 向上移动减少主区域高度
} else {
delta_x = fabsf(delta_x); // 向下移动增加主区域高度
}
} else if (grabc->ismaster && next && !next->ismaster) {
if (moving_left) {
delta_x = fabsf(delta_x); // 向上移动增加主区域高度
} else {
delta_x = -fabsf(delta_x); // 向下移动减少主区域高度
}
} else if (!grabc->ismaster && prev && prev->ismaster) {
if (moving_left) {
delta_x = -fabsf(delta_x); // 向上移动减少栈区域高度
} else {
delta_x = fabsf(delta_x); // 向下移动增加栈区域高度
}
} else if (!grabc->ismaster && !next) {
if (moving_left) {
delta_x = fabsf(delta_x); // 向上移动增加栈区域高度
} else {
delta_x = -fabsf(delta_x); // 向下移动减少栈区域高度
}
} else if ((grabc->cursor_in_left_half && moving_left) ||
(!grabc->cursor_in_left_half && moving_right)) {
// 光标在窗口左侧且向左移动,或在窗口右侧且向右移动 → 增加宽度
delta_x = fabsf(delta_x);
delta_x = delta_x * 2;
} else {
// 其他情况 → 减小宽度
delta_x = -fabsf(delta_x);
delta_x = delta_x * 2;
}
// 直接设置新的比例,基于初始值 + 变化量
float new_master_mfact_per = grabc->old_master_mfact_per +
delta_y; // 垂直delta_y调整主区域高度
float new_master_inner_per = grabc->old_master_inner_per +
delta_x; // 垂直delta_x调整主区域内部宽度
float new_stack_inner_per = grabc->old_stack_inner_per +
delta_x; // 垂直delta_x调整栈区域内部宽度
// 应用限制,确保比例在合理范围内
new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per));
new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per));
new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per));
// 应用到所有平铺窗口
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
if (!isdrag && tc != grabc && type != CENTER_TILE) {
if (!tc->ismaster && new_stack_inner_per != 1.0f &&
grabc->old_stack_inner_per != 1.0f)
tc->stack_inner_per = (1 - new_stack_inner_per) /
(1 - grabc->old_stack_inner_per) *
tc->stack_inner_per;
if (tc->ismaster && new_master_inner_per != 1.0f &&
grabc->old_master_inner_per != 1.0f)
tc->master_inner_per =
(1.0f - new_master_inner_per) /
(1.0f - grabc->old_master_inner_per) *
tc->master_inner_per;
}
tc->master_mfact_per = new_master_mfact_per;
}
}
grabc->master_inner_per = new_master_inner_per;
grabc->stack_inner_per = new_stack_inner_per;
if (!isdrag) {
arrange(grabc->mon, false, false);
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_tile_refresh_interval) {
arrange(grabc->mon, false, false);
last_apply_drap_time = time;
}
}
}
void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, bool isvertical) {
Client *tc = NULL;
float delta_x, delta_y;
float new_scroller_proportion;
float new_stack_proportion;
Client *stack_head = get_scroll_stack_head(grabc);
if (grabc && grabc->mon->visible_tiling_clients == 1 &&
!scroller_ignore_proportion_single)
return;
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
// 记录初始状态
stack_head->old_scroller_pproportion = stack_head->scroller_proportion;
grabc->old_stack_proportion = grabc->stack_proportion;
grabc->cursor_in_left_half =
cursor->x < grabc->geom.x + grabc->geom.width / 2;
grabc->cursor_in_upper_half =
cursor->y < grabc->geom.y + grabc->geom.height / 2;
// 记录初始几何信息
grabc->drag_begin_geom = grabc->geom;
} else {
// 计算相对于屏幕尺寸的比例变化
// 计算相对于屏幕尺寸的比例变化
if (isdrag) {
offsetx = cursor->x - drag_begin_cursorx;
offsety = cursor->y - drag_begin_cursory;
} else {
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->drag_begin_geom = grabc->geom;
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_left_half = false;
}
if (isvertical) {
delta_y = (float)(offsety) *
(stack_head->old_scroller_pproportion) /
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_down;
bool moving_left;
bool moving_right;
if (!isdrag) {
moving_up = offsety < 0 ? true : false;
moving_down = offsety > 0 ? true : false;
moving_left = offsetx < 0 ? true : false;
moving_right = offsetx > 0 ? true : false;
} else {
moving_up = cursor->y < drag_begin_cursory;
moving_down = cursor->y > drag_begin_cursory;
moving_left = cursor->x < drag_begin_cursorx;
moving_right = cursor->x > drag_begin_cursorx;
}
if ((grabc->cursor_in_upper_half && moving_up) ||
(!grabc->cursor_in_upper_half && moving_down)) {
// 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度
delta_y = fabsf(delta_y);
} else {
// 其他情况 → 减小高度
delta_y = -fabsf(delta_y);
}
if ((grabc->cursor_in_left_half && moving_left) ||
(!grabc->cursor_in_left_half && moving_right)) {
delta_x = fabsf(delta_x);
} else {
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) {
new_scroller_proportion =
stack_head->old_scroller_pproportion + delta_y;
new_stack_proportion = grabc->old_stack_proportion + delta_x;
} else {
new_scroller_proportion =
stack_head->old_scroller_pproportion + delta_x;
new_stack_proportion = grabc->old_stack_proportion + delta_y;
}
// 应用限制,确保比例在合理范围内
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->stack_proportion = new_stack_proportion;
stack_head->scroller_proportion = new_scroller_proportion;
wl_list_for_each(tc, &clients, link) {
if (!isdrag && new_stack_proportion != 1.0f &&
grabc->old_stack_proportion != 1.0f && tc != grabc &&
ISTILED(tc) && get_scroll_stack_head(tc) == stack_head) {
tc->stack_proportion = (1.0f - new_stack_proportion) /
(1.0f - grabc->old_stack_proportion) *
tc->stack_proportion;
}
}
if (!isdrag) {
arrange(grabc->mon, false, false);
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_tile_refresh_interval) {
arrange(grabc->mon, false, false);
last_apply_drap_time = time;
}
}
}
void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time) {
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
return;
if (grabc->mon->isoverview)
return;
const Layout *current_layout =
grabc->mon->pertag->ltidxs[grabc->mon->pertag->curtag];
if (current_layout->id == TILE || current_layout->id == DECK ||
current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE ||
(current_layout->id == TGMIX && grabc->mon->visible_tiling_clients <= 3)
) {
resize_tile_master_horizontal(grabc, isdrag, offsetx, offsety, time,
current_layout->id);
} else if (current_layout->id == VERTICAL_TILE ||
current_layout->id == VERTICAL_DECK) {
resize_tile_master_vertical(grabc, isdrag, offsetx, offsety, time,
current_layout->id);
} else if (current_layout->id == SCROLLER) {
resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, false);
} else if (current_layout->id == VERTICAL_SCROLLER) {
resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, true);
}
}
/* If there are no calculation omissions,
these two functions will never be triggered.
Just in case to facilitate the final investigation*/
void check_size_per_valid(Client *c) {
if (c->ismaster) {
assert(c->master_inner_per > 0.0f && c->master_inner_per <= 1.0f);
} else {
assert(c->stack_inner_per > 0.0f && c->stack_inner_per <= 1.0f);
}
}
void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
double total_left_stack_hight_percent,
double total_right_stack_hight_percent,
double total_stack_hight_percent,
double total_master_inner_percent, int32_t master_num,
int32_t stack_num) {
Client *c = NULL;
int32_t i = 0;
uint32_t stack_index = 0;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
if (m->pertag->ltidxs[m->pertag->curtag]->id != CENTER_TILE) {
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) {
c->ismaster = true;
c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f;
c->master_inner_per =
c->master_inner_per / total_master_inner_percent;
} else {
c->ismaster = false;
c->master_inner_per =
master_num > 0 ? 1.0f / master_num : 1.0f;
c->stack_inner_per =
total_stack_hight_percent
? c->stack_inner_per / total_stack_hight_percent
: 1.0f;
}
i++;
check_size_per_valid(c);
}
}
} else {
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) {
c->ismaster = true;
if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) {
c->stack_inner_per =
stack_num > 1 ? 1.0f / ((stack_num - 1) / 2.0f)
: 1.0f;
} else {
c->stack_inner_per =
stack_num > 1 ? 2.0f / stack_num : 1.0f;
}
c->master_inner_per =
c->master_inner_per / total_master_inner_percent;
} else {
stack_index = i - nmasters;
c->ismaster = false;
c->master_inner_per =
master_num > 0 ? 1.0f / master_num : 1.0f;
if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) {
c->stack_inner_per =
total_right_stack_hight_percent
? c->stack_inner_per /
total_right_stack_hight_percent
: 1.0f;
} else {
c->stack_inner_per =
total_left_stack_hight_percent
? c->stack_inner_per /
total_left_stack_hight_percent
: 1.0f;
}
}
i++;
check_size_per_valid(c);
}
}
}
}
void pre_caculate_before_arrange(Monitor *m, bool want_animation,
bool from_view, bool only_caculate) {
Client *c = NULL;
double total_stack_inner_percent = 0;
double total_master_inner_percent = 0;
double total_right_stack_hight_percent = 0;
double total_left_stack_hight_percent = 0;
int32_t i = 0;
int32_t nmasters = 0;
int32_t stack_index = 0;
int32_t master_num = 0;
int32_t stack_num = 0;
if (!m)
return;
if (!m->wlr_output->enabled)
return;
m->visible_clients = 0;
m->visible_tiling_clients = 0;
m->visible_scroll_tiling_clients = 0;
wl_list_for_each(c, &clients, link) {
if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) {
exit_scroller_stack(c);
}
if (from_view && (c->isglobal || c->isunglobal)) {
set_size_per(m, c);
}
if (c->mon == m && (c->isglobal || c->isunglobal)) {
c->tags = m->tagset[m->seltags];
}
if (from_view && m->sel == NULL && c->isglobal && VISIBLEON(c, m)) {
focusclient(c, 1);
}
if (VISIBLEON(c, m)) {
if (from_view && !client_only_in_one_tag(c)) {
set_size_per(m, c);
}
if (!c->isunglobal)
m->visible_clients++;
if (ISTILED(c)) {
m->visible_tiling_clients++;
}
if (ISSCROLLTILED(c) && !c->prev_in_stack) {
m->visible_scroll_tiling_clients++;
}
}
}
nmasters = m->pertag->nmasters[m->pertag->curtag];
wl_list_for_each(c, &clients, link) {
if (c->iskilling)
continue;
if (c->mon == m) {
if (VISIBLEON(c, m)) {
if (ISTILED(c)) {
if (i < nmasters) {
master_num++;
total_master_inner_percent += c->master_inner_per;
} else {
stack_num++;
total_stack_inner_percent += c->stack_inner_per;
stack_index = i - nmasters;
if ((stack_index % 2) ^
(m->visible_tiling_clients % 2 == 0)) {
c->isleftstack = false;
total_right_stack_hight_percent +=
c->stack_inner_per;
} else {
c->isleftstack = true;
total_left_stack_hight_percent +=
c->stack_inner_per;
}
}
i++;
}
if (!only_caculate)
set_arrange_visible(m, c, want_animation);
} else {
if (!only_caculate)
set_arrange_hidden(m, c, want_animation);
}
}
if (!only_caculate && c->mon == m && c->ismaximizescreen &&
!c->animation.tagouted && !c->animation.tagouting &&
VISIBLEON(c, m)) {
reset_maximizescreen_size(c);
}
}
reset_size_per_mon(
m, m->visible_tiling_clients, total_left_stack_hight_percent,
total_right_stack_hight_percent, total_stack_inner_percent,
total_master_inner_percent, master_num, stack_num);
}
void // 17
arrange(Monitor *m, bool want_animation, bool from_view) {
pre_caculate_before_arrange(m, want_animation, from_view, false);
if (m->isoverview) {
overviewlayout.arrange(m);
} else {
m->pertag->ltidxs[m->pertag->curtag]->arrange(m);
}
if (!start_drag_window) {
motionnotify(0, NULL, 0, 0, 0, 0);
checkidleinhibitor(NULL);
}
printstatus();
}

File diff suppressed because it is too large Load diff

View file

@ -1,38 +1,50 @@
static void tile(Monitor *m);
static void center_tile(Monitor *m);
static void right_tile(Monitor *m);
static void overview(Monitor *m);
static void grid(Monitor *m);
static void scroller(Monitor *m);
static void deck(Monitor *mon);
static void dwindle(Monitor *mon);
static void spiral(Monitor *mon);
static void monocle(Monitor *m);
static void vertical_tile(Monitor *m);
static void vertical_overview(Monitor *m);
static void vertical_grid(Monitor *m);
static void vertical_scroller(Monitor *m);
static void vertical_deck(Monitor *mon);
static void vertical_dwindle(Monitor *mon);
static void vertical_spiral(Monitor *mon);
static void vertical_monocle(Monitor *m);
static void tgmix(Monitor *m);
/* layout(s) */
Layout overviewlayout = {"󰃇", overview, "overview"};
enum {
TILE,
SCROLLER,
GRID,
MONOCLE,
DECK,
CENTER_TILE,
VERTICAL_SCROLLER,
VERTICAL_TILE,
VERTICAL_GRID,
VERTICAL_DECK,
RIGHT_TILE,
TGMIX,
};
Layout layouts[] = {
// 最少两个,不能删除少于两个
/* symbol arrange function name */
{"S", scroller, "scroller"}, // 滚动布局
{"T", tile, "tile"}, // 堆栈布局
{"G", grid, "grid"},
{"M", monocle, "monocle"},
{"D", dwindle, "dwindle"},
{"P", spiral, "spiral"},
{"K", deck, "deck"},
{"VS", vertical_scroller, "vertical_scroller"},
{"VT", vertical_tile, "vertical_tile"},
{"VM", vertical_monocle, "vertical_monocle"},
{"VD", vertical_dwindle, "vertical_dwindle"},
{"VP", vertical_spiral, "vertical_spiral"},
{"VG", vertical_grid, "vertical_grid"},
{"VK", vertical_deck, "vertical_deck"},
{"T", tile, "tile", TILE}, // 平铺布局
{"S", scroller, "scroller", SCROLLER}, // 滚动布局
{"G", grid, "grid", GRID}, // 格子布局
{"M", monocle, "monocle", MONOCLE}, // 单屏布局
{"K", deck, "deck", DECK}, // 卡片布局
{"CT", center_tile, "center_tile", CENTER_TILE}, // 居中布局
{"RT", right_tile, "right_tile", RIGHT_TILE}, // 右布局
{"VS", vertical_scroller, "vertical_scroller",
VERTICAL_SCROLLER}, // 垂直滚动布局
{"VT", vertical_tile, "vertical_tile", VERTICAL_TILE}, // 垂直平铺布局
{"VG", vertical_grid, "vertical_grid", VERTICAL_GRID}, // 垂直格子布局
{"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局
{"TG", tgmix, "tgmix", TGMIX}, // 混合布局
};

View file

@ -1,260 +1,142 @@
void vertical_fibonacci(Monitor *mon, int s) {
unsigned int i = 0, n = 0, nx, ny, nw, nh;
Client *c;
unsigned int cur_gappih = enablegaps ? mon->gappih : 0;
unsigned int cur_gappiv = enablegaps ? mon->gappiv : 0;
unsigned int cur_gappoh = enablegaps ? mon->gappoh : 0;
unsigned int cur_gappov = enablegaps ? mon->gappov : 0;
void vertical_tile(Monitor *m) {
int32_t i, n = 0, w, r, ie = enablegaps, mh, mx, tx;
Client *c = NULL;
Client *fc = NULL;
double mfact = 0;
int32_t master_num = 0;
int32_t stack_num = 0;
cur_gappih = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappiv = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappoh = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappoh;
cur_gappov = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappov;
// Count visible clients
wl_list_for_each(c, &clients, link) if (VISIBLEON(c, mon) && ISTILED(c))
n++;
n = m->visible_tiling_clients;
master_num = m->pertag->nmasters[m->pertag->curtag];
master_num = n > master_num ? master_num : n;
stack_num = n - master_num;
if (n == 0)
return;
// Initial dimensions including outer gaps
nx = mon->w.x + cur_gappoh;
ny = mon->w.y + cur_gappov;
nw = mon->w.width - 2 * cur_gappoh;
nh = mon->w.height - 2 * cur_gappov;
int32_t cur_gapih = enablegaps ? m->gappih : 0;
int32_t cur_gapiv = enablegaps ? m->gappiv : 0;
int32_t cur_gapoh = enablegaps ? m->gappoh : 0;
int32_t cur_gapov = enablegaps ? m->gappov : 0;
// First pass: calculate client geometries
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, mon) || !ISTILED(c))
continue;
cur_gapih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapih;
cur_gapiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapiv;
cur_gapoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapoh;
cur_gapov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapov;
c->bw = mon->visible_tiling_clients == 1 && no_border_when_single &&
smartgaps
? 0
: borderpx;
if ((i % 2 && nw / 2 > 2 * c->bw) || (!(i % 2) && nh / 2 > 2 * c->bw)) {
if (i < n - 1) {
if (i % 2) {
if (i == 1) {
nw = nw * mon->pertag->smfacts[mon->pertag->curtag];
} else {
nw = (nw - cur_gappih) / 2;
}
} else {
nh = (nh - cur_gappiv) / 2;
}
if ((i % 4) == 2 && !s)
ny += nh + cur_gappiv;
else if ((i % 4) == 3 && !s)
nx += nw + cur_gappih;
}
if ((i % 4) == 0) {
if (s)
nx += nw + cur_gappih;
else
nx -= nw + cur_gappih;
} else if ((i % 4) == 1)
ny += nh + cur_gappiv;
else if ((i % 4) == 2)
nx += nw + cur_gappih;
else if ((i % 4) == 3) {
if (s)
ny += nh + cur_gappiv;
else
ny -= nh + cur_gappiv;
}
if (i == 0) {
if (n != 1)
nh = (mon->w.height - 2 * cur_gappov) *
mon->pertag->mfacts[mon->pertag->curtag];
nx = mon->w.x + cur_gappoh;
} else if (i == 1) {
nh = mon->w.height - 2 * cur_gappov - nh - cur_gappiv;
} else if (i == 2) {
nw = mon->w.width - 2 * cur_gappoh - nw - cur_gappih;
}
i++;
}
c->geom = (struct wlr_box){.x = nx, .y = ny, .width = nw, .height = nh};
}
// Second pass: apply gaps between clients
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, mon) || !ISTILED(c))
continue;
unsigned int right_gap = 0;
unsigned int bottom_gap = 0;
Client *nc;
wl_list_for_each(nc, &clients, link) {
if (!VISIBLEON(nc, mon) || !ISTILED(nc))
continue;
if (c == nc)
continue;
// Check for right neighbor
if (c->geom.y == nc->geom.y &&
c->geom.x + c->geom.width == nc->geom.x) {
right_gap = cur_gappih;
}
// Check for bottom neighbor
if (c->geom.x == nc->geom.x &&
c->geom.y + c->geom.height == nc->geom.y) {
bottom_gap = cur_gappiv;
}
}
resize(c,
(struct wlr_box){.x = c->geom.x,
.y = c->geom.y,
.width = c->geom.width - right_gap,
.height = c->geom.height - bottom_gap},
0);
}
}
void vertical_dwindle(Monitor *mon) { vertical_fibonacci(mon, 1); }
void vertical_spiral(Monitor *mon) { vertical_fibonacci(mon, 0); }
void vertical_grid(Monitor *m) {
unsigned int i, n;
unsigned int cx, cy, cw, ch;
unsigned int dy;
unsigned int rows, cols, overrows;
Client *c;
n = 0;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_should_ignore_focus(c)) || ISTILED(c))) {
n++;
}
}
if (n == 0) {
return;
}
if (n == 1) {
wl_list_for_each(c, &clients, link) {
c->bw = m->visible_tiling_clients == 1 && no_border_when_single &&
smartgaps
? 0
: borderpx;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_should_ignore_focus(c)) ||
ISTILED(c))) {
ch = (m->w.height - 2 * overviewgappo) * 0.7;
cw = (m->w.width - 2 * overviewgappo) * 0.8;
c->geom.x = m->w.x + (m->w.width - cw) / 2;
c->geom.y = m->w.y + (m->w.height - ch) / 2;
c->geom.width = cw - 2 * c->bw;
c->geom.height = ch - 2 * c->bw;
resize(c, c->geom, 0);
return;
}
}
}
if (n == 2) {
ch = (m->w.height - 2 * overviewgappo - overviewgappi) / 2;
cw = (m->w.width - 2 * overviewgappo) * 0.65;
i = 0;
wl_list_for_each(c, &clients, link) {
c->bw = m->visible_tiling_clients == 1 && no_border_when_single &&
smartgaps
? 0
: borderpx;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_should_ignore_focus(c)) ||
ISTILED(c))) {
if (i == 0) {
c->geom.x = m->w.x + (m->w.width - cw) / 2 + overviewgappo;
c->geom.y = m->w.y + overviewgappo;
c->geom.width = cw - 2 * c->bw;
c->geom.height = ch - 2 * c->bw;
resize(c, c->geom, 0);
} else if (i == 1) {
c->geom.x = m->w.x + (m->w.width - cw) / 2 + overviewgappo;
c->geom.y = m->w.y + ch + overviewgappo + overviewgappi;
c->geom.width = cw - 2 * c->bw;
c->geom.height = ch - 2 * c->bw;
resize(c, c->geom, 0);
}
i++;
}
}
return;
}
for (rows = 0; rows <= n / 2; rows++) {
if (rows * rows >= n) {
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc))
break;
}
}
cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows;
cw = (m->w.width - 2 * overviewgappo - (cols - 1) * overviewgappi) / cols;
ch = (m->w.height - 2 * overviewgappo - (rows - 1) * overviewgappi) / rows;
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag];
overrows = n % rows;
if (overrows) {
dy =
(m->w.height - overrows * ch - (overrows - 1) * overviewgappi) / 2 -
overviewgappo;
}
if (n > m->pertag->nmasters[m->pertag->curtag])
mh = m->pertag->nmasters[m->pertag->curtag]
? (m->w.height + cur_gapiv * ie) * mfact
: 0;
else
mh = m->w.height - 2 * cur_gapov + cur_gapiv * ie;
i = 0;
mx = tx = cur_gapoh;
int32_t master_surplus_width =
(m->w.width - 2 * cur_gapoh - cur_gapih * ie * (master_num - 1));
float master_surplus_ratio = 1.0;
int32_t slave_surplus_width =
(m->w.width - 2 * cur_gapoh - cur_gapih * ie * (stack_num - 1));
float slave_surplus_ratio = 1.0;
wl_list_for_each(c, &clients, link) {
c->bw =
m->visible_tiling_clients == 1 && no_border_when_single && smartgaps
? 0
: borderpx;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_should_ignore_focus(c)) || ISTILED(c))) {
cx = m->w.x + (i / rows) * (cw + overviewgappi);
cy = m->w.y + (i % rows) * (ch + overviewgappi);
if (overrows && i >= n - overrows) {
cy += dy;
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
if (i < m->pertag->nmasters[m->pertag->curtag]) {
r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i;
if (c->master_inner_per > 0.0f) {
w = master_surplus_width * c->master_inner_per /
master_surplus_ratio;
master_surplus_width = master_surplus_width - w;
master_surplus_ratio =
master_surplus_ratio - c->master_inner_per;
c->master_mfact_per = mfact;
} else {
w = (m->w.width - mx - cur_gapih - cur_gapih * ie * (r - 1)) /
r;
c->master_inner_per = w / (m->w.width - mx - cur_gapih -
cur_gapih * ie * (r - 1));
c->master_mfact_per = mfact;
}
c->geom.x = cx + overviewgappo;
c->geom.y = cy + overviewgappo;
c->geom.width = cw - 2 * c->bw;
c->geom.height = ch - 2 * c->bw;
resize(c, c->geom, 0);
i++;
resize(c,
(struct wlr_box){.x = m->w.x + mx,
.y = m->w.y + cur_gapov,
.width = w,
.height = mh - cur_gapiv * ie},
0);
mx += c->geom.width + cur_gapih * ie;
} else {
r = n - i;
if (c->stack_inner_per > 0.0f) {
w = slave_surplus_width * c->stack_inner_per /
slave_surplus_ratio;
slave_surplus_width = slave_surplus_width - w;
slave_surplus_ratio = slave_surplus_ratio - c->stack_inner_per;
c->master_mfact_per = mfact;
} else {
w = (m->w.width - tx - cur_gapih - cur_gapih * ie * (r - 1)) /
r;
c->stack_inner_per = w / (m->w.width - tx - cur_gapih -
cur_gapih * ie * (r - 1));
c->master_mfact_per = mfact;
}
resize(c,
(struct wlr_box){.x = m->w.x + tx,
.y = m->w.y + mh + cur_gapov,
.width = w,
.height = m->w.height - mh - 2 * cur_gapov},
0);
tx += c->geom.width + cur_gapih * ie;
}
i++;
}
}
void vertical_deck(Monitor *m) {
unsigned int mh, mx;
int i, n = 0;
Client *c;
unsigned int cur_gappiv = enablegaps ? m->gappiv : 0;
unsigned int cur_gappoh = enablegaps ? m->gappoh : 0;
unsigned int cur_gappov = enablegaps ? m->gappov : 0;
int32_t mh, mx;
int32_t i, n = 0;
Client *c = NULL;
Client *fc = NULL;
float mfact;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && ISTILED(c)) n++;
n = m->visible_tiling_clients;
if (n == 0)
return;
float mfact = m->pertag ? m->pertag->mfacts[m->pertag->curtag] : m->mfact;
wl_list_for_each(fc, &clients, link) {
if (n > m->nmaster)
mh = m->nmaster ? round((m->w.height - 2 * cur_gappov) * mfact) : 0;
if (VISIBLEON(fc, m) && ISTILED(fc))
break;
}
// Calculate master width using mfact from pertag
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag];
if (n > nmasters)
mh = nmasters ? round((m->w.height - 2 * cur_gappov) * mfact) : 0;
else
mh = m->w.height - 2 * cur_gappov;
@ -262,13 +144,13 @@ void vertical_deck(Monitor *m) {
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
if (i < m->nmaster) {
if (i < nmasters) {
resize(
c,
(struct wlr_box){.x = m->w.x + cur_gappoh + mx,
.y = m->w.y + cur_gappov,
.width = (m->w.width - 2 * cur_gappoh - mx) /
(MIN(n, m->nmaster) - i),
(MIN(n, nmasters) - i),
.height = mh},
0);
mx += c->geom.width;
@ -287,31 +169,124 @@ void vertical_deck(Monitor *m) {
}
}
void vertical_scroller(Monitor *m) {
unsigned int i, n;
Client *c, *root_client = NULL;
Client **tempClients = NULL;
n = 0;
struct wlr_box target_geom;
int focus_client_index = 0;
bool need_scroller = false;
unsigned int cur_gappiv = enablegaps ? m->gappiv : 0;
unsigned int cur_gappov = enablegaps ? m->gappov : 0;
unsigned int cur_gappoh = enablegaps ? m->gappoh : 0;
void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) {
Monitor *m = c->mon;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
cur_gappiv =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappov =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh;
unsigned int max_client_height =
m->w.height - 2 * scroller_structs - cur_gappiv;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
n++;
}
if (c->isfullscreen) {
target_geom->width = m->m.width;
target_geom->height = m->m.height;
target_geom->x = m->m.x;
return;
}
if (c->ismaximizescreen) {
target_geom->width = m->w.width - 2 * cur_gappoh;
target_geom->height = m->w.height - 2 * cur_gappov;
target_geom->x = m->w.x + cur_gappoh;
return;
}
target_geom->width = m->w.width - 2 * cur_gappoh;
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) {
int32_t i, n, j;
float single_proportion = 1.0;
Client *c = NULL, *root_client = NULL;
Client **tempClients = NULL;
struct wlr_box target_geom;
int32_t focus_client_index = 0;
bool need_scroller = false;
bool over_overspread_to_up = false;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t cur_gappih = enablegaps ? m->gappih : 0;
cur_gappiv =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappov =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh;
int32_t max_client_height = m->w.height - 2 * scroller_structs - cur_gappiv;
n = m->visible_scroll_tiling_clients;
if (n == 0) {
return;
}
@ -321,35 +296,44 @@ void vertical_scroller(Monitor *m) {
return;
}
n = 0;
j = 0;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
tempClients[n] = c;
n++;
if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) {
tempClients[j] = c;
j++;
}
}
if (n == 1) {
if (n == 1 && !scroller_ignore_proportion_single &&
!tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) {
c = tempClients[0];
single_proportion = c->scroller_proportion_single > 0.0f
? c->scroller_proportion_single
: scroller_default_proportion_single;
target_geom.width = m->w.width - 2 * cur_gappoh;
target_geom.height =
(m->w.height - 2 * cur_gappov) * scroller_default_proportion_single;
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion;
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
resize(c, target_geom, 0);
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
vertical_check_scroller_root_inside_mon(c, &target_geom);
arrange_stack_vertical(c, target_geom, cur_gappih);
free(tempClients);
return;
}
if (m->sel && !client_is_unmanaged(m->sel) && !m->sel->isfloating &&
!m->sel->ismaxmizescreen && !m->sel->isfullscreen) {
if (m->sel && !client_is_unmanaged(m->sel) && ISSCROLLTILED(m->sel)) {
root_client = m->sel;
} else if (m->prevsel && !client_is_unmanaged(m->prevsel) &&
!m->prevsel->isfloating && !m->prevsel->ismaxmizescreen &&
!m->prevsel->isfullscreen) {
} else if (m->prevsel && ISSCROLLTILED(m->prevsel) &&
VISIBLEON(m->prevsel, m) && !client_is_unmanaged(m->prevsel)) {
root_client = m->prevsel;
} else {
root_client = center_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) {
@ -360,8 +344,7 @@ void vertical_scroller(Monitor *m) {
for (i = 0; i < n; i++) {
c = tempClients[i];
if (root_client == c) {
if (!c->is_pending_open_animation &&
c->geom.y >= m->w.y + scroller_structs &&
if (c->geom.y >= m->w.y + scroller_structs &&
c->geom.y + c->geom.height <=
m->w.y + m->w.height - scroller_structs) {
need_scroller = false;
@ -373,116 +356,232 @@ void vertical_scroller(Monitor *m) {
}
}
bool need_apply_overspread =
scroller_prefer_overspread && m->visible_scroll_tiling_clients > 1 &&
(focus_client_index == 0 || focus_client_index == n - 1) &&
tempClients[focus_client_index]->scroller_proportion < 1.0f;
if (need_apply_overspread) {
if (focus_client_index == 0) {
over_overspread_to_up = true;
} else {
over_overspread_to_up = false;
}
if (over_overspread_to_up &&
(!INSIDEMON(tempClients[1]) ||
(tempClients[1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else if (!over_overspread_to_up &&
(!INSIDEMON(tempClients[focus_client_index - 1]) ||
(tempClients[focus_client_index - 1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else {
need_apply_overspread = false;
}
}
bool need_apply_center =
scroller_focus_center || m->visible_scroll_tiling_clients == 1 ||
(scroller_prefer_center && !need_apply_overspread &&
(!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_height) +
(tempClients[focus_client_index]->scroller_proportion *
max_client_height) >
m->w.height - 2 * scroller_structs - cur_gappiv)));
if (n == 1 && scroller_ignore_proportion_single) {
need_scroller = true;
}
if (start_drag_window)
need_scroller = false;
target_geom.width = m->w.width - 2 * cur_gappoh;
target_geom.height = max_client_height * c->scroller_proportion;
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
vertical_scroll_adjust_fullandmax(tempClients[focus_client_index],
&target_geom);
if (need_scroller) {
if (scroller_focus_center ||
((!m->prevsel ||
(m->prevsel->scroller_proportion * max_client_height) +
(root_client->scroller_proportion * max_client_height) >
m->w.height - 2 * scroller_structs - cur_gappiv) &&
scroller_prefer_center)) {
if (tempClients[focus_client_index]->isfullscreen) {
target_geom.y = m->m.y;
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) {
target_geom.y = m->w.y + cur_gappov;
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) {
if (need_apply_center) {
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
} else if (need_apply_overspread) {
if (over_overspread_to_up) {
target_geom.y = m->w.y + scroller_structs;
} else {
target_geom.y =
m->w.y +
(m->w.height -
tempClients[focus_client_index]->scroller_proportion *
max_client_height -
scroller_structs);
}
} else {
target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2
? m->w.y + (m->w.height -
root_client->scroller_proportion *
tempClients[focus_client_index]
->scroller_proportion *
max_client_height -
scroller_structs)
: m->w.y + scroller_structs;
}
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 {
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++) {
c = tempClients[focus_client_index - i];
target_geom.height = max_client_height * c->scroller_proportion;
vertical_scroll_adjust_fullandmax(c, &target_geom);
target_geom.y = tempClients[focus_client_index - i + 1]->geom.y -
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++) {
c = tempClients[focus_client_index + i];
target_geom.height = max_client_height * c->scroller_proportion;
vertical_scroll_adjust_fullandmax(c, &target_geom);
target_geom.y = tempClients[focus_client_index + i - 1]->geom.y +
cur_gappiv +
tempClients[focus_client_index + i - 1]->geom.height;
resize(c, target_geom, 0);
arrange_stack_vertical(c, target_geom, cur_gappih);
}
free(tempClients);
}
void vertical_tile(Monitor *m) {
unsigned int i, n = 0, w, r, ie = enablegaps, mh, mx, tx;
Client *c;
void vertical_grid(Monitor *m) {
int32_t i, n;
int32_t cx, cy, cw, ch;
int32_t dy;
int32_t rows, cols, overrows;
Client *c = NULL;
int32_t target_gappo =
enablegaps ? m->isoverview ? overviewgappo : gappov : 0;
int32_t target_gappi =
enablegaps ? m->isoverview ? overviewgappi : gappiv : 0;
float single_width_ratio = m->isoverview ? 0.7 : 0.9;
float single_height_ratio = m->isoverview ? 0.8 : 0.9;
wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && ISTILED(c)) n++;
if (n == 0)
n = m->isoverview ? m->visible_clients : m->visible_tiling_clients;
if (n == 0) {
return;
}
unsigned int cur_gappiv = enablegaps ? m->gappiv : 0;
unsigned int cur_gappih = enablegaps ? m->gappih : 0;
unsigned int cur_gappov = enablegaps ? m->gappov : 0;
unsigned int cur_gappoh = enablegaps ? m->gappoh : 0;
if (n == 1) {
wl_list_for_each(c, &clients, link) {
cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
if (c->mon != m)
continue;
if (n > selmon->pertag->nmasters[selmon->pertag->curtag])
mh = selmon->pertag->nmasters[selmon->pertag->curtag]
? (m->w.height + cur_gappiv * ie) *
selmon->pertag->mfacts[selmon->pertag->curtag]
: 0;
else
mh = m->w.height - 2 * cur_gappoh + cur_gappiv * ie;
i = 0;
mx = tx = cur_gappov;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
if (i < selmon->pertag->nmasters[selmon->pertag->curtag]) {
r = MIN(n, selmon->pertag->nmasters[selmon->pertag->curtag]) - i;
w = (m->w.width - mx - cur_gappov - cur_gappiv * ie * (r - 1)) / r;
resize(c,
(struct wlr_box){.x = m->w.x + mx,
.y = m->w.y + cur_gappoh,
.width = w,
.height = mh - cur_gappih * ie},
0);
mx += c->geom.width + cur_gappiv * ie;
} else {
r = n - i;
w = (m->w.width - tx - cur_gappov - cur_gappiv * ie * (r - 1)) / r;
resize(
c,
(struct wlr_box){.x = m->w.x + tx,
.y = m->w.y + mh + cur_gappoh,
.width = w,
.height = m->w.height - mh - 2 * cur_gappoh},
0);
tx += c->geom.width + cur_gappiv * ie;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
ch = (m->w.height - 2 * target_gappo) * single_height_ratio;
cw = (m->w.width - 2 * target_gappo) * single_width_ratio;
c->geom.x = m->w.x + (m->w.width - cw) / 2;
c->geom.y = m->w.y + (m->w.height - ch) / 2;
c->geom.width = cw;
c->geom.height = ch;
resize(c, c->geom, 0);
return;
}
}
i++;
}
}
void vertical_monocle(Monitor *m) {
Client *c;
if (n == 2) {
ch = (m->w.height - 2 * target_gappo - target_gappi) / 2;
cw = (m->w.width - 2 * target_gappo) * 0.65;
i = 0;
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
if (i == 0) {
c->geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo;
c->geom.y = m->w.y + target_gappo;
c->geom.width = cw;
c->geom.height = ch;
resize(c, c->geom, 0);
} else if (i == 1) {
c->geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo;
c->geom.y = m->w.y + ch + target_gappo + target_gappi;
c->geom.width = cw;
c->geom.height = ch;
resize(c, c->geom, 0);
}
i++;
}
}
return;
}
for (rows = 0; rows <= n / 2; rows++) {
if (rows * rows >= n) {
break;
}
}
cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows;
cw = (m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols;
ch = (m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows;
overrows = n % rows;
if (overrows) {
dy = (m->w.height - overrows * ch - (overrows - 1) * target_gappi) / 2 -
target_gappo;
}
i = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
if (c->mon != m)
continue;
resize(c, m->w, 0);
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
cx = m->w.x + (i / rows) * (cw + target_gappi);
cy = m->w.y + (i % rows) * (ch + target_gappi);
if (overrows && i >= n - overrows) {
cy += dy;
}
c->geom.x = cx + target_gappo;
c->geom.y = cy + target_gappo;
c->geom.width = cw;
c->geom.height = ch;
resize(c, c->geom, 0);
i++;
}
}
if ((c = focustop(m)))
wlr_scene_node_raise_to_top(&c->scene->node);
}

File diff suppressed because it is too large Load diff