mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-06-13 03:02:14 -04:00
Compare commits
No commits in common. "main" and "0.10.8" have entirely different histories.
90 changed files with 5398 additions and 16066 deletions
48
.github/scripts/generate-nix-options-docs.py
vendored
48
.github/scripts/generate-nix-options-docs.py
vendored
|
|
@ -1,48 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Post-processes nixos-render-docs commonmark output into a single docs page.
|
||||
# Input: two pre-rendered .md files (NixOS and HM), produced by nixos-render-docs.
|
||||
# Steps: strip internal _module.args section, promote ## headings to ###.
|
||||
import re
|
||||
import sys
|
||||
|
||||
HEADER = (
|
||||
"---\n"
|
||||
"title: Nix Module Options\n"
|
||||
"description: NixOS and Home Manager configuration options for mangowm.\n"
|
||||
"---\n\n"
|
||||
"> **Note:** This document is automatically generated from the Nix module source code.\n\n"
|
||||
)
|
||||
|
||||
SECTIONS = [
|
||||
("NixOS", "**System-level options via `programs.mango`.**"),
|
||||
("Home Manager", "**Configure mangowm declaratively via `wayland.windowManager.mango`.**"),
|
||||
]
|
||||
|
||||
|
||||
def process(md):
|
||||
# Remove internal _module.args option injected by the module system
|
||||
md = re.sub(r'## _module\\\.args.*?(?=\n## |\Z)', '', md, flags=re.DOTALL)
|
||||
# Promote option headings (##) to subheadings (###) under the section (##)
|
||||
md = re.sub(r'^## ', '### ', md, flags=re.MULTILINE)
|
||||
return md.strip()
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
sys.exit("Usage: generate-nix-options-docs.py <nixos.md> <hm.md> <output.md>")
|
||||
|
||||
nixos_md, hm_md, output_md = sys.argv[1:4]
|
||||
|
||||
with open(output_md, 'w', encoding='utf-8') as out:
|
||||
out.write(HEADER)
|
||||
for path, (title, subtitle) in zip([nixos_md, hm_md], SECTIONS):
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
md = f.read()
|
||||
out.write(f"## {title}\n\n{subtitle}\n\n")
|
||||
out.write(process(md))
|
||||
out.write('\n\n')
|
||||
print(f"Written {title} section.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
61
.github/scripts/sync-wiki.py
vendored
61
.github/scripts/sync-wiki.py
vendored
|
|
@ -1,61 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
DOCS_DIR = Path("docs")
|
||||
WIKI_DIR = Path("wiki-temp")
|
||||
|
||||
FRONTMATTER_RE = re.compile(r"\A---\s*\n.*?^---\s*\n", re.DOTALL | re.MULTILINE)
|
||||
DOCS_LINK_RE = re.compile(r"\[([^\]]+)\]\(/docs/(?:[^/)]+/)*([^/)#]+)(#[^)]+)?\)")
|
||||
|
||||
|
||||
def collect_all_files() -> list[tuple[Path, str]]:
|
||||
files = []
|
||||
|
||||
def from_dir(directory: Path) -> list[Path]:
|
||||
meta = directory / "meta.json"
|
||||
if meta.exists():
|
||||
data = json.loads(meta.read_text())
|
||||
return [directory / f"{p}.md" for p in data.get("pages", []) if (directory / f"{p}.md").exists()]
|
||||
return sorted(directory.glob("*.md"))
|
||||
|
||||
for src in from_dir(DOCS_DIR):
|
||||
files.append((src, "Home" if src.stem == "index" else src.stem))
|
||||
|
||||
for subdir in sorted(DOCS_DIR.iterdir()):
|
||||
if subdir.is_dir():
|
||||
for src in from_dir(subdir):
|
||||
files.append((src, src.stem))
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def main() -> None:
|
||||
files = collect_all_files()
|
||||
|
||||
contents = {src: src.read_text() for src, _ in files}
|
||||
|
||||
for src, dest_name in files:
|
||||
text = FRONTMATTER_RE.sub("", contents[src], count=1).lstrip("\n")
|
||||
text = DOCS_LINK_RE.sub(lambda m: f"[{m.group(1)}]({m.group(2)}{m.group(3) or ''})", text)
|
||||
(WIKI_DIR / f"{dest_name}.md").write_text(text)
|
||||
|
||||
lines: list[str] = []
|
||||
current_section = None
|
||||
for src, dest_name in files:
|
||||
section = "General" if src.parent == DOCS_DIR else src.parent.name.replace("-", " ").title()
|
||||
if section != current_section:
|
||||
if current_section is not None:
|
||||
lines.append("")
|
||||
lines.append(f"## {section}\n")
|
||||
current_section = section
|
||||
if dest_name != "Home":
|
||||
title = dest_name.replace("-", " ").replace("_", " ").title()
|
||||
lines.append(f"- [[{dest_name}|{title}]]")
|
||||
|
||||
(WIKI_DIR / "_Sidebar.md").write_text("\n".join(lines))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
63
.github/workflows/generate-nix-options-docs.yml
vendored
63
.github/workflows/generate-nix-options-docs.yml
vendored
|
|
@ -1,63 +0,0 @@
|
|||
name: Generate Nix Options Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'nix/**-modules.nix'
|
||||
- 'nix/generate-options.nix'
|
||||
- 'flake.nix'
|
||||
- '.github/scripts/generate-nix-options-docs.py'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nix/**-modules.nix'
|
||||
|
||||
jobs:
|
||||
update-docs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
|
||||
- name: Build Options JSON
|
||||
run: |
|
||||
nix build .#nixos-options-json --out-link result-nixos
|
||||
nix build .#hm-options-json --out-link result-hm
|
||||
|
||||
- name: Render to CommonMark
|
||||
run: |
|
||||
echo '{}' > /tmp/manpage-urls.json
|
||||
nix run nixpkgs#nixos-render-docs -- options commonmark \
|
||||
--manpage-urls /tmp/manpage-urls.json \
|
||||
--revision nightly \
|
||||
result-nixos/share/doc/nixos/options.json \
|
||||
/tmp/nixos-raw.md
|
||||
nix run nixpkgs#nixos-render-docs -- options commonmark \
|
||||
--manpage-urls /tmp/manpage-urls.json \
|
||||
--revision nightly \
|
||||
result-hm/share/doc/nixos/options.json \
|
||||
/tmp/hm-raw.md
|
||||
|
||||
- name: Format to Markdown
|
||||
run: |
|
||||
python3 ./.github/scripts/generate-nix-options-docs.py \
|
||||
/tmp/nixos-raw.md \
|
||||
/tmp/hm-raw.md \
|
||||
'docs/nix-options.md'
|
||||
|
||||
- name: Auto-commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "docs: auto-generate Nix module options"
|
||||
file_pattern: 'docs/nix-options.md'
|
||||
branch: ${{ github.head_ref }}
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
|
|
@ -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"
|
||||
|
|
|
|||
74
.github/workflows/sync-website.yml
vendored
74
.github/workflows/sync-website.yml
vendored
|
|
@ -1,74 +0,0 @@
|
|||
name: Sync website
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- docs/**
|
||||
|
||||
concurrency:
|
||||
group: sync-website
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
sync-website:
|
||||
if: github.repository == 'mangowm/mango'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
|
||||
- name: Fetch tags for versioned docs
|
||||
run: git fetch origin --tags --depth=1
|
||||
|
||||
- name: Checkout website
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: mangowm/mangowm.github.io
|
||||
path: website
|
||||
token: ${{ secrets.WEBSITE_SYNC_TOKEN }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Sync docs
|
||||
run: |
|
||||
TARGET=website/apps/web/content/docs
|
||||
rm -rf "$TARGET"
|
||||
mkdir -p "$TARGET"
|
||||
|
||||
# Copy current docs as "(git)"
|
||||
cp -r docs "$TARGET/(git)"
|
||||
|
||||
# Generate versioned docs from last 2 semver tags
|
||||
tags=$(git tag --sort=-v:refname | grep '^v\?[0-9]\+\.[0-9]\+\.[0-9]\+$' | head -2)
|
||||
for tag in $tags; do
|
||||
git worktree add /tmp/docs-"$tag" "$tag" || continue
|
||||
if [ -d "/tmp/docs-$tag/docs" ]; then
|
||||
name="v${tag#v}"
|
||||
cp -r "/tmp/docs-$tag/docs" "$TARGET/$name"
|
||||
jq --arg title "$name" --arg desc "$name release" \
|
||||
'.title = $title | .description = $desc | .root = true' \
|
||||
"$TARGET/$name/meta.json" > "$TARGET/$name/meta.json.tmp" \
|
||||
&& mv "$TARGET/$name/meta.json.tmp" "$TARGET/$name/meta.json"
|
||||
fi
|
||||
git worktree remove /tmp/docs-"$tag"
|
||||
done
|
||||
|
||||
# Generate root version index
|
||||
{
|
||||
echo "(git)"
|
||||
for tag in $tags; do
|
||||
echo "v${tag#v}"
|
||||
done
|
||||
} | jq -Rn '[inputs | select(length > 0)] | {pages: .}' > "$TARGET/meta.json"
|
||||
|
||||
- name: Commit and push
|
||||
working-directory: website
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add apps/web/content/docs
|
||||
git diff --staged --quiet || git commit \
|
||||
-m "docs: content update from mangowm/mango" \
|
||||
-m "${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}"
|
||||
git push
|
||||
40
.github/workflows/sync-wiki.yml
vendored
40
.github/workflows/sync-wiki.yml
vendored
|
|
@ -1,40 +0,0 @@
|
|||
name: Sync wiki
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- docs/**
|
||||
|
||||
concurrency:
|
||||
group: sync-wiki
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
sync-wiki:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Clone wiki
|
||||
run: |
|
||||
git clone --depth 1 \
|
||||
https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.wiki.git \
|
||||
wiki-temp
|
||||
|
||||
- name: Sync docs to wiki
|
||||
run: |
|
||||
find wiki-temp -not -path 'wiki-temp/.git*' -type f -delete
|
||||
python3 .github/scripts/sync-wiki.py
|
||||
|
||||
- name: Commit and push
|
||||
working-directory: wiki-temp
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add -A
|
||||
git diff --staged --quiet || git commit -m "sync from ${{ github.sha }}"
|
||||
git push
|
||||
336
README.md
336
README.md
|
|
@ -1,129 +1,287 @@
|
|||
<div align="center">
|
||||
<img src="https://github.com/mangowm/mango/blob/main/assets/mango-transparency-256.png" alt="Mango Logo" width="120"/>
|
||||
# MangoWC
|
||||
|
||||
<h1>Mango Wayland Compositor</h1>
|
||||
<img width="255" height="256" alt="mango-transparency-256" src="https://github.com/DreamMaoMao/mangowc/blob/main/assets/mango-transparency-256.png" />
|
||||
|
||||
<p>A fast, feature-rich Wayland compositor built on <a href="https://codeberg.org/dwl/dwl">dwl</a></p>
|
||||
|
||||
<a href="https://github.com/mangowm/mango/stargazers"><img src="https://img.shields.io/github/stars/mangowm/mango?style=flat&color=orange" alt="Stars"/></a>
|
||||
<a href="https://github.com/mangowm/mango/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-GPL--3.0-blue?style=flat" alt="License"/></a>
|
||||
<a href="https://repology.org/project/mangowm/versions"><img src="https://repology.org/badge/tiny-repos/mangowm.svg" alt="Packaged in"/></a>
|
||||
<a href="https://discord.gg/CPjbDxesh5"><img src="https://img.shields.io/discord/1430889676264177687?style=flat&logo=discord&label=discord" alt="Discord"/></a>
|
||||
This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
1. **Lightweight & Fast Build**
|
||||
|
||||
https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
|
||||
- _Mango_ is as lightweight as _dwl_, and can be built completely within a few seconds. Despite this, _Mango_ does not compromise on functionality.
|
||||
|
||||
> See all layouts in action at [mangowm.github.io](https://mangowm.github.io/)
|
||||
2. **Feature Highlights**
|
||||
- In addition to basic WM functionality, Mango provides:
|
||||
- Excellent xwayland support.
|
||||
- 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-stack, monocle,center-master, etc.)
|
||||
- Rich window states (swallow, minimize, maximize, unglobal, global, fakefullscreen, overlay, etc.)
|
||||
- Simple yet powerful external configuration(support shortcuts hot-reload)
|
||||
- Sway-like scratchpad and named scratchpad
|
||||
- Ipc support(get/send message from/to compositor by external program)
|
||||
- Hycov-like overview
|
||||
- Window effects from scenefx (blur, shadow, corner radius, opacity)
|
||||
|
||||
## Why Mango?
|
||||
Master-Stack Layout
|
||||
|
||||
Mango starts where dwl ends. It keeps the lightweight, fast-build philosophy while adding the features that make a compositor actually usable day-to-day — without the bloat.
|
||||
https://github.com/user-attachments/assets/a9d4776e-b50b-48fb-94ce-651d8a749b8a
|
||||
|
||||
- **Lightweight & fast** — as lean as dwl, builds in seconds, no functionality compromised
|
||||
- **Excellent xwayland support** — run X11 apps without friction
|
||||
- **Tags, not workspaces** — each tag maintains its own independent window layout
|
||||
- **Smooth animations** — window open/move/close, tag transitions, layer surfaces
|
||||
- **Flexible layouts** — scroller, master-stack, monocle, dwindle, grid, and more
|
||||
- **Rich window states** — swallow, minimize, maximize, global, overlay, fakefullscreen
|
||||
- **Window effects** — blur, shadow, corner radius, opacity (via scenefx)
|
||||
- **Excellent input method support** — text-input v2/v3
|
||||
- **Sway-like scratchpad** — named scratchpad support included
|
||||
- **Hycov-style overview** — see all windows at a glance
|
||||
- **IPC** — send/receive messages from external programs
|
||||
- **Hot-reload config** — no restart needed for keybinding changes
|
||||
- **Zero flickering** — every frame is correct
|
||||
Scroller Layout
|
||||
|
||||
## Vision
|
||||
https://github.com/user-attachments/assets/c9bf9415-fad1-4400-bcdc-3ad2d76de85a
|
||||
|
||||
**Stability first.** After months of testing, Mango is solid enough for daily use. Breaking changes will be minimal.
|
||||
Layer animation
|
||||
|
||||
**Practicality over novelty.** Features get added when they genuinely improve daily workflows — not for the sake of completeness.
|
||||
https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
|
||||
|
||||
**Focused scope.** Niche requests are evaluated by community interest. Significant upvotes move things forward.
|
||||
|
||||
## Installation
|
||||
# Our discord
|
||||
[mangowc](https://discord.gg/CPjbDxesh5)
|
||||
|
||||
[](https://repology.org/project/mangowm/versions)
|
||||
# Supported layouts
|
||||
|
||||
### Arch Linux
|
||||
- tile
|
||||
- scroller
|
||||
- monocle
|
||||
- grid
|
||||
- deck
|
||||
- center_tile
|
||||
- vertical_tile
|
||||
- vertical_grid
|
||||
- vertical_scroller
|
||||
|
||||
# Installation
|
||||
|
||||
## 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 availble for manual download [here](https://aur.archlinux.org/packages/mangowc-git) or through a AUR helper like yay:
|
||||
```bash
|
||||
yay -S mangowc-git
|
||||
|
||||
```
|
||||
|
||||
## Gentoo Linux
|
||||
The package is in the community-maintained repository called GURU.
|
||||
First, add GURU repository:
|
||||
|
||||
```bash
|
||||
yay -S mangowm-git
|
||||
emerge --ask --verbose eselect-repository
|
||||
eselect repository enable guru
|
||||
emerge --sync guru
|
||||
```
|
||||
#### use my config
|
||||
- install dependencies
|
||||
|
||||
Then, add `gui-libs/scenefx` and `gui-wm/mangowc` to the `package.accept_keywords`.
|
||||
|
||||
Finally, install the package:
|
||||
|
||||
```bash
|
||||
emerge --ask --verbose gui-wm/mangowc
|
||||
```
|
||||
|
||||
## 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 mangowc
|
||||
```
|
||||
|
||||
## Other
|
||||
|
||||
```bash
|
||||
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 -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
|
||||
meson build -Dprefix=/usr
|
||||
sudo ninja -C build install
|
||||
```
|
||||
|
||||
## Suggested Tools
|
||||
|
||||
### 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)
|
||||
- Notification daemon (swaync, dunst,mako)
|
||||
- Desktop portal (xdg-desktop-portal, xdg-desktop-portal-wlr, xdg-desktop-portal-gtk)
|
||||
- Clipboard (wl-clipboard, wl-clip-persist, cliphist)
|
||||
- Gamma control/night light (wlsunset, gammastep)
|
||||
- Miscellaneous (xfce-polkit, wlogout)
|
||||
|
||||
## 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 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
|
||||
```
|
||||
- clone 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
|
||||
```
|
||||
|
||||
### Other distributions
|
||||
|
||||
See the [Installation Guide](https://mangowm.github.io/docs/installation) for Fedora, Gentoo, Guix, NixOS, openSUSE, PikaOS, AerynOS, and building from source.
|
||||
## Config Documentation
|
||||
|
||||
## Documentation
|
||||
Refer to the repo wiki [wiki](https://github.com/DreamMaoMao/mango/wiki/)
|
||||
|
||||
- **[mangowm.github.io](https://mangowm.github.io/)** — website docs with configuration reference, keybindings, layouts, IPC, and more
|
||||
- **[GitHub Wiki](https://github.com/mangowm/mango/wiki/)** — community-maintained wiki
|
||||
or the website docs [docs](https://mangowc.vercel.app/docs)
|
||||
|
||||
## Community
|
||||
# NixOS + Home-manager
|
||||
|
||||
Join us on **[Discord](https://discord.gg/CPjbDxesh5)**
|
||||
The repo contains a flake that provides a NixOS module and a home-manager module for mango.
|
||||
Use the NixOS module to install mango with other necessary components of a working Wayland environment.
|
||||
Use the home-manager module to declare configuration and autostart for mango.
|
||||
|
||||
## Acknowledgements
|
||||
Here's an example of using the modules in a flake:
|
||||
|
||||
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) — Wayland protocol implementation
|
||||
- [dwl](https://codeberg.org/dwl/dwl) — the foundation Mango builds on
|
||||
- [scenefx](https://github.com/wlrfx/scenefx) — window effects library
|
||||
- [owl](https://github.com/dqrk0jeste/owl) — animation groundwork
|
||||
- [sway](https://github.com/swaywm/sway) — protocol reference
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
mango = {
|
||||
url = "github:DreamMaoMao/mango";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
outputs =
|
||||
inputs@{ self, flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
debug = true;
|
||||
systems = [ "x86_64-linux" ];
|
||||
flake = {
|
||||
nixosConfigurations = {
|
||||
hostname = inputs.nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
|
||||
## Sponsor
|
||||
# Add mango nixos module
|
||||
inputs.mango.nixosModules.mango
|
||||
{
|
||||
programs.mango.enable = true;
|
||||
}
|
||||
{
|
||||
home-manager = {
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
backupFileExtension = "backup";
|
||||
users."username".imports =
|
||||
[
|
||||
(
|
||||
{ ... }:
|
||||
{
|
||||
wayland.windowManager.mango = {
|
||||
enable = true;
|
||||
settings = ''
|
||||
# see config.conf
|
||||
'';
|
||||
autostart_sh = ''
|
||||
# see autostart.sh
|
||||
# Note: here no need to add shebang
|
||||
'';
|
||||
};
|
||||
}
|
||||
)
|
||||
]
|
||||
++ [
|
||||
# Add mango hm module
|
||||
inputs.mango.hmModules.mango
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
If Mango makes your desktop better, consider supporting its development.
|
||||
# Packaging mango
|
||||
|
||||
Thanks to everyone who has sponsored this project:
|
||||
To package mango for other distributions, you can check the reference setup for:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<!-- add new sponsors here: copy the <td>...</td> block below -->
|
||||
<td align="center">
|
||||
<a href="https://github.com/dl09r">
|
||||
<img src="https://unavatar.io/github/dl09r" width="48" style="border-radius:50%"/><br/>
|
||||
<sub>dl09r</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/tonybanters">
|
||||
<img src="https://unavatar.io/github/tonybanters" width="48" style="border-radius:50%"/><br/>
|
||||
<sub>tonybanters</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/vinthara">
|
||||
<img src="https://unavatar.io/github/vinthara" width="48" style="border-radius:50%"/><br/>
|
||||
<sub>vinthara</sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
- [nix](https://github.com/DreamMaoMao/mangowc/blob/main/nix/default.nix)
|
||||
- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mangowc-git).
|
||||
- [gentoo](https://data.gpo.zugaina.org/guru/gui-wm/mangowc)
|
||||
|
||||
Crypto donations accepted:
|
||||
You might need to package `scenefx` for your distribution, check availability [here](https://github.com/wlrfx/scenefx.git).
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="middle">
|
||||
<strong>Network:</strong> BEP20 (BSC)<br/>
|
||||
<strong>Address:</strong> <code>0xf9cda472f2556671d2504afc4c35340ec5615da1</code>
|
||||
</td>
|
||||
<td valign="middle">
|
||||
<img width="120" alt="sponsor QR" src="assets/crypto_sponserme_qrcode.png" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
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.
|
||||
|
||||
# Thanks to These Reference Repositories
|
||||
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots - Implementation of Wayland protocol
|
||||
|
||||
- https://github.com/dqrk0jeste/owl - Basal window animation
|
||||
|
||||
- https://codeberg.org/dwl/dwl - Basal dwl feature
|
||||
|
||||
- https://github.com/swaywm/sway - Sample of Wayland protocol
|
||||
|
||||
- https://github.com/wlrfx/scenefx - Make it simple to add window effect.
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
|
|
@ -1,5 +0,0 @@
|
|||
[preferred]
|
||||
default=gtk
|
||||
org.freedesktop.impl.portal.ScreenCast=wlr
|
||||
org.freedesktop.impl.portal.Screenshot=wlr
|
||||
org.freedesktop.impl.portal.Inhibit=none
|
||||
|
|
@ -26,7 +26,7 @@ focused_opacity=1.0
|
|||
unfocused_opacity=1.0
|
||||
|
||||
# Animation Configuration(support type:zoom,slide)
|
||||
# tag_animation_direction: 1-horizontal,0-vertical
|
||||
# tag_animation_direction: 0-horizontal,1-vertical
|
||||
animations=1
|
||||
layer_animations=1
|
||||
animation_type_open=slide
|
||||
|
|
@ -34,7 +34,7 @@ animation_type_close=slide
|
|||
animation_fade_in=1
|
||||
animation_fade_out=1
|
||||
tag_animation_direction=1
|
||||
zoom_initial_ratio=0.4
|
||||
zoom_initial_ratio=0.3
|
||||
zoom_end_ratio=0.8
|
||||
fadein_begin_opacity=0.5
|
||||
fadeout_begin_opacity=0.8
|
||||
|
|
@ -57,7 +57,6 @@ scroller_default_proportion=0.8
|
|||
scroller_focus_center=0
|
||||
scroller_prefer_center=0
|
||||
edge_scroller_pointer_focus=1
|
||||
edge_scroller_focus_allow_speed=0.0
|
||||
scroller_default_proportion_single=1.0
|
||||
scroller_proportion_preset=0.5,0.8,1.0
|
||||
|
||||
|
|
@ -67,19 +66,10 @@ default_mfact=0.55
|
|||
default_nmaster=1
|
||||
smartgaps=0
|
||||
|
||||
# Dwindle Layout Setting
|
||||
dwindle_smart_split=0
|
||||
dwindle_drop_simple_split=1
|
||||
dwindle_manual_split=0
|
||||
dwindle_hsplit=1
|
||||
dwindle_vsplit=1
|
||||
dwindle_preserve_split=0
|
||||
|
||||
# Overview Setting
|
||||
hotarea_size=10
|
||||
enable_hotarea=0
|
||||
ov_tab_mode=1
|
||||
ov_no_resize=1
|
||||
enable_hotarea=1
|
||||
ov_tab_mode=0
|
||||
overviewgappi=5
|
||||
overviewgappo=30
|
||||
|
||||
|
|
@ -87,7 +77,7 @@ overviewgappo=30
|
|||
no_border_when_single=0
|
||||
axis_bind_apply_timeout=100
|
||||
focus_on_activate=1
|
||||
idleinhibit_ignore_visible=0
|
||||
inhibit_regardless_of_visibility=0
|
||||
sloppyfocus=1
|
||||
warpcursor=1
|
||||
focus_cross_monitor=0
|
||||
|
|
@ -96,7 +86,6 @@ enable_floating_snap=0
|
|||
snap_distance=30
|
||||
cursor_size=24
|
||||
drag_tile_to_tile=1
|
||||
drag_tile_small=1
|
||||
|
||||
# keyboard
|
||||
repeat_rate=25
|
||||
|
|
@ -130,8 +119,6 @@ scratchpad_height_ratio=0.9
|
|||
borderpx=4
|
||||
rootcolor=0x201b14ff
|
||||
bordercolor=0x444444ff
|
||||
dropcolor=0x8FBA7C55
|
||||
splitcolor=0xEB441EFF
|
||||
focuscolor=0xc9b890ff
|
||||
maximizescreencolor=0x89aa61ff
|
||||
urgentcolor=0xad401fff
|
||||
|
|
@ -194,13 +181,6 @@ bind=ALT,z,toggle_scratchpad
|
|||
# scroller layout
|
||||
bind=ALT,e,set_proportion,1.0
|
||||
bind=ALT,x,switch_proportion_preset,
|
||||
bind=alt+super+ctrl,Left,scroller_stack,left
|
||||
bind=alt+super+ctrl,Right,scroller_stack,right
|
||||
bind=alt+super+ctrl,Up,scroller_stack,up
|
||||
bind=alt+super+ctrl,Down,scroller_stack,down
|
||||
|
||||
#dwindle layout(manual split mode)
|
||||
bind=alt+shift,Return,dwindle_toggle_split_direction
|
||||
|
||||
# switch layout
|
||||
bind=SUPER,n,switch_layout
|
||||
|
|
@ -260,11 +240,12 @@ bind=CTRL+ALT,Left,resizewin,-50,+0
|
|||
bind=CTRL+ALT,Right,resizewin,+50,+0
|
||||
|
||||
# Mouse Button Bindings
|
||||
# btn_left and btn_right can't bind none mod key
|
||||
# NONE mode key only work in ov mode
|
||||
mousebind=SUPER,btn_left,moveresize,curmove
|
||||
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
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
title: Bindings & Input
|
||||
description: Keybindings, mouse gestures, and input devices.
|
||||
icon: Keyboard
|
||||
---
|
||||
|
||||
Configure how you interact with mangowm using flexible keybindings and input options.
|
||||
|
||||
<Cards>
|
||||
|
||||
<Card href="/docs/bindings/keys" title="Key Bindings" description="Keyboard shortcuts and modes" />
|
||||
|
||||
<Card href="/docs/bindings/mouse-gestures" title="Mouse Gestures" description="Touchpad and mouse gestures" />
|
||||
|
||||
</Cards>
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
---
|
||||
title: Key Bindings
|
||||
description: Define keyboard shortcuts and modes.
|
||||
---
|
||||
|
||||
## Syntax
|
||||
|
||||
Key bindings follow this format:
|
||||
|
||||
```ini
|
||||
bind[flags]=MODIFIERS,KEY,COMMAND,PARAMETERS
|
||||
```
|
||||
|
||||
- **Modifiers**: `SUPER`, `CTRL`, `ALT`, `SHIFT`, `NONE` (combine with `+`, e.g. `SUPER+CTRL+ALT`).
|
||||
- **Key**: Key name (from `xev` or `wev`) or keycode (e.g., `code:24` for `q`).
|
||||
|
||||
> **Info:** `bind` automatically converts keysym to keycode for comparison. This makes it compatible with all keyboard layouts, but the matching may not always be precise. If a key combination doesn't work on your keyboard layout, use a keycode instead (e.g., `code:24` instead of `q`).
|
||||
|
||||
### Flags
|
||||
|
||||
- `l`: Works even when screen is locked.
|
||||
- `s`: Uses keysym instead of keycode to bind.
|
||||
- `r`: Triggers on key release instead of press.
|
||||
- `p`: Pass key event to client.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```ini
|
||||
bind=SUPER,Q,killclient
|
||||
bindl=SUPER,L,spawn,swaylock
|
||||
|
||||
# Using keycode instead of key name
|
||||
bind=ALT,code:24,killclient
|
||||
|
||||
# Combining keycodes for modifiers and keys
|
||||
bind=code:64,code:24,killclient
|
||||
bind=code:64+code:133,code:24,killclient
|
||||
|
||||
# Bind with no modifier
|
||||
bind=NONE,XF86MonBrightnessUp,spawn,brightnessctl set +5%
|
||||
|
||||
# Bind a modifier key itself as the trigger key
|
||||
bind=alt,shift_l,switch_keyboard_layout
|
||||
```
|
||||
|
||||
## Key Modes (Submaps)
|
||||
|
||||
You can divide key bindings into named modes. Rules:
|
||||
|
||||
1. Set `keymode=<name>` before a group of `bind` lines — those binds only apply in that mode.
|
||||
2. If no `keymode` is set before a bind, it belongs to the `default` mode.
|
||||
3. The special `common` keymode applies its binds **across all modes**.
|
||||
|
||||
Use `setkeymode` to switch modes, and `mmsg get keymode` to query the current mode.
|
||||
|
||||
```ini
|
||||
# Binds in 'common' apply in every mode
|
||||
keymode=common
|
||||
bind=SUPER,r,reload_config
|
||||
|
||||
# Default mode bindings
|
||||
keymode=default
|
||||
bind=ALT,Return,spawn,foot
|
||||
bind=SUPER,F,setkeymode,resize
|
||||
|
||||
# 'resize' mode bindings
|
||||
keymode=resize
|
||||
bind=NONE,Left,resizewin,-10,0
|
||||
bind=NONE,Right,resizewin,+10,0
|
||||
bind=NONE,Escape,setkeymode,default
|
||||
```
|
||||
|
||||
### Single Modifier Key Binding
|
||||
|
||||
When binding a modifier key itself, use `NONE` for press and the modifier name for release:
|
||||
|
||||
```ini
|
||||
# Trigger on press of Super key
|
||||
bind=none,Super_L,spawn,rofi -show run
|
||||
|
||||
# Trigger on release of Super key
|
||||
bindr=Super,Super_L,spawn,rofi -show run
|
||||
```
|
||||
|
||||
## Dispatchers List
|
||||
|
||||
### Window Management
|
||||
|
||||
| Command | Param | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `killclient` | `force` | Close the focused window. If `force` is specified, sends `SIGKILL`. |
|
||||
| `togglefloating` | - | Toggle floating state. |
|
||||
| `toggle_all_floating` | - | Toggle all visible clients floating state. |
|
||||
| `togglefullscreen` | - | Toggle fullscreen. |
|
||||
| `togglefakefullscreen` | - | Toggle "fake" fullscreen (remains constrained). |
|
||||
| `togglemaximizescreen` | - | Maximize window (keep decoration/bar). |
|
||||
| `toggleglobal` | - | Pin window to all tags. |
|
||||
| `toggle_render_border` | - | Toggle border rendering. |
|
||||
| `centerwin` | - | Center the floating window. |
|
||||
| `minimized` | - | Minimize window to scratchpad. |
|
||||
| `restore_minimized` | - | Restore window from scratchpad. |
|
||||
| `toggle_scratchpad` | - | Toggle scratchpad. |
|
||||
| `toggle_named_scratchpad` | `appid,title,cmd` | Toggle named scratchpad. Launches app if not running, otherwise shows/hides it. |
|
||||
|
||||
### Focus & Movement
|
||||
|
||||
| Command | Param | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `focusdir` | `left/right/up/down` | Focus window in direction. |
|
||||
| `focusstack` | `next/prev` | Cycle focus within the stack. |
|
||||
| `focuslast` | - | Focus the previously active window. |
|
||||
| `exchange_client` | `left/right/up/down` | Swap window with neighbor in direction. |
|
||||
| `exchange_stack_client` | `next/prev` | Exchange window position in stack. |
|
||||
| `zoom` | - | Swap focused window with Master. |
|
||||
|
||||
### Tags & Monitors
|
||||
|
||||
| Command | Param | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `view` | `-1/0/1-9` or `mask [,synctag]` | View tag. `-1` = previous tagset, `0` = all tags, `1-9` = specific tag, mask e.g. `1\|3\|5`. Optional `synctag` (0/1) syncs the action to all monitors. |
|
||||
| `viewtoleft` | `[synctag]` | View previous tag. Optional `synctag` (0/1) syncs to all monitors. |
|
||||
| `viewtoright` | `[synctag]` | View next tag. Optional `synctag` (0/1) syncs to all monitors. |
|
||||
| `viewtoleft_have_client` | `[synctag]` | View left tag and focus client if present. Optional `synctag` (0/1). |
|
||||
| `viewtoright_have_client` | `[synctag]` | View right tag and focus client if present. Optional `synctag` (0/1). |
|
||||
| `viewcrossmon` | `tag,monitor_spec` | View specified tag on specified monitor. |
|
||||
| `tag` | `1-9 [,synctag]` | Move window to tag. Optional `synctag` (0/1) syncs to all monitors. |
|
||||
| `tagsilent` | `1-9` | Move window to tag without focusing it. |
|
||||
| `tagtoleft` | `[synctag]` | Move window to left tag. Optional `synctag` (0/1). |
|
||||
| `tagtoright` | `[synctag]` | Move window to right tag. Optional `synctag` (0/1). |
|
||||
| `tagcrossmon` | `tag,monitor_spec` | Move window to specified tag on specified monitor. |
|
||||
| `toggletag` | `0-9` | Toggle tag on window (0 means all tags). |
|
||||
| `toggleview` | `1-9` | Toggle tag view. |
|
||||
| `comboview` | `1-9` | View multi tags pressed simultaneously. |
|
||||
| `focusmon` | `left/right/up/down/monitor_spec` | Focus monitor by direction or [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
|
||||
| `tagmon` | `left/right/up/down/monitor_spec,[keeptag]` | Move window to monitor by direction or [monitor spec](/docs/configuration/monitors#monitor-spec-format). `keeptag` is 0 or 1. |
|
||||
|
||||
### Layouts
|
||||
|
||||
| Command | Param | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `setlayout` | `name` | Switch to layout (e.g., `scroller`, `tile`). |
|
||||
| `switch_layout` | - | Cycle through available layouts. |
|
||||
| `incnmaster` | `+1/-1` | Increase/Decrease number of master windows. |
|
||||
| `setmfact` | `+0.05` | Increase/Decrease master area size. |
|
||||
| `set_proportion` | `float` | Set scroller window proportion (0.0–1.0). |
|
||||
| `switch_proportion_preset` | - | Cycle proportion presets of scroller window. |
|
||||
| `scroller_stack` | `left/right/up/down` | Move window inside/outside scroller stack by direction. |
|
||||
| `incgaps` | `+/-value` | Adjust gap size. |
|
||||
| `togglegaps` | - | Toggle gaps. |
|
||||
| `dwindle_toggle_split_direction` | - | Toggle split direction in dwindle layout. |
|
||||
| `dwindle_split_horizontal` | - | Set split window direction to horizontal in dwindle layout. |
|
||||
| `dwindle_split_vertical` | - | Set split window direction to vertical in dwindle layout. |
|
||||
|
||||
### System
|
||||
|
||||
| Command | Param | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `spawn` | `cmd` | Execute a command. |
|
||||
| `spawn_shell` | `cmd` | Execute shell command (supports pipes `\|`). |
|
||||
| `spawn_on_empty` | `cmd,tagnumber` | Open command on empty tag. |
|
||||
| `reload_config` | - | Hot-reload configuration. |
|
||||
| `quit` | - | Exit mangowm. |
|
||||
| `toggleoverview` | - | Toggle overview mode. |
|
||||
| `create_virtual_output` | - | Create a headless monitor (for VNC/Sunshine). |
|
||||
| `destroy_all_virtual_output` | - | Destroy all virtual monitors. |
|
||||
| `toggleoverlay` | - | Toggle overlay state for the focused window. |
|
||||
| `toggle_trackpad_enable` | - | Toggle trackpad enable. |
|
||||
| `setkeymode` | `mode` | Set keymode. |
|
||||
| `switch_keyboard_layout` | `[index]` | Switch keyboard layout. Optional index (0, 1, 2...) to switch to specific layout. |
|
||||
| `setoption` | `key,value` | Set config option temporarily. |
|
||||
| `disable_monitor` | `monitor_spec` | Shutdown monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
|
||||
| `enable_monitor` | `monitor_spec` | Power on monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
|
||||
| `toggle_monitor` | `monitor_spec` | Toggle monitor power. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
|
||||
|
||||
### Media Controls
|
||||
|
||||
> **Warning:** Some keyboards don't send standard media keys. Run `wev` and press your key to check the exact key name.
|
||||
|
||||
#### Brightness
|
||||
|
||||
Requires: `brightnessctl`
|
||||
|
||||
```ini
|
||||
bind=NONE,XF86MonBrightnessUp,spawn,brightnessctl s +2%
|
||||
bind=SHIFT,XF86MonBrightnessUp,spawn,brightnessctl s 100%
|
||||
bind=NONE,XF86MonBrightnessDown,spawn,brightnessctl s 2%-
|
||||
bind=SHIFT,XF86MonBrightnessDown,spawn,brightnessctl s 1%
|
||||
```
|
||||
|
||||
#### Volume
|
||||
|
||||
Requires: `wpctl` (WirePlumber)
|
||||
|
||||
```ini
|
||||
bind=NONE,XF86AudioRaiseVolume,spawn,wpctl set-volume @DEFAULT_SINK@ 5%+
|
||||
bind=NONE,XF86AudioLowerVolume,spawn,wpctl set-volume @DEFAULT_SINK@ 5%-
|
||||
bind=NONE,XF86AudioMute,spawn,wpctl set-mute @DEFAULT_SINK@ toggle
|
||||
bind=SHIFT,XF86AudioMute,spawn,wpctl set-mute @DEFAULT_SOURCE@ toggle
|
||||
```
|
||||
|
||||
#### Playback
|
||||
|
||||
Requires: `playerctl`
|
||||
|
||||
```ini
|
||||
bind=NONE,XF86AudioNext,spawn,playerctl next
|
||||
bind=NONE,XF86AudioPrev,spawn,playerctl previous
|
||||
bind=NONE,XF86AudioPlay,spawn,playerctl play-pause
|
||||
```
|
||||
|
||||
### Floating Window Movement
|
||||
|
||||
| Command | Param | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `smartmovewin` | `left/right/up/down` | Move floating window by snap distance. |
|
||||
| `smartresizewin` | `left/right/up/down` | Resize floating window by snap distance. |
|
||||
| `movewin` | `(x,y)` | Move floating window. |
|
||||
| `resizewin` | `(width,height)` | Resize window. |
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"title": "Bindings & Input",
|
||||
"pages": ["keys", "mouse-gestures"]
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
---
|
||||
title: Mouse & Gestures
|
||||
description: Configure mouse buttons, scrolling, gestures, and lid switches.
|
||||
---
|
||||
|
||||
## Mouse Bindings
|
||||
|
||||
Assign actions to mouse button presses with optional modifier keys.
|
||||
|
||||
### Syntax
|
||||
|
||||
```ini
|
||||
mousebind=MODIFIERS,BUTTON,COMMAND,PARAMETERS
|
||||
```
|
||||
|
||||
- **Modifiers**: `SUPER`, `CTRL`, `ALT`, `SHIFT`, `NONE`. Combine with `+` (e.g., `SUPER+CTRL`).
|
||||
- **Buttons**: Can be specified in one of the following ways:
|
||||
- **Standard Names**: `btn_left`, `btn_right`, `btn_middle`, `btn_side`, `btn_extra`, `btn_forward`, `btn_back`, `btn_task`
|
||||
- **Hardware Codes**: `code:NUMBER` (e.g., `code:272`, `code:273`, useful for binding non-standard or extra mouse buttons)
|
||||
|
||||
> **Warning:** When modifiers are set to `NONE`, only `btn_middle` works in normal mode. `btn_left` and `btn_right` only work in overview mode.
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
```ini
|
||||
# Window manipulation
|
||||
mousebind=SUPER,btn_left,moveresize,curmove
|
||||
mousebind=SUPER,btn_right,moveresize,curresize
|
||||
mousebind=SUPER+CTRL,btn_right,killclient
|
||||
|
||||
mousebind=NONE,code:273,togglemaximizescreen,0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Axis Bindings
|
||||
|
||||
Map scroll wheel movements to actions for workspace and window navigation.
|
||||
|
||||
### Syntax
|
||||
|
||||
```ini
|
||||
axisbind=MODIFIERS,DIRECTION,COMMAND,PARAMETERS
|
||||
```
|
||||
|
||||
- **Direction**: `UP`, `DOWN`, `LEFT`, `RIGHT`
|
||||
|
||||
### Examples
|
||||
|
||||
```ini
|
||||
axisbind=SUPER,UP,viewtoleft_have_client
|
||||
axisbind=SUPER,DOWN,viewtoright_have_client
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gesture Bindings
|
||||
|
||||
Enable touchpad swipe gestures for navigation and window management.
|
||||
|
||||
### Syntax
|
||||
|
||||
```ini
|
||||
gesturebind=MODIFIERS,DIRECTION,FINGERS,COMMAND,PARAMETERS
|
||||
```
|
||||
|
||||
- **Direction**: `up`, `down`, `left`, `right`
|
||||
- **Fingers**: `3` or `4`
|
||||
|
||||
> **Info:** Gestures require proper touchpad configuration. See [Input Devices](/docs/configuration/input) for touchpad settings like `tap_to_click` and `disable_while_typing`.
|
||||
|
||||
### Examples
|
||||
|
||||
```ini
|
||||
# 3-finger: Window focus
|
||||
gesturebind=none,left,3,focusdir,left
|
||||
gesturebind=none,right,3,focusdir,right
|
||||
gesturebind=none,up,3,focusdir,up
|
||||
gesturebind=none,down,3,focusdir,down
|
||||
|
||||
# 4-finger: Workspace navigation
|
||||
gesturebind=none,left,4,viewtoleft_have_client
|
||||
gesturebind=none,right,4,viewtoright_have_client
|
||||
gesturebind=none,up,4,toggleoverview
|
||||
gesturebind=none,down,4,toggleoverview
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Switch Bindings
|
||||
|
||||
Trigger actions on hardware events like laptop lid open/close.
|
||||
|
||||
### Syntax
|
||||
|
||||
```ini
|
||||
switchbind=FOLD_STATE,COMMAND,PARAMETERS
|
||||
```
|
||||
|
||||
- **Fold State**: `fold` (lid closed), `unfold` (lid opened)
|
||||
|
||||
> **Warning:** Disable system lid handling in `/etc/systemd/logind.conf`:
|
||||
>
|
||||
> ```ini
|
||||
> HandleLidSwitch=ignore
|
||||
> HandleLidSwitchExternalPower=ignore
|
||||
> HandleLidSwitchDocked=ignore
|
||||
> ```
|
||||
|
||||
### Examples
|
||||
|
||||
```ini
|
||||
switchbind=fold,spawn,swaylock -f -c 000000
|
||||
switchbind=unfold,spawn,wlr-dpms on
|
||||
```
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
---
|
||||
title: Basic Configuration
|
||||
description: Learn how to configure mangowm files, environment variables, and autostart scripts.
|
||||
---
|
||||
|
||||
## Configuration File
|
||||
|
||||
mangowm uses a simple configuration file format. By default, it looks for a configuration file in `~/.config/mango/`.
|
||||
|
||||
1. **Locate Default Config**
|
||||
|
||||
A fallback configuration is provided at `/etc/mango/config.conf`. You can use this as a reference.
|
||||
|
||||
2. **Create User Config**
|
||||
|
||||
Copy the default config to your local config directory to start customizing.
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/mango
|
||||
cp /etc/mango/config.conf ~/.config/mango/config.conf
|
||||
```
|
||||
|
||||
3. **Launch with Custom Config (Optional)**
|
||||
|
||||
If you prefer to keep your config elsewhere, you can launch mango with the `-c` flag.
|
||||
|
||||
```bash
|
||||
mango -c /path/to/your_config.conf
|
||||
```
|
||||
|
||||
### Sub-Configuration
|
||||
|
||||
To keep your configuration organized, you can split it into multiple files and include them using the `source` keyword.
|
||||
|
||||
```ini
|
||||
# Import keybindings from a separate file
|
||||
source=~/.config/mango/bind.conf
|
||||
|
||||
# Relative paths work too
|
||||
source=./theme.conf
|
||||
|
||||
# Optional: ignore if file doesn't exist (useful for shared configs)
|
||||
source-optional=~/.config/mango/optional.conf
|
||||
```
|
||||
|
||||
### Validate Configuration
|
||||
|
||||
You can check your configuration for errors without starting mangowm:
|
||||
|
||||
```bash
|
||||
mango -c /path/to/config.conf -p
|
||||
```
|
||||
|
||||
Use with `source-optional` for shared configs across different setups.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
You can define environment variables directly within your config file. These are set before the window manager fully initializes.
|
||||
|
||||
> **Warning:** Environment variables defined here will be **reset** every time you reload the configuration.
|
||||
|
||||
```ini
|
||||
env=QT_IM_MODULES,wayland;fcitx
|
||||
env=XMODIFIERS,@im=fcitx
|
||||
```
|
||||
|
||||
## Autostart
|
||||
|
||||
mangowm can automatically run commands or scripts upon startup. There are two modes for execution:
|
||||
|
||||
| Command | Behavior | Usage Case |
|
||||
| :--- | :--- | :--- |
|
||||
| `exec-once` | Runs **only once** when mangowm starts. | Status bars, Wallpapers, Notification daemons |
|
||||
| `exec` | Runs **every time** the config is reloaded. | Scripts that need to refresh settings |
|
||||
|
||||
### Example Setup
|
||||
|
||||
```ini
|
||||
# Start the status bar once
|
||||
exec-once=waybar
|
||||
|
||||
# Set wallpaper
|
||||
exec-once=swaybg -i ~/.config/mango/wallpaper/room.png
|
||||
|
||||
# Reload a custom script on config change
|
||||
exec=bash ~/.config/mango/reload-settings.sh
|
||||
```
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
title: Configuration
|
||||
description: Configure mangowm with config files, environment variables, and autostart.
|
||||
icon: Settings
|
||||
---
|
||||
|
||||
Configure mangowm through config files, environment variables, and autostart.
|
||||
|
||||
<Cards>
|
||||
|
||||
<Card href="/docs/configuration/basics" title="Basics" description="Config files, env vars, exec-once, exec" />
|
||||
|
||||
<Card href="/docs/configuration/monitors" title="Monitors" description="Monitor settings and resolution" />
|
||||
|
||||
<Card href="/docs/configuration/input" title="Input" description="Keyboard, mouse, and touchpad" />
|
||||
|
||||
<Card href="/docs/configuration/xdg-portals" title="XDG Portals" description="File pickers and notifications" />
|
||||
|
||||
<Card href="/docs/configuration/miscellaneous" title="Miscellaneous" description="Additional options" />
|
||||
|
||||
</Cards>
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
---
|
||||
title: Input Devices
|
||||
description: Configure keyboard layouts, mouse sensitivity, and touchpad gestures.
|
||||
---
|
||||
|
||||
## Device Configuration
|
||||
|
||||
mangowm provides granular control over different input devices.
|
||||
|
||||
### Keyboard Settings
|
||||
|
||||
Control key repeat rates and layout rules.
|
||||
|
||||
| Setting | Type | Default | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `repeat_rate` | `int` | `25` | How many times a key repeats per second. |
|
||||
| `repeat_delay` | `int` | `600` | Delay (ms) before a held key starts repeating. |
|
||||
| `numlockon` | `0` or `1` | `0` | Enable NumLock on startup. |
|
||||
| `xkb_rules_rules` | `string` | - | XKB rules file (e.g., `evdev`, `base`). Usually auto-detected. |
|
||||
| `xkb_rules_model` | `string` | - | Keyboard model (e.g., `pc104`, `macbook`). |
|
||||
| `xkb_rules_layout` | `string` | - | Keyboard layout code (e.g., `us`, `de`, `us,de`). |
|
||||
| `xkb_rules_variant` | `string` | - | Layout variant (e.g., `dvorak`, `colemak`, `intl`). |
|
||||
| `xkb_rules_options` | `string` | - | XKB options (e.g., `caps:escape`, `ctrl:nocaps`). |
|
||||
|
||||
**Example:**
|
||||
|
||||
```ini
|
||||
repeat_rate=40
|
||||
repeat_delay=300
|
||||
numlockon=1
|
||||
xkb_rules_layout=us,de
|
||||
xkb_rules_variant=dvorak
|
||||
xkb_rules_options=caps:escape,ctrl:nocaps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Mouse Settings
|
||||
|
||||
Configuration for external mice.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `mouse_natural_scrolling` | `0` | Invert scrolling direction. |
|
||||
| `mouse_accel_profile` | `2` | `0` (None), `1` (Flat), `2` (Adaptive). |
|
||||
| `mouse_accel_speed` | `0.0` | Speed adjustment (-1.0 to 1.0). |
|
||||
| `left_handed` | `0` | Swap left and right buttons. |
|
||||
| `axis_scroll_factor` | `1.0` | Scroll factor for axis scroll speed (0.1–10.0). |
|
||||
---
|
||||
|
||||
### Trackpad Settings
|
||||
|
||||
Specific settings for laptop touchpads. Some settings may require a relogin to take effect.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `disable_trackpad` | `0` | Set to `1` to disable the trackpad entirely. |
|
||||
| `tap_to_click` | `1` | Tap to trigger a left click. |
|
||||
| `tap_and_drag` | `1` | Tap and hold to drag items. |
|
||||
| `trackpad_natural_scrolling` | `0` | Invert scrolling direction (natural scrolling). |
|
||||
| `trackpad_accel_profile` | `2` | `0` (None), `1` (Flat), `2` (Adaptive). |
|
||||
| `trackpad_accel_speed` | `0.0` | Speed adjustment (-1.0 to 1.0). |
|
||||
| `scroll_button` | `274` | The mouse button that use for scrolling(272 to 279).
|
||||
| `scroll_method` | `1` | `1` (Two-finger), `2` (Edge), `4` (Button). |
|
||||
| `click_method` | `1` | `1` (Button areas), `2` (Clickfinger). |
|
||||
| `send_events_mode` | `0` | `0` (Enabled), `1` (Disabled), `2` (Disabled on external mouse). |
|
||||
| `drag_lock` | `1` | Lock dragging after tapping. |
|
||||
| `disable_while_typing` | `1` | Disable trackpad while typing. |
|
||||
| `left_handed` | `0` | Swap left/right buttons. |
|
||||
| `middle_button_emulation` | `0` | Emulate middle button. |
|
||||
| `swipe_min_threshold` | `1` | Minimum swipe threshold when use gesture. |
|
||||
| `button_map` | `0` | `0` (Left/right/middle), `1` (Left/middle/right). |
|
||||
| `trackpad_scroll_factor` | `1.0` | Scroll factor for trackpad scroll speed (0.1–10.0). |
|
||||
---
|
||||
|
||||
**Detailed descriptions:**
|
||||
|
||||
- `scroll_button` values:
|
||||
- `272` — Left button.
|
||||
- `273` — Right button.
|
||||
- `274` — Middle button.
|
||||
- `275` — Side button.
|
||||
- `276` — Extra button.
|
||||
- `277` — Forward button.
|
||||
- `278` — Back button.
|
||||
- `279` — Task button.
|
||||
|
||||
- `scroll_method` values:
|
||||
- `0` — Never send scroll events (no scrolling).
|
||||
- `1` — Two-finger scrolling: send scroll events when two fingers are logically down on the device.
|
||||
- `2` — Edge scrolling: send scroll events when a finger moves along the bottom or right edge.
|
||||
- `4` — Button scrolling: send scroll events when a button is held and the device moves along a scroll axis.
|
||||
|
||||
- `click_method` values:
|
||||
- `0` — No software click emulation.
|
||||
- `1` — Button areas: use software-defined areas on the touchpad to generate button events.
|
||||
- `2` — Clickfinger: the number of fingers determines which button is pressed.
|
||||
|
||||
- `mouse_accel_profile` or `trackpad_scroll_profile` values:
|
||||
- `0` — No acceleration.
|
||||
- `1` — Flat: no dynamic acceleration. Pointer speed = original input speed × (1 + `mouse_accel_speed`).
|
||||
- `2` — Adaptive: slow movement results in less acceleration, fast movement results in more.
|
||||
|
||||
- `button_map` values:
|
||||
- `0` — 1/2/3 finger tap maps to left / right / middle.
|
||||
- `1` — 1/2/3 finger tap maps to left / middle / right.
|
||||
|
||||
- `send_events_mode` values:
|
||||
- `0` — Send events from this device normally.
|
||||
- `1` — Do not send events from this device.
|
||||
- `2` — Disable this device when an external pointer device is plugged in.
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
## Keyboard Layout Switching
|
||||
|
||||
To bind multiple layouts and toggle between them, define the layouts in `xkb_rules_layout` and use `xkb_rules_options` to set a toggle key combination. Then bind `switch_keyboard_layout` to trigger a switch.
|
||||
|
||||
```ini
|
||||
# Define two layouts: US QWERTY and US Dvorak
|
||||
xkb_rules_layout=us,us
|
||||
xkb_rules_variant=,dvorak
|
||||
xkb_rules_options=grp:lalt_lshift_toggle
|
||||
```
|
||||
|
||||
Or bind it manually to a key:
|
||||
|
||||
```ini
|
||||
# Bind Alt+Shift_L to cycle keyboard layout
|
||||
bind=alt,shift_l,switch_keyboard_layout
|
||||
```
|
||||
|
||||
Use `mmsg get keyboardlayout` to query the current layout.
|
||||
|
||||
---
|
||||
|
||||
## Input Method Editor (IME)
|
||||
|
||||
To use Fcitx5 or IBus, set these environment variables in your config file.
|
||||
|
||||
> **Info:** These settings require a restart of the window manager to take effect.
|
||||
|
||||
**For Fcitx5:**
|
||||
|
||||
```ini
|
||||
env=GTK_IM_MODULE,fcitx
|
||||
env=QT_IM_MODULE,fcitx
|
||||
env=QT_IM_MODULES,wayland;fcitx
|
||||
env=SDL_IM_MODULE,fcitx
|
||||
env=XMODIFIERS,@im=fcitx
|
||||
env=GLFW_IM_MODULE,ibus
|
||||
```
|
||||
|
||||
**For IBus:**
|
||||
|
||||
```ini
|
||||
env=GTK_IM_MODULE,ibus
|
||||
env=QT_IM_MODULE,ibus
|
||||
env=XMODIFIERS,@im=ibus
|
||||
```
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"title": "Configuration",
|
||||
"pages": ["basics", "monitors", "input", "xdg-portals", "miscellaneous"]
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
---
|
||||
title: Miscellaneous
|
||||
description: Advanced settings for XWayland, focus behavior, and system integration.
|
||||
---
|
||||
|
||||
## System & Hardware
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `xwayland_persistence` | `1` | Keep XWayland running even when no X11 apps are open (reduces startup lag). |
|
||||
| `syncobj_enable` | `0` | Enable `drm_syncobj` timeline support (helps with gaming stutter/lag). **Requires restart.** |
|
||||
| `allow_lock_transparent` | `0` | Allow the lock screen to be transparent. |
|
||||
| `allow_shortcuts_inhibit` | `1` | Allow shortcuts to be inhibited by clients. |
|
||||
| `vrr` | - | Set via [monitor rule](/docs/configuration/monitors#monitor-rules). |
|
||||
|
||||
## Focus & Input
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `focus_on_activate` | `1` | Automatically focus windows when they request activation. |
|
||||
| `sloppyfocus` | `1` | Focus follows the mouse cursor. |
|
||||
| `warpcursor` | `1` | Warp the cursor to the center of the window when focus changes via keyboard. |
|
||||
| `cursor_hide_timeout` | `0` | Hide the cursor after `N` seconds of inactivity (`0` to disable). |
|
||||
| `cursor_hide_on_keypress` | `0` | Hide the cursor on keypress. |
|
||||
| `drag_tile_to_tile` | `0` | Allow dragging a tiled window onto another to swap their positions. |
|
||||
| `drag_tile_small` | `1` | Allow dragging a tiled window temporarily to small size.|
|
||||
| `drag_corner` | `3` | Corner for drag-to-tile detection (0: none, 1–3: corners, 4: auto-detect). |
|
||||
| `drag_warp_cursor` | `1` | Warp cursor when dragging windows to tile. |
|
||||
| `axis_bind_apply_timeout` | `100` | Timeout (ms) for detecting consecutive scroll events for axis bindings. |
|
||||
|
||||
## Multi-Monitor & Tags
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `focus_cross_monitor` | `0` | Allow directional focus to cross monitor boundaries. |
|
||||
| `exchange_cross_monitor` | `0` | Allow exchanging clients across monitor boundaries. |
|
||||
| `focus_cross_tag` | `0` | Allow directional focus to cross into other tags. |
|
||||
| `view_current_to_back` | `0` | Toggling the current tag switches back to the previously viewed tag. |
|
||||
| `scratchpad_cross_monitor` | `0` | Share the scratchpad pool across all monitors. |
|
||||
| `single_scratchpad` | `1` | Only allow one scratchpad (named or standard) to be visible at a time. |
|
||||
|
||||
## Window Behavior
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `enable_floating_snap` | `0` | Snap floating windows to edges or other windows. |
|
||||
| `snap_distance` | `30` | Max distance (pixels) to trigger floating snap. |
|
||||
| `no_border_when_single` | `0` | Remove window borders when only one window is visible on the tag. |
|
||||
| `idleinhibit_ignore_visible` | `0` | Allow invisible clients (e.g., background audio players) to inhibit idle. |
|
||||
| `tag_carousel` | `0` | Enable tag carousel (cycling through tags). |
|
||||
| `drag_tile_refresh_interval` | `8.0` | Interval (1.0–16.0) to refresh tiled window resize during drag. Too small may cause application lag. |
|
||||
| `drag_floating_refresh_interval` | `8.0` | Interval (1.0–16.0) to refresh floating window resize during drag. Too small may cause application lag. |
|
||||
|
|
@ -1,276 +0,0 @@
|
|||
---
|
||||
title: Monitors
|
||||
description: Manage display outputs, resolution, scaling, and tearing.
|
||||
---
|
||||
|
||||
## Monitor Rules
|
||||
|
||||
You can configure each display output individually using the `monitorrule` keyword.
|
||||
|
||||
**Syntax:**
|
||||
|
||||
```ini
|
||||
monitorrule=name:Values,Parameter:Values,Parameter:Values
|
||||
```
|
||||
|
||||
> **Info:** If any of the matching fields (`name`, `make`, `model`, `serial`) are set, **all** of the set ones must match to be considered a match. Use `wlr-randr` to get your monitor's name, make, model, and serial.
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `name` | string | Any | Match by monitor name (supports regex) |
|
||||
| `make` | string | Any | Match by monitor manufacturer |
|
||||
| `model` | string | Any | Match by monitor model |
|
||||
| `serial` | string | Any | Match by monitor serial number |
|
||||
| `width` | integer | 0-9999 | Monitor width |
|
||||
| `height` | integer | 0-9999 | Monitor height |
|
||||
| `refresh` | float | 0.001-9999.0 | Monitor refresh rate |
|
||||
| `x` | integer | 0-99999 | X position |
|
||||
| `y` | integer | 0-99999 | Y position |
|
||||
| `scale` | float | 0.01-100.0 | Monitor scale |
|
||||
| `vrr` | integer | 0, 1 | Enable variable refresh rate |
|
||||
| `rr` | integer | 0-7 | Monitor transform |
|
||||
| `custom` | integer | 0, 1 | Enable custom mode (not supported on all displays — may cause black screen) |
|
||||
|
||||
### Transform Values
|
||||
|
||||
| Value | Rotation |
|
||||
| :--- | :--- |
|
||||
| `0` | No transform |
|
||||
| `1` | 90° counter-clockwise |
|
||||
| `2` | 180° counter-clockwise |
|
||||
| `3` | 270° counter-clockwise |
|
||||
| `4` | 180° vertical flip |
|
||||
| `5` | Flip + 90° counter-clockwise |
|
||||
| `6` | Flip + 180° counter-clockwise |
|
||||
| `7` | Flip + 270° counter-clockwise |
|
||||
|
||||
> **Critical:** If you use XWayland applications, **never use negative coordinates** for your monitor positions. This is a known XWayland bug that causes click events to malfunction. Always arrange your monitors starting from `0,0` and extend into positive coordinates.
|
||||
|
||||
> **Note:** that "name" is a regular expression. If you want an exact match, you need to add `^` and `$` to the beginning and end of the expression, for example, `^eDP-1$` matches exactly the string `eDP-1`.
|
||||
|
||||
### Examples
|
||||
|
||||
```ini
|
||||
# Laptop display: 1080p, 60Hz, positioned at origin
|
||||
monitorrule=name:^eDP-1$,width:1920,height:1080,refresh:60,x:0,y:10
|
||||
|
||||
# Match by make and model instead of name
|
||||
monitorrule=make:Chimei Innolux Corporation,model:0x15F5,width:1920,height:1080,refresh:60,x:0,y:0
|
||||
|
||||
# Virtual monitor with pattern matching
|
||||
monitorrule=name:HEADLESS-.*,width:1920,height:1080,refresh:60,x:1926,y:0,scale:1,rr:0,vrr:0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitor Spec Format
|
||||
|
||||
Several commands (`focusmon`, `tagmon`, `disable_monitor`, `enable_monitor`, `toggle_monitor`, `viewcrossmon`, `tagcrossmon`) accept a **monitor_spec** string to identify a monitor.
|
||||
|
||||
**Format:**
|
||||
|
||||
```text
|
||||
name:xxx&&make:xxx&&model:xxx&&serial:xxx
|
||||
```
|
||||
|
||||
- Any field can be omitted and there is no order requirement.
|
||||
- If all fields are omitted, the string is treated as the monitor name directly (e.g., `eDP-1`).
|
||||
- Use `wlr-randr` to find your monitor's name, make, model, and serial.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# By name (shorthand)
|
||||
mmsg dispatch toggle_monitor,eDP-1
|
||||
|
||||
# By make and model
|
||||
mmsg dispatch toggle_monitor,make:Chimei Innolux Corporation&&model:0x15F5
|
||||
|
||||
# By serial
|
||||
mmsg dispatch toggle_monitor,serial:12345678
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tearing (Game Mode)
|
||||
|
||||
Tearing allows games to bypass the compositor's VSync for lower latency.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `allow_tearing` | `0` | Global tearing control: `0` (Disable), `1` (Enable), `2` (Fullscreen only). |
|
||||
|
||||
### Configuration
|
||||
|
||||
**Enable Globally:**
|
||||
|
||||
```ini
|
||||
allow_tearing=1
|
||||
```
|
||||
|
||||
**Enable per Window:**
|
||||
|
||||
Use a window rule to force tearing for specific games.
|
||||
|
||||
```ini
|
||||
windowrule=force_tearing:1,title:vkcube
|
||||
```
|
||||
|
||||
### Tearing Behavior Matrix
|
||||
|
||||
| `force_tearing` \ `allow_tearing` | DISABLED (0) | ENABLED (1) | FULLSCREEN_ONLY (2) |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **UNSPECIFIED** (0) | Not Allowed | Follows tearing_hint | Only fullscreen follows tearing_hint |
|
||||
| **ENABLED** (1) | Not Allowed | Allowed | Only fullscreen allowed |
|
||||
| **DISABLED** (2) | Not Allowed | Not Allowed | Not Allowed |
|
||||
|
||||
### Graphics Card Compatibility
|
||||
|
||||
> **Warning:** Some graphics cards require setting the `WLR_DRM_NO_ATOMIC` environment variable before mango starts to successfully enable tearing.
|
||||
|
||||
Add this to `/etc/environment` and reboot:
|
||||
|
||||
```bash
|
||||
WLR_DRM_NO_ATOMIC=1
|
||||
```
|
||||
|
||||
Or run mango with the environment variable:
|
||||
|
||||
```bash
|
||||
WLR_DRM_NO_ATOMIC=1 mango
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GPU Compatibility
|
||||
|
||||
If mango cannot display correctly or shows a black screen, try selecting a specific GPU:
|
||||
|
||||
```bash
|
||||
# Use a single GPU
|
||||
WLR_DRM_DEVICES=/dev/dri/card1 mango
|
||||
|
||||
# Use multiple GPUs
|
||||
WLR_DRM_DEVICES=/dev/dri/card0:/dev/dri/card1 mango
|
||||
```
|
||||
|
||||
Some GPUs have compatibility issues with `syncobj_enable=1` — it may crash apps like `kitty` that use syncobj. Set `WLR_DRM_NO_ATOMIC=1` in `/etc/environment` and reboot to resolve this.
|
||||
|
||||
---
|
||||
|
||||
## Power Management
|
||||
|
||||
You can control monitor power using the `mmsg` IPC tool.
|
||||
|
||||
```bash
|
||||
# Turn off
|
||||
mmsg dispatch disable_monitor,eDP-1
|
||||
|
||||
# Turn on
|
||||
mmsg dispatch enable_monitor,eDP-1
|
||||
|
||||
# Toggle
|
||||
mmsg dispatch toggle_monitor,eDP-1
|
||||
```
|
||||
|
||||
You can also use `wlr-randr` for monitor management:
|
||||
|
||||
```bash
|
||||
# Turn off monitor
|
||||
wlr-randr --output eDP-1 --off
|
||||
|
||||
# Turn on monitor
|
||||
wlr-randr --output eDP-1 --on
|
||||
|
||||
# Show all monitors
|
||||
wlr-randr
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Screen Scale
|
||||
|
||||
### Without Global Scale (Recommended)
|
||||
|
||||
- If you do not use XWayland apps, you can use monitor rules or `wlr-randr` to set a global monitor scale.
|
||||
- If you are using XWayland apps, it is not recommended to set a global monitor scale.
|
||||
|
||||
You can set scale like this, for example with a 1.4 factor.
|
||||
|
||||
**Dependencies:**
|
||||
|
||||
```bash
|
||||
yay -S xorg-xrdb
|
||||
yay -S xwayland-satellite
|
||||
```
|
||||
|
||||
**In config file:**
|
||||
|
||||
```ini
|
||||
env=QT_AUTO_SCREEN_SCALE_FACTOR,1
|
||||
env=QT_WAYLAND_FORCE_DPI,140
|
||||
```
|
||||
|
||||
**In autostart:**
|
||||
|
||||
```bash
|
||||
echo "Xft.dpi: 140" | xrdb -merge
|
||||
gsettings set org.gnome.desktop.interface text-scaling-factor 1.4
|
||||
```
|
||||
|
||||
**Edit autostart for XWayland:**
|
||||
|
||||
```bash
|
||||
# Start xwayland
|
||||
/usr/sbin/xwayland-satellite :11 &
|
||||
# Apply scale 1.4 for xwayland
|
||||
sleep 0.5s && echo "Xft.dpi: 140" | xrdb -merge
|
||||
```
|
||||
|
||||
### Using xwayland-satellite to Prevent Blurry XWayland Apps
|
||||
|
||||
If you use fractional scaling, you can use `xwayland-satellite` to automatically scale XWayland apps to prevent blurriness, for example with a scale of 1.4.
|
||||
|
||||
**Dependencies:**
|
||||
|
||||
```bash
|
||||
yay -S xwayland-satellite
|
||||
```
|
||||
|
||||
**In config file:**
|
||||
|
||||
```ini
|
||||
env=DISPLAY,:2
|
||||
exec-once=xwayland-satellite :2
|
||||
monitorrule=name:eDP-1,width:1920,height:1080,refresh:60,x:0,y:0,scale:1.4,vrr:0,rr:0
|
||||
```
|
||||
|
||||
> **Warning:** Use a `DISPLAY` value other than `:1` to avoid conflicting with mangowm.
|
||||
|
||||
---
|
||||
|
||||
## Virtual Monitors
|
||||
|
||||
You can create and manage virtual displays through IPC commands:
|
||||
|
||||
```bash
|
||||
# Create virtual output
|
||||
mmsg dispatch create_virtual_output
|
||||
|
||||
# Destroy all virtual outputs
|
||||
mmsg dispatch destroy_all_virtual_output
|
||||
```
|
||||
|
||||
You can configure virtual monitors using `wlr-randr`:
|
||||
|
||||
```bash
|
||||
# Show all monitors
|
||||
wlr-randr
|
||||
|
||||
# Configure virtual monitor
|
||||
wlr-randr --output HEADLESS-1 --pos 1921,0 --scale 1 --custom-mode 1920x1080@60Hz
|
||||
```
|
||||
|
||||
Virtual monitors can be used for screen sharing with tools like [Sunshine](https://github.com/LizardByte/Sunshine) and [Moonlight](https://github.com/moonlight-stream/moonlight-android), allowing other devices to act as extended monitors.
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
---
|
||||
title: XDG Portals
|
||||
description: Set up screen sharing, clipboard, keyring, and file pickers using XDG portals.
|
||||
---
|
||||
|
||||
## Portal Configuration
|
||||
|
||||
You can customize portal settings via the following paths:
|
||||
|
||||
- **User Configuration (Priority):** `~/.config/xdg-desktop-portal/mango-portals.conf`
|
||||
- **System Fallback:** `/usr/share/xdg-desktop-portal/mango-portals.conf`
|
||||
|
||||
> **Warning:** If you previously added `dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=wlroots` to your config, remove it. Mango now handles this automatically.
|
||||
|
||||
## Screen Sharing
|
||||
|
||||
To enable screen sharing (OBS, Discord, WebRTC), you need `xdg-desktop-portal-wlr`.
|
||||
|
||||
1. **Install Dependencies**
|
||||
|
||||
`pipewire`, `pipewire-pulse`, `xdg-desktop-portal-wlr`
|
||||
|
||||
2. **Optional: Add to autostart**
|
||||
|
||||
In some situations the portal may not start automatically. You can add this to your autostart script to ensure it launches:
|
||||
|
||||
```bash
|
||||
/usr/lib/xdg-desktop-portal-wlr &
|
||||
```
|
||||
|
||||
3. **Restart your computer** to apply changes.
|
||||
|
||||
### Known Issues
|
||||
|
||||
- **Window screen sharing:** Some applications may have issues sharing individual windows. See [#184](https://github.com/mangowm/mango/pull/184) for workarounds.
|
||||
|
||||
- **Screen recording lag:** If you experience stuttering during screen recording, see [xdg-desktop-portal-wlr#351](https://github.com/emersion/xdg-desktop-portal-wlr/issues/351).
|
||||
|
||||
## Clipboard Manager
|
||||
|
||||
Use `cliphist` to manage clipboard history.
|
||||
|
||||
**Dependencies:** `wl-clipboard`, `cliphist`, `wl-clip-persist`
|
||||
|
||||
**Autostart Config:**
|
||||
|
||||
```bash
|
||||
# Keep clipboard content after app closes
|
||||
wl-clip-persist --clipboard regular --reconnect-tries 0 &
|
||||
|
||||
# Watch clipboard and store history
|
||||
wl-paste --type text --watch cliphist store &
|
||||
```
|
||||
|
||||
## GNOME Keyring
|
||||
|
||||
If you need to store passwords or secrets (e.g., for VS Code or Minecraft launchers), install `gnome-keyring`.
|
||||
|
||||
**Configuration:**
|
||||
|
||||
Add the following to `~/.config/xdg-desktop-portal/mango-portals.conf`:
|
||||
|
||||
```ini
|
||||
[preferred]
|
||||
default=gtk
|
||||
org.freedesktop.impl.portal.ScreenCast=wlr
|
||||
org.freedesktop.impl.portal.Screenshot=wlr
|
||||
org.freedesktop.impl.portal.Secret=gnome-keyring
|
||||
org.freedesktop.impl.portal.Inhibit=none
|
||||
```
|
||||
|
||||
## File Picker (File Selector)
|
||||
|
||||
**Dependencies:** `xdg-desktop-portal`, `xdg-desktop-portal-gtk`
|
||||
|
||||
Reboot your computer once to apply.
|
||||
100
docs/faq.md
100
docs/faq.md
|
|
@ -1,100 +0,0 @@
|
|||
---
|
||||
title: FAQ
|
||||
description: Frequently asked questions and troubleshooting.
|
||||
---
|
||||
|
||||
### How do I arrange tiled windows with my mouse?
|
||||
|
||||
You can enable the `drag_tile_to_tile` option in your config. This allows you to drag a tiled window onto another to swap them.
|
||||
|
||||
```ini
|
||||
drag_tile_to_tile=1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Why is my background blurry or why does blur look wrong?
|
||||
|
||||
Blur applies to the transparent areas of windows. To disable it entirely, set `blur=0`.
|
||||
|
||||
If you are experiencing **performance issues with blur**, make sure `blur_optimized=1` (the default). This caches the wallpaper as the blur background, which is much cheaper on the GPU:
|
||||
|
||||
```ini
|
||||
blur_optimized=1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Blur shows my wallpaper instead of the real background content
|
||||
|
||||
This is expected behavior when `blur_optimized=1` (the default). The optimizer caches the wallpaper to reduce GPU load — windows will blur against the wallpaper rather than the actual content stacked beneath them.
|
||||
|
||||
If you want blur to composite against the true background (i.e., show whatever is actually behind the window), set:
|
||||
|
||||
```ini
|
||||
blur_optimized=0
|
||||
```
|
||||
|
||||
> **Warning:** Disabling `blur_optimized` significantly increases GPU consumption and may cause rendering lag, especially on lower-end hardware.
|
||||
|
||||
---
|
||||
|
||||
### My games are lagging or stuttering
|
||||
|
||||
Try enabling **SyncObj** timeline support.
|
||||
|
||||
```ini
|
||||
syncobj_enable=1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### My games have high input latency
|
||||
|
||||
You can enable **Tearing** (similar to VSync off).
|
||||
|
||||
First, enable it globally:
|
||||
|
||||
```ini
|
||||
allow_tearing=1
|
||||
```
|
||||
|
||||
Then force it for your specific game:
|
||||
|
||||
```ini
|
||||
windowrule=force_tearing:1,title:Counter-Strike 2
|
||||
```
|
||||
|
||||
> **Warning:** Some graphics cards require setting `WLR_DRM_NO_ATOMIC=1` before mango starts for tearing to work. Add it to `/etc/environment` and reboot, or launch mango with `WLR_DRM_NO_ATOMIC=1 mango`. See [Monitors — Tearing](/docs/configuration/monitors#tearing-game-mode) for details.
|
||||
|
||||
---
|
||||
|
||||
### How do I use pipes `|` in spawn commands?
|
||||
|
||||
The standard `spawn` command does not support shell pipes directly. You must use `spawn_shell` instead.
|
||||
|
||||
```ini
|
||||
bind=SUPER,P,spawn_shell,echo "hello" | rofi -dmenu
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Certain key combinations do not work on my keyboard layout.
|
||||
|
||||
`bind` automatically converts keysym to keycode, which is compatible with most layouts but can sometimes be imprecise. If a key combination is not triggering, use the **keycode** directly instead of the key name.
|
||||
|
||||
Run `wev` and press the key to find its keycode, then use it in your bind:
|
||||
|
||||
```ini
|
||||
# Instead of:
|
||||
bind=ALT,q,killclient
|
||||
|
||||
# Use the keycode (e.g., code:24 = q on most layouts):
|
||||
bind=ALT,code:24,killclient
|
||||
```
|
||||
|
||||
You can also use `binds` (the `s` flag) to match by keysym instead of keycode:
|
||||
|
||||
```ini
|
||||
binds=ALT,q,killclient
|
||||
```
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
---
|
||||
title: Introduction
|
||||
description: A lightweight and feature-rich Wayland compositor based on dwl.
|
||||
---
|
||||
|
||||
|
||||
**mango** is a Wayland compositor based on [dwl](https://codeberg.org/dwl/dwl/). It aims to be as lightweight as `dwl` and can be built completely within a few seconds, without compromising on functionality.
|
||||
|
||||
> **Philosophy:** **Lightweight & Fast**: mango is designed to be minimal yet functional. It compiles in seconds and offers a robust set of features out of the box.
|
||||
|
||||
## Feature Highlights
|
||||
|
||||
Beyond basic window management, mangowm provides a rich set of features designed for a modern Wayland experience.
|
||||
|
||||
- **[Animations](/docs/visuals/animations)** — Smooth, customizable animations for opening, moving, closing windows and tag switching.
|
||||
- **[Layouts](/docs/window-management/layouts)** — Supports Scroller, Master-Stack, Monocle, Grid, Deck, and more, with per-tag layouts.
|
||||
- **[Visual Effects](/docs/visuals/effects)** — Built-in blur, shadows, corner radius, and opacity effects powered by scenefx.
|
||||
- **[IPC & Scripting](/docs/ipc)** — Control the compositor externally with robust IPC support for custom scripts and widgets.
|
||||
|
||||
## Additional Features
|
||||
|
||||
- **XWayland Support** — Excellent compatibility for legacy X11 applications.
|
||||
- **Tag System** — Uses tags instead of workspaces, allowing separate window layouts for each tag.
|
||||
- **Input Methods** — Great support for text input v2/v3 (Fcitx5, IBus).
|
||||
- **Window States** — Rich states including swallow, minimize, maximize, fullscreen, and overlay.
|
||||
- **Hot-Reload Config** — Simple external configuration that supports hot-reloading without restarting.
|
||||
- **Scratchpads** — Support for both Sway-like and named scratchpads.
|
||||
|
||||
## Community
|
||||
|
||||
- **[Join the mangowm Discord](https://discord.gg/CPjbDxesh5)** — Chat with the community, get support, share your setup, and stay updated with the latest mangowm news.
|
||||
- **[Join the GitHub Discussions](https://github.com/mangowm/mango/discussions)** — Ask questions, request features, report issues, or share ideas directly with contributors and other users.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
This project is built upon the hard work of several open-source projects:
|
||||
|
||||
- **[wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)** — Implementation of the Wayland protocol.
|
||||
- **[mwc](https://github.com/nikoloc/mwc)** — Basal window animation reference.
|
||||
- **[dwl](https://codeberg.org/dwl/dwl)** — Basal dwl features.
|
||||
- **[sway](https://github.com/swaywm/sway)** — Sample implementation of the Wayland protocol.
|
||||
- **[scenefx](https://github.com/wlrfx/scenefx)** — Library to simplify adding window effects.
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
---
|
||||
title: Installation
|
||||
description: Install mangowm on AerynOS, Arch, Fedora, Gentoo, Guix System, NixOS, PikaOS, or build from source.
|
||||
---
|
||||
|
||||
## Package Installation
|
||||
|
||||
mangowm is available as a pre-built package on several distributions. Choose your distribution below.
|
||||
|
||||
---
|
||||
|
||||
### AerynOS
|
||||
|
||||
mangowm is available in the **AerynOS package repository**.
|
||||
|
||||
You can install it using the `moss` package manager:
|
||||
|
||||
```bash
|
||||
sudo moss install mangowm
|
||||
```
|
||||
* The Default config will be located at `/usr/share/defaults/mango`.
|
||||
|
||||
---
|
||||
|
||||
### Arch Linux
|
||||
|
||||
mangowm is available in the **Arch User Repository (AUR)**.
|
||||
|
||||
You can install it using an AUR helper like `yay` or `paru`:
|
||||
|
||||
```bash
|
||||
yay -S mangowm-git
|
||||
```
|
||||
|
||||
> **Tip:** This package pulls the latest git version, ensuring you have the newest features and fixes.
|
||||
|
||||
---
|
||||
|
||||
### Fedora
|
||||
|
||||
The package is in the third-party **Terra repository**. First, add the Terra Repository.
|
||||
|
||||
> **Warning:** Both commands require root privileges. Use `sudo` if needed.
|
||||
|
||||
```bash
|
||||
dnf install --nogpgcheck --repofrompath 'terra,https://repos.fyralabs.com/terra$releasever' terra-release
|
||||
```
|
||||
|
||||
Then, install the package:
|
||||
|
||||
```bash
|
||||
dnf install mangowm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Gentoo
|
||||
|
||||
The package is hosted in the community-maintained **GURU** repository.
|
||||
|
||||
1. **Add the GURU repository**
|
||||
|
||||
```bash
|
||||
emerge --ask --verbose eselect-repository
|
||||
eselect repository enable guru
|
||||
emerge --sync guru
|
||||
```
|
||||
|
||||
2. **Unmask packages**
|
||||
Add the required packages to your `package.accept_keywords` file:
|
||||
- `gui-libs/scenefx`
|
||||
- `gui-wm/mangowm`
|
||||
|
||||
3. **Install mango**
|
||||
```bash
|
||||
emerge --ask --verbose gui-wm/mangowm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Guix System
|
||||
|
||||
The package definition is described in the source repository.
|
||||
|
||||
1. **Add mango channel**
|
||||
Add to `$HOME/.config/guix/channels.scm`:
|
||||
|
||||
```scheme
|
||||
(cons (channel
|
||||
(name 'mangowm)
|
||||
(url "https://github.com/mangowm/mango.git")
|
||||
(branch "main"))
|
||||
%default-channels)
|
||||
```
|
||||
|
||||
2. **Install**
|
||||
After running `guix pull`, you can install mangowm:
|
||||
|
||||
```bash
|
||||
guix install mangowm
|
||||
```
|
||||
|
||||
Or add it to your system configuration using the mangowm module:
|
||||
|
||||
```scheme
|
||||
(use-modules (mangowm))
|
||||
|
||||
(packages (cons*
|
||||
mangowm-git
|
||||
... ;; Other packages
|
||||
%base-packages))
|
||||
```
|
||||
|
||||
> **Tip:** For more information, see the [Guix System documentation](https://guix.gnu.org/manual/devel/en/html_node/Channels.html).
|
||||
|
||||
---
|
||||
|
||||
### NixOS
|
||||
|
||||
The repository provides a Flake with a NixOS module.
|
||||
|
||||
1. **Add flake input**
|
||||
|
||||
```nix
|
||||
# flake.nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
mangowm = {
|
||||
url = "github:mangowm/mango";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
# other inputs ...
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
2. **Import the NixOS module**
|
||||
|
||||
**Option A — Import in `configuration.nix`:**
|
||||
|
||||
```nix
|
||||
# configuration.nix (or any other file that you import)
|
||||
{inputs, ...}: {
|
||||
imports = [
|
||||
inputs.mangowm.nixosModules.mango
|
||||
# .. other imports ...
|
||||
];
|
||||
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
**Option B — Import directly in flake:**
|
||||
|
||||
```nix
|
||||
# flake.nix
|
||||
{
|
||||
# ...
|
||||
|
||||
outputs = { self, nixpkgs, mangowm, ...}@inputs: let
|
||||
inherit (nixpkgs) lib;
|
||||
# ...
|
||||
in {
|
||||
nixosConfigurations.YourHostName = lib.nixosSystem {
|
||||
modules = [
|
||||
mangowm.nixosModules.mango # or inputs.mangowm.nixosModules.mango
|
||||
# other imports ...
|
||||
];
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Enable the module**
|
||||
|
||||
```nix
|
||||
# configuration.nix (or any other file that you import)
|
||||
{
|
||||
programs.mango.enable = true;
|
||||
}
|
||||
```
|
||||
|
||||
4. **Start mango on login**
|
||||
|
||||
The following are common examples. Refer to the official NixOS documentation for full configuration options.
|
||||
|
||||
**Option A — greetd:** Autologin on first start; login screen after logout.
|
||||
|
||||
```nix
|
||||
services.greetd = {
|
||||
enable = true;
|
||||
settings = {
|
||||
initial_session = {
|
||||
command = "mango";
|
||||
user = "your-username"; # auto-login on first start, no password required
|
||||
};
|
||||
default_session = {
|
||||
command = "${pkgs.greetd.tuigreet}/bin/tuigreet --cmd mango";
|
||||
user = "greeter";
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
**Option B — Display manager autologin:** Autologin via an existing display manager (e.g. SDDM, GDM). [`addLoginEntry`](/docs/nix-options#addloginentry) (default: `true`) automatically registers mango as a session.
|
||||
|
||||
```nix
|
||||
services.displayManager = {
|
||||
defaultSession = "mango"; # derived from mango.desktop filename
|
||||
autoLogin = {
|
||||
enable = true;
|
||||
user = "your-username";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
**Option C — getty autologin:** No login screen, boots directly into mango on TTY1.
|
||||
|
||||
For bash/zsh:
|
||||
|
||||
```nix
|
||||
services.getty.autologinUser = "your-username";
|
||||
|
||||
environment.loginShellInit = ''
|
||||
[ "$(tty)" = /dev/tty1 ] && exec mango
|
||||
'';
|
||||
```
|
||||
|
||||
For fish:
|
||||
|
||||
```nix
|
||||
services.getty.autologinUser = "your-username";
|
||||
|
||||
programs.fish.loginShellInit = ''
|
||||
if test (tty) = /dev/tty1
|
||||
exec mango
|
||||
end
|
||||
'';
|
||||
```
|
||||
|
||||
5. **All available options**
|
||||
|
||||
See [Nix Module Options](/docs/nix-options) for the full list of NixOS and Home Manager options.
|
||||
|
||||
---
|
||||
|
||||
### PikaOS
|
||||
|
||||
mangowm is available in the **PikaOS package repository**.
|
||||
|
||||
You can install it using the `pikman` package manager:
|
||||
|
||||
```bash
|
||||
pikman install mangowm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building from Source
|
||||
|
||||
If your distribution isn't listed above, or you want the latest unreleased changes, you can build mangowm from source.
|
||||
|
||||
> **Info:** Ensure the following dependencies are installed before proceeding:
|
||||
>
|
||||
> - `wayland`
|
||||
> - `wayland-protocols`
|
||||
> - `libinput`
|
||||
> - `libdrm`
|
||||
> - `libxkbcommon`
|
||||
> - `pixman`
|
||||
> - `libdisplay-info`
|
||||
> - `libliftoff`
|
||||
> - `hwdata`
|
||||
> - `seatd`
|
||||
> - `pcre2`
|
||||
> - `cjson`
|
||||
> - `pixman`
|
||||
> - `xorg-xwayland`
|
||||
> - `libxcb`
|
||||
|
||||
You will need to build `wlroots` and `scenefx` manually as well.
|
||||
|
||||
1. **Build wlroots**
|
||||
Clone and install the specific version required (check README for latest version).
|
||||
|
||||
```bash
|
||||
git clone -b 0.19.3 https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||
cd wlroots
|
||||
meson build -Dprefix=/usr
|
||||
sudo ninja -C build install
|
||||
```
|
||||
|
||||
2. **Build scenefx**
|
||||
This library handles the visual effects.
|
||||
|
||||
```bash
|
||||
git clone -b 0.4.1 https://github.com/wlrfx/scenefx.git
|
||||
cd scenefx
|
||||
meson build -Dprefix=/usr
|
||||
sudo ninja -C build install
|
||||
```
|
||||
|
||||
3. **Build mangowm**
|
||||
Finally, compile the compositor itself.
|
||||
```bash
|
||||
git clone https://github.com/mangowm/mango.git
|
||||
cd mango
|
||||
meson build -Dprefix=/usr
|
||||
sudo ninja -C build install
|
||||
```
|
||||
78
docs/ipc.md
78
docs/ipc.md
|
|
@ -1,78 +0,0 @@
|
|||
---
|
||||
title: IPC
|
||||
description: Control mangowm programmatically using mmsg.
|
||||
---
|
||||
|
||||
# mmsg(1) - User Manual
|
||||
|
||||
`mmsg` is the command-line interface for the Mango compositor's Inter-Process Communication (IPC) system. It allows users and scripts to query the state of the compositor or subscribe to real-time events.
|
||||
|
||||
## SYNOPSIS
|
||||
`mmsg <command> [arguments...]`
|
||||
|
||||
## DESCRIPTION
|
||||
`mmsg` acts as a client that connects to the Mango compositor via a Unix domain socket defined by the `MANGO_INSTANCE_SIGNATURE` environment variable. It supports two primary modes of operation:
|
||||
1. **One-shot Request (`get`)**: Sends a query to the compositor, receives a single JSON response, and terminates.
|
||||
2. **Persistent Stream (`watch`)**: Subscribes to a specific state, receiving continuous JSON updates whenever that state changes.
|
||||
|
||||
## ENVIRONMENT VARIABLES
|
||||
* **`MANGO_INSTANCE_SIGNATURE`**: Must be set to the path of the Unix socket created by the running Mango instance. This is typically handled automatically when running `mmsg` from within a terminal spawned by the compositor.
|
||||
|
||||
## COMMANDS
|
||||
|
||||
### GET (One-Shot Queries)
|
||||
| Command | Description |
|
||||
| :--- | :--- |
|
||||
| `get version` | Returns the current version of the compositor. |
|
||||
| `get keymode` | Returns the current active keyboard mode (e.g., normal, insert). |
|
||||
| `get keyboardlayout` | Returns the active XKB layout (abbreviated). |
|
||||
| `get monitor <name>` | Returns full JSON details for a specific monitor. |
|
||||
| `get focusing-client` | Returns full JSON details for the client currently in focus. |
|
||||
| `get client <id>` | Returns full JSON details for a client with the given ID. |
|
||||
| `get tag <mon> <idx>` | Queries status of a specific tag on a monitor. |
|
||||
| `get tags <mon>` | Returns a JSON object containing the status of all tags on a monitor. |
|
||||
| `get all-clients` | Returns a JSON array of all active clients. |
|
||||
| `get all-monitors` | Returns a JSON array of all connected monitors. |
|
||||
| `get all-tags` | Returns a JSON object containing the status of all tags. |
|
||||
| `get last_open_surface [<mon>]` | Returns the last focused surface name for a monitor,if the mon not set, it will get current monitor. |
|
||||
|
||||
*Example:*
|
||||
```bash
|
||||
mmsg get monitor eDP-1
|
||||
mmsg get all-clients
|
||||
mmsg get all-monitors
|
||||
```
|
||||
|
||||
### WATCH (Event Subscription)
|
||||
Subscribes the client to real-time updates. When the state changes, the server pushes a new JSON object to the output stream.
|
||||
|
||||
* `watch monitor <name>`
|
||||
* `watch focusing-client`
|
||||
* `watch client <id>`
|
||||
* `watch tags <mon_name>`
|
||||
* `watch all-monitors`
|
||||
* `watch all-tags`
|
||||
* `watch all-clients`
|
||||
* `watch keymode`
|
||||
* `watch keyboardlayout`
|
||||
* `watch last_open_surface [<mon_name>]`
|
||||
|
||||
*Example:*
|
||||
```bash
|
||||
# watch all monitors
|
||||
mmsg watch all-monitors
|
||||
# watch all tags
|
||||
mmsg watch all-tags
|
||||
```
|
||||
|
||||
### DISPATCH
|
||||
Allows sending commands to the compositor to alter its state.
|
||||
* `dispatch <func_name>,[args...] [client,<id>]`
|
||||
|
||||
*Example:*
|
||||
```bash
|
||||
# operate specific client by id
|
||||
mmsg dispatch exchange_client,left client,375
|
||||
# operate current client
|
||||
mmsg dispatch exchange_client,left
|
||||
````
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"title": "git (latest)",
|
||||
"description": "Latest development",
|
||||
"root": true,
|
||||
"pages": [
|
||||
"---Getting Started---",
|
||||
"index",
|
||||
"installation",
|
||||
"quick-start",
|
||||
"---Configuration---",
|
||||
"configuration",
|
||||
"visuals",
|
||||
"window-management",
|
||||
"bindings",
|
||||
"---Examples---",
|
||||
"screenshot",
|
||||
"---Reference---",
|
||||
"nix-options",
|
||||
"ipc",
|
||||
"faq"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,519 +0,0 @@
|
|||
---
|
||||
title: Nix Module Options
|
||||
description: NixOS and Home Manager configuration options for mangowm.
|
||||
---
|
||||
|
||||
> **Note:** This document is automatically generated from the Nix module source code.
|
||||
|
||||
## NixOS
|
||||
|
||||
**System-level options via `programs.mango`.**
|
||||
|
||||
### enable
|
||||
|
||||
|
||||
|
||||
Whether to enable mango, a wayland compositor based on dwl\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
boolean
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
false
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
true
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/nixos-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/nixos-modules.nix)
|
||||
|
||||
|
||||
|
||||
### package
|
||||
|
||||
|
||||
|
||||
The mango package to use
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
package
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
<derivation mango-nightly>
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/nixos-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/nixos-modules.nix)
|
||||
|
||||
|
||||
|
||||
### addLoginEntry
|
||||
|
||||
|
||||
|
||||
Whether to add a login entry to the display manager for mango\. Only has effect if a display manager is configured (e\.g\. SDDM, GDM via ` services.displayManager `)\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
boolean
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
true
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/nixos-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/nixos-modules.nix)
|
||||
|
||||
## Home Manager
|
||||
|
||||
**Configure mangowm declaratively via `wayland.windowManager.mango`.**
|
||||
|
||||
### enable
|
||||
|
||||
|
||||
|
||||
Whether to enable mangowm, a Wayland compositor based on dwl\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
boolean
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
false
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### package
|
||||
|
||||
|
||||
|
||||
The mango package to use
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
package
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
<derivation mango-nightly>
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### autostart_sh
|
||||
|
||||
|
||||
|
||||
Shell script to run on mango startup\. No shebang needed\.
|
||||
|
||||
When this option is set, the script will be written to
|
||||
` ~/.config/mango/autostart.sh ` and an ` exec-once ` line
|
||||
will be automatically added to the config to execute it\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
strings concatenated with “\\n”
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
""
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
''
|
||||
waybar &
|
||||
dunst &
|
||||
''
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### bottomPrefixes
|
||||
|
||||
|
||||
|
||||
List of prefixes for attributes that should appear at the bottom of the config file\.
|
||||
Attributes starting with these prefixes will be sorted to the end\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
list of string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
[ ]
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
[
|
||||
"source"
|
||||
]
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### extraConfig
|
||||
|
||||
|
||||
|
||||
Extra configuration lines to add to ` ~/.config/mango/config.conf `\.
|
||||
This is useful for advanced configurations that don’t fit the structured
|
||||
settings format, or for options that aren’t yet supported by the module\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
strings concatenated with “\\n”
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
""
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
''
|
||||
# Advanced config that doesn't fit structured format
|
||||
special_option = 1
|
||||
''
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### settings
|
||||
|
||||
|
||||
|
||||
Mango configuration written in Nix\. Entries with the same key
|
||||
should be written as lists\. Variables and colors names should be
|
||||
quoted\. See [https://mangowm\.github\.io/docs](https://mangowm\.github\.io/docs) for more examples\.
|
||||
|
||||
**Note:** This option uses a structured format that is converted to Mango’s
|
||||
configuration syntax\. Nested attributes are flattened with underscore separators\.
|
||||
For example: ` animation.duration_open = 400 ` becomes ` animation_duration_open = 400 `
|
||||
|
||||
Keymodes (submaps) are supported via the special ` keymode ` attribute\. Each keymode
|
||||
is a nested attribute set under ` keymode ` that contains its own bindings\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
Mango configuration value
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
{ }
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
{
|
||||
# Window effects
|
||||
blur = 1;
|
||||
blur_optimized = 1;
|
||||
blur_params = {
|
||||
radius = 5;
|
||||
num_passes = 2;
|
||||
};
|
||||
border_radius = 6;
|
||||
focused_opacity = 1.0;
|
||||
|
||||
# Animations - use underscores for multi-part keys
|
||||
animations = 1;
|
||||
animation_type_open = "slide";
|
||||
animation_type_close = "slide";
|
||||
animation_duration_open = 400;
|
||||
animation_duration_close = 800;
|
||||
|
||||
# Or use nested attrs (will be flattened with underscores)
|
||||
animation_curve = {
|
||||
open = "0.46,1.0,0.29,1";
|
||||
close = "0.08,0.92,0,1";
|
||||
};
|
||||
|
||||
# Use lists for duplicate keys like bind and tagrule
|
||||
bind = [
|
||||
"SUPER,r,reload_config"
|
||||
"Alt,space,spawn,rofi -show drun"
|
||||
"Alt,Return,spawn,foot"
|
||||
"ALT,R,setkeymode,resize" # Enter resize mode
|
||||
];
|
||||
|
||||
tagrule = [
|
||||
"id:1,layout_name:tile"
|
||||
"id:2,layout_name:scroller"
|
||||
];
|
||||
|
||||
# Keymodes (submaps) for modal keybindings
|
||||
keymode = {
|
||||
resize = {
|
||||
bind = [
|
||||
"NONE,Left,resizewin,-10,0"
|
||||
"NONE,Escape,setkeymode,default"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### systemd\.enable
|
||||
|
||||
|
||||
|
||||
Whether to enable ` mango-session.target ` on
|
||||
mango startup\. This links to
|
||||
` graphical-session.target `\.
|
||||
Some important environment variables will be imported to systemd
|
||||
and dbus user environment before reaching the target, including
|
||||
|
||||
- ` DISPLAY `
|
||||
- ` WAYLAND_DISPLAY `
|
||||
- ` XDG_CURRENT_DESKTOP `
|
||||
- ` XDG_SESSION_TYPE `
|
||||
- ` NIXOS_OZONE_WL `
|
||||
You can extend this list using the ` systemd.variables ` option\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
boolean
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
true
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
false
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### systemd\.extraCommands
|
||||
|
||||
|
||||
|
||||
Extra commands to run after D-Bus activation\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
list of string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
[
|
||||
"systemctl --user reset-failed"
|
||||
"systemctl --user start mango-session.target"
|
||||
]
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### systemd\.variables
|
||||
|
||||
|
||||
|
||||
Environment variables imported into the systemd and D-Bus user environment\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
list of string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
[
|
||||
"DISPLAY"
|
||||
"WAYLAND_DISPLAY"
|
||||
"XDG_CURRENT_DESKTOP"
|
||||
"XDG_SESSION_TYPE"
|
||||
"NIXOS_OZONE_WL"
|
||||
"XCURSOR_THEME"
|
||||
"XCURSOR_SIZE"
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
[
|
||||
"--all"
|
||||
]
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### systemd\.xdgAutostart
|
||||
|
||||
|
||||
|
||||
Whether to enable autostart of applications using
|
||||
` systemd-xdg-autostart-generator(8) `
|
||||
\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
boolean
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
false
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
true
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
||||
|
||||
### topPrefixes
|
||||
|
||||
|
||||
|
||||
List of prefixes for attributes that should appear at the top of the config file\.
|
||||
Attributes starting with these prefixes will be sorted to the beginning\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
list of string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```nix
|
||||
[ ]
|
||||
```
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```nix
|
||||
[
|
||||
"source"
|
||||
]
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [\<mango/nix/hm-modules\.nix>](https://github.com/mangowm/mango/blob/main/nix/hm-modules.nix)
|
||||
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
---
|
||||
title: Quick Start
|
||||
description: Basic configuration and first steps with mangowm.
|
||||
---
|
||||
|
||||
Now that you have mangowm installed, let's get your environment set up.
|
||||
|
||||
## Initial Setup
|
||||
|
||||
1. **Create Configuration Directory**
|
||||
|
||||
mangowm looks for configuration files in `~/.config/mango/`.
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/mango
|
||||
```
|
||||
|
||||
2. **Copy Default Config**
|
||||
|
||||
A default configuration file is provided at `/etc/mango/config.conf`. Copy it to your local directory to start customizing.
|
||||
|
||||
```bash
|
||||
cp /etc/mango/config.conf ~/.config/mango/config.conf
|
||||
```
|
||||
|
||||
3. **Launch mangowm**
|
||||
|
||||
You can now start the compositor from your TTY.
|
||||
|
||||
```bash
|
||||
mango
|
||||
```
|
||||
|
||||
Optional: To specify a custom config file path:
|
||||
|
||||
```bash
|
||||
mango -c /path/to/your/config.conf
|
||||
```
|
||||
|
||||
## Essential Keybindings
|
||||
|
||||
mangowm uses the following keybinds by default:
|
||||
|
||||
| Key Combination | Action |
|
||||
| :--- | :--- |
|
||||
| `Alt` + `Return` | Open Terminal (defaults to `foot`) |
|
||||
| `Alt` + `Space` | Open Launcher (defaults to `rofi`) |
|
||||
| `Alt` + `Q` | Close (Kill) the active window |
|
||||
| `Super` + `M` | Quit mangowm |
|
||||
| `Super` + `F` | Toggle Fullscreen |
|
||||
| `Alt` + `Arrow Keys` | Move focus (Left, Right, Up, Down) |
|
||||
| `Ctrl` + `1-9` | Switch to Tag 1-9 |
|
||||
| `Alt` + `1-9` | Move window to Tag 1-9 |
|
||||
|
||||
> **Warning:** Some default bindings rely on specific tools like `foot` (terminal) and `rofi` (launcher). Ensure you have them installed or update your `config.conf` to use your preferred alternatives.
|
||||
|
||||
## Recommended Tools
|
||||
|
||||
To get a fully functional desktop experience, we recommend installing the following components:
|
||||
|
||||
| Category | Recommended Tools |
|
||||
| :--- | :--- |
|
||||
| Application Launcher | rofi, bemenu, wmenu, fuzzel |
|
||||
| Terminal Emulator | foot, wezterm, alacritty, kitty, ghostty |
|
||||
| Status Bar | waybar, eww, quickshell, ags |
|
||||
| Desktop Shell | Noctalia, DankMaterialShell |
|
||||
| Wallpaper Setup | awww(swww), swaybg |
|
||||
| Notification Daemon | swaync, dunst, mako |
|
||||
| Desktop Portal | xdg-desktop-portal, xdg-desktop-portal-wlr, xdg-desktop-portal-gtk |
|
||||
| Clipboard | wl-clipboard, wl-clip-persist, cliphist |
|
||||
| Gamma Control / Night Light | wlsunset, gammastep |
|
||||
| Miscellaneous | xfce-polkit, wlogout |
|
||||
|
||||
## Example Configuration
|
||||
|
||||
Check out the [example configuration](https://github.com/DreamMaoMao/mango-config) by the creator of mangowm, including complete setups for mangowm, Waybar, Rofi, and more.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you are up and running, dive deeper into customizing mangowm:
|
||||
|
||||
- [Configure Monitors](/docs/configuration/monitors) — Set up resolution, scaling, and multi-monitor layouts.
|
||||
- [Window Rules](/docs/window-management/rules#window-rules) — Define how specific applications should behave.
|
||||
- [Appearance](/docs/visuals/theming) — Customize colors, borders, gaps, and effects.
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
---
|
||||
|
||||
title: Screenshots
|
||||
description: Example screenshot keybindings and capture workflows for mangowm.
|
||||
|
||||
---
|
||||
|
||||
mangowm does not include a built-in screenshot tool. This keeps the compositor lean.
|
||||
Instead, compose your own workflow from small Wayland utilities and bind them to keys;
|
||||
|
||||
| Tool | Purpose |
|
||||
| :--- | :--- |
|
||||
| [`grim`](https://github.com/emersion/grim) | Capture the screen or a region to a file |
|
||||
| [`slurp`](https://github.com/emersion/slurp) | Interactively select a region for `grim` |
|
||||
| [`wl-copy`](https://github.com/bugaevc/wl-clipboard) | Copy screenshots directly to the clipboard |
|
||||
| [`satty`](https://github.com/gabm/Satty) | Annotate screenshots before saving |
|
||||
| [`wayfreeze`](https://github.com/Jappie3/wayfreeze) | Freeze the screen before capture |
|
||||
|
||||
Install the required with your package manager or from source.
|
||||
|
||||
`grim` writes to the file path you give it, but **will not create missing directories**. Create one first:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/Pictures/Screenshots
|
||||
```
|
||||
|
||||
Any directory works. `~/Pictures/Screenshots/` is just a convention.
|
||||
|
||||
## Quick Binds
|
||||
|
||||
Short, single-step commands can be placed directly in `config.conf` with `spawn_shell`.
|
||||
No script file needed.
|
||||
|
||||
### Fullscreen
|
||||
|
||||
Captures the entire display.
|
||||
|
||||
```ini
|
||||
bind=NONE,Print,spawn_shell,grim $HOME/Pictures/Screenshots/$(date +%Y%m%d%H%M%S).png
|
||||
```
|
||||
|
||||
### Region
|
||||
|
||||
Select an area with `slurp` before capturing.
|
||||
|
||||
```ini
|
||||
bind=SHIFT,Print,spawn_shell,g=$(slurp -d) && [ -n "$g" ] && grim -g "$g" $HOME/Pictures/Screenshots/$(date +%Y%m%d%H%M%S).png
|
||||
```
|
||||
|
||||
### Pointer
|
||||
|
||||
Captures the full screen including the cursor.
|
||||
|
||||
```ini
|
||||
bind=ALT,Print,spawn_shell,grim -c $HOME/Pictures/Screenshots/$(date +%Y%m%d%H%M%S).png
|
||||
```
|
||||
|
||||
### Clipboard
|
||||
|
||||
Captures to a temporary file and copies the image to the clipboard; no file is saved.
|
||||
|
||||
```ini
|
||||
bind=CTRL,Print,spawn_shell,f=$(mktemp -t screenshot-XXXXXX.png) && grim "$f" && wl-copy < "$f" && rm -f "$f"
|
||||
```
|
||||
|
||||
### Annotate
|
||||
|
||||
Captures and opens `satty` for drawing before saving.
|
||||
|
||||
```ini
|
||||
bind=SUPER,Print,spawn_shell,f=$HOME/Pictures/Screenshots/$(date +%Y%m%d%H%M%S).png && grim "$f" && satty --filename "$f" --output-filename "$f" --actions-on-enter save-to-file --early-exit
|
||||
```
|
||||
|
||||
## Script Binds
|
||||
|
||||
When a command involves multi-step logic, geometry parsing, FIFOs, or screen freezing,
|
||||
move it into a script and invoke it with `spawn` instead of `spawn_shell`.
|
||||
|
||||
Create the scripts directory first:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/mango/scripts/screenshot
|
||||
```
|
||||
|
||||
### Window
|
||||
|
||||
Uses `mmsg` (ships with mango) to capture the focused window.
|
||||
|
||||
`~/.config/mango/scripts/screenshot/window.sh`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
geometry=$(mmsg get focusing-client | jq -r '"\(.x),\(.y) \(.width)x\(.height)"')
|
||||
[ -z "$geometry" ] && exit 1
|
||||
grim -g "$geometry" "$HOME/Pictures/Screenshots/$(date +%Y%m%d%H%M%S).png"
|
||||
```
|
||||
|
||||
```ini
|
||||
bind=CTRL+SHIFT,Print,spawn,$HOME/.config/mango/scripts/screenshot/window.sh
|
||||
```
|
||||
|
||||
### Freeze
|
||||
|
||||
Freezes the screen with `wayfreeze` before capturing.
|
||||
|
||||
`~/.config/mango/scripts/screenshot/freeze.sh`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
pipe=$(mktemp -u).fifo
|
||||
mkfifo "$pipe"
|
||||
wayfreeze --after-freeze-timeout 100 --after-freeze-cmd "echo > $pipe" &
|
||||
wayfreeze_pid=$!
|
||||
read -r < "$pipe"
|
||||
grim "$HOME/Pictures/Screenshots/$(date +%Y%m%d%H%M%S).png"
|
||||
kill "$wayfreeze_pid" 2>/dev/null
|
||||
rm -f "$pipe"
|
||||
```
|
||||
|
||||
```ini
|
||||
bind=CTRL+SUPER,Print,spawn,$HOME/.config/mango/scripts/screenshot/freeze.sh
|
||||
```
|
||||
|
||||
### Freeze + Region
|
||||
|
||||
Freeze, then select a region with `slurp`. Cleans up on cancel.
|
||||
|
||||
`~/.config/mango/scripts/screenshot/freeze-region.sh`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
pipe=$(mktemp -u).fifo
|
||||
mkfifo "$pipe"
|
||||
wayfreeze --after-freeze-timeout 100 --after-freeze-cmd "echo > $pipe" &
|
||||
wayfreeze_pid=$!
|
||||
read -r < "$pipe"
|
||||
geometry=$(slurp -d)
|
||||
if [[ -z "$geometry" ]]; then
|
||||
kill "$wayfreeze_pid" 2>/dev/null
|
||||
rm -f "$pipe"
|
||||
exit 1
|
||||
fi
|
||||
grim -g "$geometry" "$HOME/Pictures/Screenshots/$(date +%Y%m%d%H%M%S).png"
|
||||
kill "$wayfreeze_pid" 2>/dev/null
|
||||
rm -f "$pipe"
|
||||
```
|
||||
|
||||
```ini
|
||||
bind=SHIFT+SUPER,Print,spawn,$HOME/.config/mango/scripts/screenshot/freeze-region.sh
|
||||
```
|
||||
|
||||
Make all three scripts executable:
|
||||
|
||||
```bash
|
||||
chmod +x ~/.config/mango/scripts/screenshot/*.sh
|
||||
```
|
||||
|
||||
## All-in-One Script
|
||||
|
||||
Prefer fewer files? A single script with subcommands covers every mode above.
|
||||
Place it in the same directory and use it in place of the individual scripts.
|
||||
|
||||
`~/.config/mango/scripts/screenshot/screenshot.sh`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
mkdir -p "$HOME/Pictures/Screenshots"
|
||||
filepath="$HOME/Pictures/Screenshots/$(date +%Y%m%d%H%M%S).png"
|
||||
|
||||
case "${1:-fullscreen}" in
|
||||
region)
|
||||
g=$(slurp -d); [ -z "$g" ] && exit 1
|
||||
grim -g "$g" "$filepath" ;;
|
||||
window)
|
||||
g=$(mmsg get focusing-client | jq -r '"\(.x),\(.y) \(.width)x\(.height)"')
|
||||
[ -z "$g" ] && exit 1
|
||||
grim -g "$g" "$filepath" ;;
|
||||
freeze)
|
||||
p=$(mktemp -u).fifo; mkfifo "$p"
|
||||
wayfreeze --after-freeze-timeout 100 --after-freeze-cmd "echo > $p" & wp=$!
|
||||
read -r < "$p"; grim "$filepath"
|
||||
kill "$wp" 2>/dev/null; rm -f "$p" ;;
|
||||
freeze-region)
|
||||
p=$(mktemp -u).fifo; mkfifo "$p"
|
||||
wayfreeze --after-freeze-timeout 100 --after-freeze-cmd "echo > $p" & wp=$!
|
||||
read -r < "$p"; g=$(slurp -d)
|
||||
if [ -z "$g" ]; then kill "$wp" 2>/dev/null; rm -f "$p"; exit 1; fi
|
||||
grim -g "$g" "$filepath"
|
||||
kill "$wp" 2>/dev/null; rm -f "$p" ;;
|
||||
annotate)
|
||||
grim "$filepath"; satty --filename "$filepath" --output-filename "$filepath" --actions-on-enter save-to-file --early-exit ;;
|
||||
*) grim "$filepath" ;;
|
||||
esac
|
||||
```
|
||||
|
||||
Make the script executable:
|
||||
|
||||
|
||||
```bash
|
||||
chmod +x ~/.config/mango/scripts/screenshot/screenshot.sh
|
||||
```
|
||||
|
||||
Then add the binds to `config.conf`:
|
||||
|
||||
```ini
|
||||
bind=NONE,Print,spawn,$HOME/.config/mango/scripts/screenshot/screenshot.sh fullscreen
|
||||
bind=SHIFT,Print,spawn,$HOME/.config/mango/scripts/screenshot/screenshot.sh region
|
||||
bind=CTRL+SHIFT,Print,spawn,$HOME/.config/mango/scripts/screenshot/screenshot.sh window
|
||||
bind=CTRL+SUPER,Print,spawn,$HOME/.config/mango/scripts/screenshot/screenshot.sh freeze
|
||||
bind=SHIFT+SUPER,Print,spawn,$HOME/.config/mango/scripts/screenshot/screenshot.sh freeze-region
|
||||
bind=SUPER,Print,spawn,$HOME/.config/mango/scripts/screenshot/screenshot.sh annotate
|
||||
```
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
---
|
||||
title: Animations
|
||||
description: Configure smooth transitions for windows and layers.
|
||||
---
|
||||
|
||||
## Enabling Animations
|
||||
|
||||
mangowm supports animations for both standard windows and layer shell surfaces (like bars and notifications).
|
||||
|
||||
```ini
|
||||
animations=1
|
||||
layer_animations=1
|
||||
```
|
||||
|
||||
## Animation Types
|
||||
|
||||
You can define different animation styles for opening and closing windows and layer surfaces.
|
||||
|
||||
Available types: `slide`, `zoom`, `fade`, `none`.
|
||||
|
||||
```ini
|
||||
animation_type_open=zoom
|
||||
animation_type_close=slide
|
||||
layer_animation_type_open=slide
|
||||
layer_animation_type_close=slide
|
||||
```
|
||||
|
||||
## Fade Settings
|
||||
|
||||
Control the fade-in and fade-out effects for animations.
|
||||
|
||||
```ini
|
||||
animation_fade_in=1
|
||||
animation_fade_out=1
|
||||
fadein_begin_opacity=0.5
|
||||
fadeout_begin_opacity=0.5
|
||||
```
|
||||
|
||||
- `animation_fade_in` — Enable fade-in effect (0: disable, 1: enable)
|
||||
- `animation_fade_out` — Enable fade-out effect (0: disable, 1: enable)
|
||||
- `fadein_begin_opacity` — Starting opacity for fade-in animations (0.0–1.0)
|
||||
- `fadeout_begin_opacity` — Starting opacity for fade-out animations (0.0–1.0)
|
||||
|
||||
## Zoom Settings
|
||||
|
||||
Adjust the zoom ratios for zoom animations.
|
||||
|
||||
```ini
|
||||
zoom_initial_ratio=0.4
|
||||
zoom_end_ratio=0.8
|
||||
```
|
||||
|
||||
- `zoom_initial_ratio` — Initial zoom ratio
|
||||
- `zoom_end_ratio` — End zoom ratio
|
||||
|
||||
## Durations
|
||||
|
||||
Control the speed of animations (in milliseconds).
|
||||
|
||||
| Setting | Type | Default | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `animation_duration_move` | integer | `500` | Move animation duration (ms) |
|
||||
| `animation_duration_open` | integer | `400` | Open animation duration (ms) |
|
||||
| `animation_duration_tag` | integer | `300` | Tag animation duration (ms) |
|
||||
| `animation_duration_close` | integer | `300` | Close animation duration (ms) |
|
||||
| `animation_duration_focus` | integer | `0` | Focus change (opacity transition) animation duration (ms) |
|
||||
|
||||
```ini
|
||||
animation_duration_move=500
|
||||
animation_duration_open=400
|
||||
animation_duration_tag=300
|
||||
animation_duration_close=300
|
||||
animation_duration_focus=0
|
||||
```
|
||||
|
||||
## Custom Bezier Curves
|
||||
|
||||
Bezier curves determine the "feel" of an animation (e.g., linear vs. bouncy). The format is `x1,y1,x2,y2`.
|
||||
|
||||
You can visualize and generate curve values using online tools like [cssportal.com](https://www.cssportal.com/css-cubic-bezier-generator/) or [easings.net](https://easings.net).
|
||||
|
||||
| Setting | Type | Default | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `animation_curve_open` | string | `0.46,1.0,0.29,0.99` | Open animation bezier curve |
|
||||
| `animation_curve_move` | string | `0.46,1.0,0.29,0.99` | Move animation bezier curve |
|
||||
| `animation_curve_tag` | string | `0.46,1.0,0.29,0.99` | Tag animation bezier curve |
|
||||
| `animation_curve_close` | string | `0.46,1.0,0.29,0.99` | Close animation bezier curve |
|
||||
| `animation_curve_focus` | string | `0.46,1.0,0.29,0.99` | Focus change (opacity transition) animation bezier curve |
|
||||
| `animation_curve_opafadein` | string | `0.46,1.0,0.29,0.99` | Open opacity animation bezier curve |
|
||||
| `animation_curve_opafadeout` | string | `0.5,0.5,0.5,0.5` | Close opacity animation bezier curve |
|
||||
|
||||
```ini
|
||||
animation_curve_open=0.46,1.0,0.29,0.99
|
||||
animation_curve_move=0.46,1.0,0.29,0.99
|
||||
animation_curve_tag=0.46,1.0,0.29,0.99
|
||||
animation_curve_close=0.46,1.0,0.29,0.99
|
||||
animation_curve_focus=0.46,1.0,0.29,0.99
|
||||
animation_curve_opafadein=0.46,1.0,0.29,0.99
|
||||
animation_curve_opafadeout=0.5,0.5,0.5,0.5
|
||||
```
|
||||
|
||||
## Tag Animation Direction
|
||||
|
||||
Control the direction of tag switch animations.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `tag_animation_direction` | `1` | Tag animation direction (1: horizontal, 0: vertical) |
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
---
|
||||
title: Window Effects
|
||||
description: Add visual polish with blur, shadows, and opacity.
|
||||
---
|
||||
|
||||
## Blur
|
||||
|
||||
Blur creates a frosted glass effect for transparent windows.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `blur` | `0` | Enable blur for windows. |
|
||||
| `blur_layer` | `0` | Enable blur for layer surfaces (like bars/docks). |
|
||||
| `blur_optimized` | `1` | Caches the wallpaper and blur background, significantly reducing GPU usage. Disabling it will significantly increase GPU consumption and may cause rendering lag. **Highly recommended.** |
|
||||
| `blur_params_radius` | `5` | The strength (radius) of the blur. |
|
||||
| `blur_params_num_passes` | `1` | Number of passes. Higher = smoother but more expensive. |
|
||||
| `blur_params_noise` | `0.02` | Blur noise level. |
|
||||
| `blur_params_brightness` | `0.9` | Blur brightness adjustment. |
|
||||
| `blur_params_contrast` | `0.9` | Blur contrast adjustment. |
|
||||
| `blur_params_saturation` | `1.2` | Blur saturation adjustment. |
|
||||
|
||||
> **Warning:** Blur has a relatively high impact on performance. If your hardware is limited, it is not recommended to enable it. If you experience lag with blur on, ensure `blur_optimized=1` — disabling it will significantly increase GPU consumption and may cause rendering lag. To disable blur entirely, set `blur=0`.
|
||||
|
||||
---
|
||||
|
||||
## Shadows
|
||||
|
||||
Drop shadows help distinguish floating windows from the background.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `shadows` | `0` | Enable shadows. |
|
||||
| `layer_shadows` | `0` | Enable shadows for layer surfaces. |
|
||||
| `shadow_only_floating` | `1` | Only draw shadows for floating windows (saves performance). |
|
||||
| `shadows_size` | `10` | Size of the shadow. |
|
||||
| `shadows_blur` | `15` | Shadow blur amount. |
|
||||
| `shadows_position_x` | `0` | Shadow X offset. |
|
||||
| `shadows_position_y` | `0` | Shadow Y offset. |
|
||||
| `shadowscolor` | `0x000000ff` | Color of the shadow. |
|
||||
|
||||
```ini
|
||||
# Example shadows configuration
|
||||
shadows=1
|
||||
layer_shadows=1
|
||||
shadow_only_floating=1
|
||||
shadows_size=12
|
||||
shadows_blur=15
|
||||
shadows_position_x=0
|
||||
shadows_position_y=0
|
||||
shadowscolor=0x000000ff
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Opacity & Corner Radius
|
||||
|
||||
Control the transparency and roundness of your windows.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `border_radius` | `0` | Window corner radius in pixels. |
|
||||
| `border_radius_location_default` | `0` | Corner radius location: `0` (all), `1` (top-left), `2` (top-right), `3` (bottom-left), `4` (bottom-right), `5` (closest corner). |
|
||||
| `no_radius_when_single` | `0` | Disable radius if only one window is visible. |
|
||||
| `focused_opacity` | `1.0` | Opacity for the active window (0.0 - 1.0). |
|
||||
| `unfocused_opacity` | `1.0` | Opacity for inactive windows (0.0 - 1.0). |
|
||||
|
||||
```ini
|
||||
# Window corner radius in pixels
|
||||
border_radius=0
|
||||
|
||||
# Corner radius location (0=all, 1=top-left, 2=top-right, 3=bottom-left, 4=bottom-right)
|
||||
border_radius_location_default=0
|
||||
|
||||
# Disable radius if only one window is visible
|
||||
no_radius_when_single=0
|
||||
|
||||
# Opacity for the active window (0.0 - 1.0)
|
||||
focused_opacity=1.0
|
||||
|
||||
# Opacity for inactive windows
|
||||
unfocused_opacity=1.0
|
||||
```
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
title: Visuals
|
||||
description: Customize borders, colors, effects, and animations.
|
||||
icon: Palette
|
||||
---
|
||||
|
||||
Customize the look of your desktop.
|
||||
|
||||
<Cards>
|
||||
|
||||
<Card href="/docs/visuals/theming" title="Theming" description="Borders, colors, and cursor" />
|
||||
|
||||
<Card href="/docs/visuals/status-bar" title="Status Bar" description="Built-in status bar" />
|
||||
|
||||
<Card href="/docs/visuals/effects" title="Effects" description="Blur, shadows, rounded corners" />
|
||||
|
||||
<Card href="/docs/visuals/animations" title="Animations" description="Window and tag animations" />
|
||||
|
||||
</Cards>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"title": "Visuals",
|
||||
"pages": ["theming", "status-bar", "effects", "animations"]
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
---
|
||||
title: Status Bar
|
||||
description: Configure Waybar for mangowm.
|
||||
---
|
||||
|
||||
## Module Configuration
|
||||
|
||||
mangowm is compatible with Waybar's `ext/workspaces` module (Wayland standard) or the `dwl/tags` module. We recommend `ext/workspaces` for the best experience.
|
||||
|
||||
> **Tip:** You can also use the `dwl/tags` module, but `ext/workspaces` provides better integration with mangowm's features. The `ext/workspaces` module requires **Waybar > 0.14.0**.
|
||||
|
||||
### `config.jsonc`
|
||||
|
||||
Add the following to your Waybar configuration:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"modules-left": [
|
||||
"ext/workspaces",
|
||||
"dwl/window"
|
||||
],
|
||||
"ext/workspaces": {
|
||||
"format": "{icon}",
|
||||
"ignore-hidden": true,
|
||||
"on-click": "activate",
|
||||
"on-click-right": "deactivate",
|
||||
"sort-by-id": true
|
||||
},
|
||||
"dwl/window": {
|
||||
"format": "[{layout}] {title}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
You can style the tags using standard CSS in `style.css`.
|
||||
|
||||
### `style.css`
|
||||
|
||||
```css
|
||||
#workspaces {
|
||||
border-radius: 4px;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-color: #c9b890;
|
||||
margin-left: 4px;
|
||||
padding-left: 10px;
|
||||
padding-right: 6px;
|
||||
background: rgba(40, 40, 40, 0.76);
|
||||
}
|
||||
|
||||
#workspaces button {
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: inherit;
|
||||
text-shadow: inherit;
|
||||
color: #ddca9e;
|
||||
padding: 1px;
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
margin-right: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
#workspaces button.hidden {
|
||||
color: #9e906f;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#workspaces button.visible {
|
||||
color: #ddca9e;
|
||||
}
|
||||
|
||||
#workspaces button:hover {
|
||||
color: #d79921;
|
||||
}
|
||||
|
||||
#workspaces button.active {
|
||||
background-color: #ddca9e;
|
||||
color: #282828;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 0px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#workspaces button.urgent {
|
||||
background-color: #ef5e5e;
|
||||
color: #282828;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 0px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#tags {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#tags button {
|
||||
background-color: #fff;
|
||||
color: #a585cd;
|
||||
}
|
||||
|
||||
#tags button:not(.occupied):not(.focused) {
|
||||
font-size: 0;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin: -17px;
|
||||
padding: 0;
|
||||
color: transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#tags button.occupied {
|
||||
background-color: #fff;
|
||||
color: #cdc885;
|
||||
}
|
||||
|
||||
#tags button.focused {
|
||||
background-color: rgb(186, 142, 213);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#tags button.urgent {
|
||||
background: rgb(171, 101, 101);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#window {
|
||||
background-color: rgb(237, 196, 147);
|
||||
color: rgb(63, 37, 5);
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Configuration Example
|
||||
|
||||
> **Tip:** You can find a complete Waybar configuration for mangowm at [waybar-config](https://github.com/DreamMaoMao/waybar-config).
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
---
|
||||
title: Theming
|
||||
description: Customize the visual appearance of borders, colors, and the cursor.
|
||||
---
|
||||
|
||||
## Dimensions
|
||||
|
||||
Control the sizing of window borders and gaps.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `borderpx` | `4` | Border width in pixels. |
|
||||
| `gappih` | `5` | Horizontal inner gap (between windows). |
|
||||
| `gappiv` | `5` | Vertical inner gap. |
|
||||
| `gappoh` | `10` | Horizontal outer gap (between windows and screen edges). |
|
||||
| `gappov` | `10` | Vertical outer gap. |
|
||||
|
||||
## Colors
|
||||
|
||||
Colors are defined in `0xRRGGBBAA` hex format.
|
||||
|
||||
```ini
|
||||
# Background color of the root window
|
||||
rootcolor=0x323232ff
|
||||
|
||||
# Inactive window border
|
||||
bordercolor=0x444444ff
|
||||
|
||||
# Drop shadow when dragging windows
|
||||
dropcolor=0x8FBA7C55
|
||||
|
||||
# Split window border color in manual dwindle layout
|
||||
splitcolor=0xEB441EFF
|
||||
|
||||
# Active window border
|
||||
focuscolor=0xc66b25ff
|
||||
|
||||
# Urgent window border (alerts)
|
||||
urgentcolor=0xad401fff
|
||||
```
|
||||
|
||||
### State-Specific Colors
|
||||
|
||||
You can also color-code windows based on their state:
|
||||
|
||||
| State | Config Key | Default Color |
|
||||
| :--- | :--- | :--- |
|
||||
| Maximized | `maximizescreencolor` | `0x89aa61ff` |
|
||||
| Scratchpad | `scratchpadcolor` | `0x516c93ff` |
|
||||
| Global | `globalcolor` | `0xb153a7ff` |
|
||||
| Overlay | `overlaycolor` | `0x14a57cff` |
|
||||
|
||||
> **Tip:** For scratchpad window sizing, see [Scratchpad](/docs/window-management/scratchpad) configuration.
|
||||
|
||||
## Cursor Theme
|
||||
|
||||
Set the size and theme of your mouse cursor.
|
||||
|
||||
```ini
|
||||
cursor_size=24
|
||||
cursor_theme=Adwaita
|
||||
```
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
title: Window Management
|
||||
description: Layouts, rules, and window behavior.
|
||||
icon: LayoutGrid
|
||||
---
|
||||
|
||||
Window management with layouts, rules, and scratchpad support.
|
||||
|
||||
<Cards>
|
||||
|
||||
<Card href="/docs/window-management/layouts" title="Layouts" description="Tile, scroller, monocle, grid, deck" />
|
||||
|
||||
<Card href="/docs/window-management/rules" title="Rules" description="Window rules and conditions" />
|
||||
|
||||
<Card href="/docs/window-management/overview" title="Overview" description="Window states and properties" />
|
||||
|
||||
<Card href="/docs/window-management/scratchpad" title="Scratchpad" description="Quick access to applications" />
|
||||
|
||||
</Cards>
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
---
|
||||
title: Layouts
|
||||
description: Configure and switch between different window layouts.
|
||||
---
|
||||
|
||||
## Supported Layouts
|
||||
|
||||
mangowm supports a variety of layouts that can be assigned per tag.
|
||||
|
||||
- `tile`
|
||||
- `scroller`
|
||||
- `monocle`
|
||||
- `grid`
|
||||
- `deck`
|
||||
- `center_tile`
|
||||
- `vertical_tile`
|
||||
- `right_tile`
|
||||
- `vertical_scroller`
|
||||
- `vertical_grid`
|
||||
- `vertical_deck`
|
||||
- `dwindle`
|
||||
- `fair`
|
||||
- `vertical_fair`
|
||||
|
||||
---
|
||||
|
||||
## Scroller Layout
|
||||
|
||||
The Scroller layout positions windows in a scrollable strip, similar to PaperWM.
|
||||
|
||||
### Configuration
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `scroller_structs` | `20` | Width reserved on sides when window ratio is 1. |
|
||||
| `scroller_default_proportion` | `0.9` | Default width proportion for new windows. |
|
||||
| `scroller_focus_center` | `0` | Always center the focused window (1 = enable). |
|
||||
| `scroller_prefer_center` | `0` | Center focused window only if it was outside the view. |
|
||||
| `scroller_prefer_overspread` | `1` | Allow windows to overspread when there's extra space. |
|
||||
| `edge_scroller_pointer_focus` | `1` | Focus windows even if partially off-screen. |
|
||||
| `edge_scroller_focus_allow_speed` | `0.0` | Allow pointer focus to happen if the pointer moves at a speed greater than this value. |
|
||||
| `scroller_proportion_preset` | `0.5,0.8,1.0` | Presets for cycling window widths. |
|
||||
| `scroller_ignore_proportion_single` | `1` | Ignore proportion adjustments for single windows. |
|
||||
| `scroller_default_proportion_single` | `1.0` | Default proportion for single windows in scroller. **Requires `scroller_ignore_proportion_single=0` to take effect.** |
|
||||
|
||||
> **Warning:** `scroller_prefer_overspread`, `scroller_focus_center`, and `scroller_prefer_center` interact with each other. Their priority order is:
|
||||
>
|
||||
> **scroller_prefer_overspread > scroller_focus_center > scroller_prefer_center**
|
||||
>
|
||||
> To ensure a lower-priority setting takes effect, you must set all higher-priority options to `0`.
|
||||
|
||||
```ini
|
||||
# Example scroller configuration
|
||||
scroller_structs=20
|
||||
scroller_default_proportion=0.9
|
||||
scroller_focus_center=0
|
||||
scroller_prefer_center=0
|
||||
scroller_prefer_overspread=1
|
||||
edge_scroller_pointer_focus=1
|
||||
edge_scroller_focus_allow_speed=0.0
|
||||
scroller_default_proportion_single=1.0
|
||||
scroller_proportion_preset=0.5,0.8,1.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Master-Stack Layouts
|
||||
|
||||
These settings apply to layouts like `tile` and `center_tile`.
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `new_is_master` | `1` | New windows become the master window. |
|
||||
| `default_mfact` | `0.55` | The split ratio between master and stack areas. |
|
||||
| `default_nmaster` | `1` | Number of allowed master windows. |
|
||||
| `smartgaps` | `0` | Disable gaps when only one window is present. |
|
||||
| `center_master_overspread` | `0` | (Center Tile) Master spreads across screen if no stack exists. |
|
||||
| `center_when_single_stack` | `1` | (Center Tile) Center master when only one stack window exists. |
|
||||
|
||||
```ini
|
||||
# Example master-stack configuration
|
||||
new_is_master=1
|
||||
smartgaps=0
|
||||
default_mfact=0.55
|
||||
default_nmaster=1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dwindle Layout
|
||||
|
||||
The Dwindle layout arranges windows as a binary tree of recursive splits. Each new window splits the focused window's container, producing a spiral-like tiling.
|
||||
|
||||
### Configuration
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `dwindle_split_ratio` | `0.5` | Ratio used for new splits (`0.05`–`0.95`). |
|
||||
| `dwindle_smart_split` | `0` | Pick the split axis from the cursor's position inside the focused window. The new window appears on the cursor's side. |
|
||||
| `dwindle_hsplit` | `1` | Side-by-side splits: where the new window goes. `0` = follow cursor, `1` = right, `2` = left. |
|
||||
| `dwindle_vsplit` | `1` | Top/bottom splits: where the new window goes. `0` = follow cursor, `1` = below, `2` = above. |
|
||||
| `dwindle_preserve_split` | `0` | Keep the sibling's split orientation when a window is closed. |
|
||||
| `dwindle_smart_resize` | `0` | When dragging to resize, move the split toward the cursor regardless of which side was grabbed. |
|
||||
| `dwindle_drop_simple_split` | `1` | Drag-to-tile drop preview. `1` = 2-zone preview matching `dwindle_split_ratio`, `0` = 4-quadrant preview. |
|
||||
| `dwindle_manual_split` | `0` | Manually split windows mode. |
|
||||
|
||||
```ini
|
||||
# Example dwindle configuration
|
||||
dwindle_split_ratio=0.5
|
||||
dwindle_smart_split=0
|
||||
dwindle_hsplit=0
|
||||
dwindle_vsplit=0
|
||||
dwindle_preserve_split=0
|
||||
dwindle_smart_resize=0
|
||||
dwindle_drop_simple_split=1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Switching Layouts
|
||||
| Setting | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `circle_layout` | - | A comma-separated list of layouts `switch_layout` cycles through,the value sample:`tile,scroller`. |
|
||||
|
||||
You can switch layouts dynamically or set a default for specific tags using [Tag Rules](/docs/window-management/rules#tag-rules).
|
||||
|
||||
**Keybinding Examples:**
|
||||
|
||||
```ini
|
||||
# Cycle through layouts
|
||||
circle_layout=grid,scroller,tile
|
||||
bind=SUPER,n,switch_layout
|
||||
|
||||
# Set specific layout
|
||||
bind=SUPER,t,setlayout,tile
|
||||
bind=SUPER,s,setlayout,scroller
|
||||
```
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"title": "Window Management",
|
||||
"pages": ["layouts", "rules", "overview", "scratchpad"]
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
title: Overview
|
||||
description: Configure the overview mode for window navigation.
|
||||
---
|
||||
|
||||
## Overview Settings
|
||||
|
||||
| Setting | Type | Default | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `hotarea_size` | integer | `10` | Hot area size in pixels. |
|
||||
| `enable_hotarea` | integer | `0` | Enable hot areas (0: disable, 1: enable). |
|
||||
| `hotarea_corner` | integer | `2` | Hot area corner (0: top-left, 1: top-right, 2: bottom-left, 3: bottom-right). |
|
||||
| `ov_tab_mode` | integer | `1` | Overview tab mode (0: disable, 1: enable). |
|
||||
| `overviewgappi` | integer | `5` | Inner gap in overview mode. |
|
||||
| `overviewgappo` | integer | `30` | Outer gap in overview mode. |
|
||||
| `ov_no_resize` | integer | `1` | Disable window resizing in overview mode. |
|
||||
|
||||
### Setting Descriptions
|
||||
|
||||
- `enable_hotarea` — Toggles overview when the cursor enters the configured corner.
|
||||
- `hotarea_size` — Size of the hot area trigger zone in pixels.
|
||||
- `hotarea_corner` — Corner that triggers the hot area (0: top-left, 1: top-right, 2: bottom-left, 3: bottom-right).
|
||||
- `ov_tab_mode` — Circles focus through windows in overview; releasing the mod key exits overview.
|
||||
- `ov_no_resize` — Disables resizing of windows in overview mode(use snap to display).
|
||||
|
||||
### Mouse Interaction in Overview
|
||||
|
||||
When in overview mode:
|
||||
|
||||
- **Left mouse button** — Jump to (focus) a window.
|
||||
- **Right mouse button** — Close a window.
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
---
|
||||
title: Rules
|
||||
description: Define behavior for specific windows, tags, and layers.
|
||||
---
|
||||
|
||||
## Window Rules
|
||||
|
||||
Window rules allow you to set specific properties (floating, opacity, size, animations, etc.) for applications based on their `appid` or `title`. You can set all parameters in one line, and if you both set appid and title, the window will only follow the rules when appid and title both match.
|
||||
|
||||
**Format:**
|
||||
|
||||
```ini
|
||||
windowrule=Parameter:Values,title:Values
|
||||
windowrule=Parameter:Values,Parameter:Values,appid:Values,title:Values
|
||||
```
|
||||
|
||||
### State & Behavior Parameters
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `appid` | string | Any | Match by application ID, supports regex |
|
||||
| `title` | string | Any | Match by window title, supports regex |
|
||||
| `isfloating` | integer | `0` / `1` | Force floating state |
|
||||
| `isfullscreen` | integer | `0` / `1` | Force fullscreen state |
|
||||
| `isfakefullscreen` | integer | `0` / `1` | Force fake-fullscreen state (window stays constrained) |
|
||||
| `isglobal` | integer | `0` / `1` | Open as global window (sticky across tags) |
|
||||
| `isoverlay` | integer | `0` / `1` | Make it always in top layer |
|
||||
| `isopensilent` | integer | `0` / `1` | Open without focus |
|
||||
| `istagsilent` | integer | `0` / `1` | Don't focus if client is not in current view tag |
|
||||
| `force_fakemaximize` | integer | `0` / `1` (default 1) | The state of client set to fake maximized |
|
||||
| `ignore_maximize` | integer | `0` / `1` (default 1) | Don't handle maximize request from client |
|
||||
| `ignore_minimize` | integer | `0` / `1` (default 1) | Don't handle minimize request from client |
|
||||
| `force_tiled_state` | integer | `0` / `1` | Deceive the window into thinking it is tiling, so it better adheres to assigned dimensions |
|
||||
| `noopenmaximized` | integer | `0` / `1` | Window does not open as maximized mode |
|
||||
| `single_scratchpad` | integer | `0` / `1` (default 1) | Only show one out of named scratchpads or the normal scratchpad |
|
||||
| `allow_shortcuts_inhibit` | integer | `0` / `1` (default 1) | Allow shortcuts to be inhibited by clients |
|
||||
| `idleinhibit_when_focus` | integer | `0` / `1` (default 0) | Automatically keep idle inhibit active when this window is focused |
|
||||
|
||||
### Geometry & Position
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `width` | float | 0-9999 | Window width when it becomes a floating window,if the value below 1, it will be the percentage of the screen width,otherwise it will be the pixel value |
|
||||
| `height` | float | 0-9999 | Window height when it becomes a floating window,if the value below 1, it will be the percentage of the screen height,otherwise it will be the pixel value |
|
||||
| `offsetx` | integer | -999-999 | X offset from center (%), 100 is the edge of screen with outer gap |
|
||||
| `offsety` | integer | -999-999 | Y offset from center (%), 100 is the edge of screen with outer gap |
|
||||
| `monitor` | string | Any | Assign to monitor by [monitor spec](/docs/configuration/monitors#monitor-spec-format) (name, make, model, or serial) |
|
||||
| `tags` | integer | 1-9 | Assign to specific tag |
|
||||
| `no_force_center` | integer | `0` / `1` | Window does not force center |
|
||||
| `isnosizehint` | integer | `0` / `1` | Don't use min size and max size for size hints |
|
||||
|
||||
### Visuals & Decoration
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `noblur` | integer | `0` / `1` | Window does not have blur effect |
|
||||
| `isnoborder` | integer | `0` / `1` | Remove window border |
|
||||
| `isnoshadow` | integer | `0` / `1` | Not apply shadow |
|
||||
| `isnoradius` | integer | `0` / `1` | Not apply corner radius |
|
||||
| `isnoanimation` | integer | `0` / `1` | Not apply animation |
|
||||
| `focused_opacity` | integer | `0` / `1` | Window focused opacity |
|
||||
| `unfocused_opacity` | integer | `0` / `1` | Window unfocused opacity |
|
||||
| `allow_csd` | integer | `0` / `1` | Allow client side decoration |
|
||||
|
||||
> **Tip:** For detailed visual effects configuration, see the [Window Effects](/docs/visuals/effects) page for blur, shadows, and opacity settings.
|
||||
|
||||
### Layout & Scroller
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `scroller_proportion` | float | 0.1-1.0 | Set scroller proportion |
|
||||
| `scroller_proportion_single` | float | 0.1-1.0 | Set scroller auto adjust proportion when it is single window |
|
||||
|
||||
> **Tip:** For comprehensive layout configuration, see the [Layouts](/docs/window-management/layouts) page for all layout options and detailed settings.
|
||||
|
||||
### Animation
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `animation_type_open` | string | zoom, slide, fade, none | Set open animation |
|
||||
| `animation_type_close` | string | zoom, slide, fade, none | Set close animation |
|
||||
| `nofadein` | integer | `0` / `1` | Window ignores fade-in animation |
|
||||
| `nofadeout` | integer | `0` / `1` | Window ignores fade-out animation |
|
||||
|
||||
> **Tip:** For detailed animation configuration, see the [Animations](/docs/visuals/animations) page for available types and settings.
|
||||
|
||||
### Terminal & Swallowing
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `isterm` | integer | `0` / `1` | A new GUI window will replace the isterm window when it is opened |
|
||||
| `noswallow` | integer | `0` / `1` | The window will not replace the isterm window |
|
||||
|
||||
### Global & Special Windows
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `globalkeybinding` | string | `[mod combination][-][key]` | Global keybinding (only works for Wayland apps) |
|
||||
| `isunglobal` | integer | `0` / `1` | Open as unmanaged global window (for desktop pets or camera windows) |
|
||||
| `isnamedscratchpad` | integer | `0` / `1` | 0: disable, 1: named scratchpad |
|
||||
|
||||
> **Tip:** For scratchpad usage, see the [Scratchpad](/docs/window-management/scratchpad) page for detailed configuration examples.
|
||||
|
||||
### Performance & Tearing
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `force_tearing` | integer | `0` / `1` | Set window to tearing state, refer to [Tearing](/docs/configuration/monitors#tearing-game-mode) |
|
||||
|
||||
### Examples
|
||||
|
||||
```ini
|
||||
# Set specific window size and position
|
||||
windowrule=width:1000,height:900,appid:yesplaymusic,title:Demons
|
||||
|
||||
# Global keybindings for OBS Studio
|
||||
windowrule=globalkeybinding:ctrl+alt-o,appid:com.obsproject.Studio
|
||||
windowrule=globalkeybinding:ctrl+alt-n,appid:com.obsproject.Studio
|
||||
windowrule=isopensilent:1,appid:com.obsproject.Studio
|
||||
|
||||
# Force tearing for games
|
||||
windowrule=force_tearing:1,title:vkcube
|
||||
windowrule=force_tearing:1,title:Counter-Strike 2
|
||||
|
||||
# Named scratchpad for file manager
|
||||
windowrule=isnamedscratchpad:1,width:1280,height:800,appid:st-yazi
|
||||
|
||||
# Custom opacity for specific apps
|
||||
windowrule=focused_opacity:0.8,appid:firefox
|
||||
windowrule=unfocused_opacity:0.6,appid:foot
|
||||
|
||||
# Disable blur for selection tools
|
||||
windowrule=noblur:1,appid:slurp
|
||||
|
||||
# Position windows relative to screen center
|
||||
windowrule=offsetx:20,offsety:-30,width:800,height:600,appid:alacritty
|
||||
|
||||
# Send to specific tag and monitor
|
||||
windowrule=tags:9,monitor:HDMI-A-1,appid:discord
|
||||
|
||||
# Terminal swallowing setup
|
||||
windowrule=isterm:1,appid:st
|
||||
windowrule=noswallow:1,appid:foot
|
||||
|
||||
# Disable client-side decorations
|
||||
windowrule=allow_csd:1,appid:firefox
|
||||
|
||||
# Unmanaged global window (desktop pets, camera)
|
||||
windowrule=isunglobal:1,appid:cheese
|
||||
|
||||
# Named scratchpad toggle
|
||||
bind=alt,h,toggle_named_scratchpad,st-yazi,none,st -c st-yazi -e yazi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tag Rules
|
||||
|
||||
You can set all parameters in one line. If only `id` is set, the rule is followed when the id matches. If any of `monitor_name`, `monitor_make`, `monitor_model`, or `monitor_serial` are set, the rule is followed only if **all** of the set monitor fields match.
|
||||
|
||||
> **Warning:** Layouts set in tag rules have a higher priority than monitor rule layouts.
|
||||
|
||||
**Format:**
|
||||
|
||||
```ini
|
||||
tagrule=id:Values,Parameter:Values,Parameter:Values
|
||||
tagrule=id:Values,monitor_name:eDP-1,Parameter:Values,Parameter:Values
|
||||
tagrule=id:Values,monitor_make:xxx,monitor_model:xxx,Parameter:Values
|
||||
```
|
||||
|
||||
> **Tip:** See [Layouts](/docs/window-management/layouts#supported-layouts) for detailed descriptions of each layout type.
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `id` | integer | 0-9 | Match by tag id, 0 means the ~0 tag |
|
||||
| `monitor_name` | string | monitor name | Match by monitor name |
|
||||
| `monitor_make` | string | monitor make | Match by monitor manufacturer |
|
||||
| `monitor_model` | string | monitor model | Match by monitor model |
|
||||
| `monitor_serial` | string | monitor serial | Match by monitor serial number |
|
||||
| `layout_name` | string | layout name | Layout name to set |
|
||||
| `no_render_border` | integer | `0` / `1` | Disable render border |
|
||||
| `open_as_floating` | integer | `0` / `1` | New open window will be floating|
|
||||
| `no_hide` | integer | `0` / `1` | Not hide even if the tag is empty |
|
||||
| `nmaster` | integer | 0, 99 | Number of master windows |
|
||||
| `mfact` | float | 0.1–0.9 | Master area factor |
|
||||
|
||||
### Examples
|
||||
|
||||
```ini
|
||||
# Set layout for specific tags
|
||||
tagrule=id:1,layout_name:scroller
|
||||
tagrule=id:2,layout_name:scroller
|
||||
|
||||
# Limit to specific monitor
|
||||
tagrule=id:1,monitor_name:eDP-1,layout_name:scroller
|
||||
tagrule=id:2,monitor_name:eDP-1,layout_name:scroller
|
||||
|
||||
# Persistent tags (1-4) with layout assignment
|
||||
tagrule=id:1,no_hide:1,layout_name:scroller
|
||||
tagrule=id:2,no_hide:1,layout_name:scroller
|
||||
tagrule=id:3,monitor_name:eDP-1,no_hide:1,layout_name:scroller
|
||||
tagrule=id:4,monitor_name:eDP-1,no_hide:1,layout_name:scroller
|
||||
|
||||
# Advanced tag configuration with master layout settings
|
||||
tagrule=id:5,layout_name:tile,nmaster:2,mfact:0.6
|
||||
tagrule=id:6,monitor_name:HDMI-A-1,layout_name:monocle,no_render_border:1
|
||||
```
|
||||
|
||||
> **Tip:** For Waybar configuration with persistent tags, see [Status Bar](/docs/visuals/status-bar) documentation.
|
||||
|
||||
---
|
||||
|
||||
## Layer Rules
|
||||
|
||||
You can set all parameters in one line. Target "layer shell" surfaces like status bars (`waybar`), launchers (`rofi`), or notification daemons.
|
||||
|
||||
**Format:**
|
||||
|
||||
```ini
|
||||
layerrule=layer_name:Values,Parameter:Values,Parameter:Values
|
||||
```
|
||||
|
||||
> **Tip:** You can use `mmsg get last_open_surface` to get the last open layer name for debugging.
|
||||
|
||||
| Parameter | Type | Values | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `layer_name` | string | layer name | Match name of layer, supports regex |
|
||||
| `animation_type_open` | string | slide, zoom, fade, none | Set open animation |
|
||||
| `animation_type_close` | string | slide, zoom, fade, none | Set close animation |
|
||||
| `noblur` | integer | `0` / `1` | Disable blur |
|
||||
| `noanim` | integer | `0` / `1` | Disable layer animation |
|
||||
| `noshadow` | integer | `0` / `1` | Disable layer shadow |
|
||||
|
||||
> **Tip:** For animation types, see [Animations](/docs/visuals/animations#animation-types). For visual effects, see [Window Effects](/docs/visuals/effects).
|
||||
|
||||
### Examples
|
||||
|
||||
```ini
|
||||
# No blur or animation for slurp selection layer (avoids occlusion and ghosting in screenshots)
|
||||
layerrule=noanim:1,noblur:1,layer_name:selection
|
||||
|
||||
# Zoom animation for Rofi with multiple parameters
|
||||
layerrule=animation_type_open:zoom,noanim:0,layer_name:rofi
|
||||
|
||||
# Disable animations and shadows for notification daemon
|
||||
layerrule=noanim:1,noshadow:1,layer_name:swaync
|
||||
|
||||
# Multiple effects for launcher
|
||||
layerrule=animation_type_open:slide,animation_type_close:fade,noblur:1,layer_name:wofi
|
||||
```
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
---
|
||||
title: Scratchpad
|
||||
description: Manage hidden "scratchpad" windows for quick access.
|
||||
---
|
||||
|
||||
mangowm supports two types of scratchpads: the standard pool (Sway-like) and named scratchpads.
|
||||
|
||||
## Standard Scratchpad
|
||||
|
||||
Any window can be sent to the "scratchpad" pile, which hides it. You can then cycle through them.
|
||||
|
||||
**Keybindings:**
|
||||
|
||||
```ini
|
||||
# Send current window to scratchpad
|
||||
bind=SUPER,i,minimized
|
||||
|
||||
# Toggle (show/hide) the scratchpad
|
||||
bind=ALT,z,toggle_scratchpad
|
||||
|
||||
# Retrieve window from scratchpad (restore)
|
||||
bind=SUPER+SHIFT,i,restore_minimized
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Named Scratchpad
|
||||
|
||||
Named scratchpads are bound to specific keys and applications. When triggered, mangowm will either launch the app (if not running) or toggle its visibility.
|
||||
|
||||
**1. Define the Window Rule**
|
||||
|
||||
You must identify the app using a unique `appid` or `title` and mark it as a named scratchpad. The application must support setting a custom appid or title at launch. Common examples:
|
||||
|
||||
- `st -c my-appid` — sets the appid
|
||||
- `kitty -T my-title` — sets the window title
|
||||
- `foot --app-id my-appid` — sets the appid
|
||||
|
||||
Use `none` as a placeholder when you only want to match by one field.
|
||||
|
||||
```ini
|
||||
# Match by appid
|
||||
windowrule=isnamedscratchpad:1,width:1280,height:800,appid:st-yazi
|
||||
|
||||
# Match by title
|
||||
windowrule=isnamedscratchpad:1,width:1000,height:700,title:kitty-scratch
|
||||
```
|
||||
|
||||
**2. Bind the Toggle Key**
|
||||
|
||||
Format: `bind=MOD,KEY,toggle_named_scratchpad,appid,title,command`
|
||||
|
||||
Use `none` for whichever field you are not matching on.
|
||||
|
||||
```ini
|
||||
# Match by appid: launch 'st' with class 'st-yazi' running 'yazi'
|
||||
bind=alt,h,toggle_named_scratchpad,st-yazi,none,st -c st-yazi -e yazi
|
||||
|
||||
# Match by title: launch 'kitty' with window title 'kitty-scratch'
|
||||
bind=alt,k,toggle_named_scratchpad,none,kitty-scratch,kitty -T kitty-scratch
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Appearance
|
||||
|
||||
You can customize the size of scratchpad windows relative to the screen.
|
||||
|
||||
```ini
|
||||
scratchpad_width_ratio=0.8
|
||||
scratchpad_height_ratio=0.9
|
||||
scratchpadcolor=0x516c93ff
|
||||
```
|
||||
6
flake.lock
generated
6
flake.lock
generated
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1778274207,
|
||||
"narHash": "sha256-I4puXmX1iovcCHZlRmztO3vW0mAbbRvq4F8wgIMQ1MM=",
|
||||
"lastModified": 1750386251,
|
||||
"narHash": "sha256-1ovgdmuDYVo5OUC5NzdF+V4zx2uT8RtsgZahxidBTyw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b3da656039dc7a6240f27b2ef8cc6a3ef3bccae7",
|
||||
"rev": "076e8c6678d8c54204abcb4b1b14c366835a58bb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -43,14 +43,6 @@
|
|||
};
|
||||
packages = {
|
||||
inherit mango;
|
||||
hm-options-json = pkgs.callPackage (import ./nix/generate-options.nix self) {
|
||||
module = ./nix/hm-modules.nix;
|
||||
optionPrefix = "wayland.windowManager.mango.";
|
||||
};
|
||||
nixos-options-json = pkgs.callPackage (import ./nix/generate-options.nix self) {
|
||||
module = ./nix/nixos-modules.nix;
|
||||
optionPrefix = "programs.mango.";
|
||||
};
|
||||
};
|
||||
devShells.default = mango.overrideAttrs shellOverride;
|
||||
formatter = pkgs.alejandra;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/mmsg.c
|
||||
clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/mmsg.c -i mmsg/arg.h -i mmsg/dynarr.h
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Name=Mango
|
||||
DesktopNames=mango;wlroots
|
||||
Comment=mango WM
|
||||
Exec=mango
|
||||
Icon=mango
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
(define-module (mangowm)
|
||||
(define-module (mangowc)
|
||||
#:use-module (guix download)
|
||||
#:use-module (guix git-download)
|
||||
#:use-module (guix gexp)
|
||||
|
|
@ -10,20 +10,19 @@
|
|||
#:use-module (gnu packages pciutils)
|
||||
#:use-module (gnu packages admin)
|
||||
#:use-module (gnu packages pcre)
|
||||
#:use-module (gnu packages javascript)
|
||||
#: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) #:prefix license:))
|
||||
#:use-module (guix licenses))
|
||||
|
||||
|
||||
(define-public mangowm-git
|
||||
(define-public mangowc-git
|
||||
(package
|
||||
(name "mangowm")
|
||||
(name "mangowc")
|
||||
(version "git")
|
||||
(source (local-file "." "mangowm-checkout"
|
||||
(source (local-file "." "mangowc-checkout"
|
||||
#:recursive? #t
|
||||
#:select? (or (git-predicate (current-source-directory))
|
||||
(const #t))))
|
||||
|
|
@ -37,13 +36,10 @@
|
|||
(add-before 'configure 'patch-meson
|
||||
(lambda _
|
||||
(substitute* "meson.build"
|
||||
;; MangoWM ignores sysconfdir handling for NixOS.
|
||||
;; We also need to skip that sysconfdir edits.
|
||||
(("is_nixos = false")
|
||||
"is_nixos = true")
|
||||
;; Unhardcode path. Fixes loading default config.
|
||||
(("'-DSYSCONFDIR=\\\"@0@\\\"'.format\\('/etc'\\)")
|
||||
"'-DSYSCONFDIR=\"@0@\"'.format(sysconfdir)")))))))
|
||||
"'-DSYSCONFDIR=\"@0@\"'.format(sysconfdir)")
|
||||
(("sysconfdir = sysconfdir.substring\\(prefix.length\\(\\)\\)")
|
||||
"")))))))
|
||||
(inputs (list wayland
|
||||
libinput
|
||||
libdrm
|
||||
|
|
@ -54,23 +50,15 @@
|
|||
hwdata
|
||||
seatd
|
||||
pcre2
|
||||
cjson
|
||||
libxcb
|
||||
pixman
|
||||
xcb-util-wm
|
||||
wlroots-0.19
|
||||
wlroots
|
||||
scenefx))
|
||||
(native-inputs (list pkg-config wayland-protocols))
|
||||
(home-page "https://github.com/mangowm/mango")
|
||||
(home-page "https://github.com/DreamMaoMao/mangowc")
|
||||
(synopsis "Wayland compositor based on wlroots and scenefx")
|
||||
(description
|
||||
"MangoWM is a modern, lightweight, high-performance Wayland compositor
|
||||
built on dwl — crafted for speed, flexibility, and a customizable desktop experience.")
|
||||
(license (list license:gpl3 ;mangowm itself, dwl
|
||||
license:expat ;dwm, sway, wlroots
|
||||
license:cc0)))) ;tinywl
|
||||
(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
|
||||
mangowc-git
|
||||
39
meson.build
39
meson.build
|
|
@ -1,5 +1,5 @@
|
|||
project('mango', ['c'],
|
||||
version : '0.14.2',
|
||||
project('mango', ['c', 'cpp'],
|
||||
version : '0.10.8',
|
||||
)
|
||||
|
||||
subdir('protocols')
|
||||
|
|
@ -24,6 +24,10 @@ if sysconfdir.startswith(prefix) and not is_nixos
|
|||
endif
|
||||
endif
|
||||
|
||||
# 打印调试信息,确认 sysconfdir 的值
|
||||
# message('prefix: ' + prefix)
|
||||
# message('sysconfdir: ' + sysconfdir)
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
libm = cc.find_library('m')
|
||||
xcb = dependency('xcb', required : get_option('xwayland'))
|
||||
|
|
@ -35,14 +39,13 @@ 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')
|
||||
pixman_dep = dependency('pixman-1')
|
||||
cjson_dep = dependency('libcjson')
|
||||
|
||||
# version info
|
||||
|
||||
# 获取版本信息
|
||||
git = find_program('git', required : false)
|
||||
is_git_repo = false
|
||||
|
||||
# check if current directory is a git repo
|
||||
# 检查当前目录是否是 Git 仓库
|
||||
if git.found()
|
||||
git_status = run_command(git, 'rev-parse', '--is-inside-work-tree', check : false)
|
||||
if git_status.returncode() == 0 and git_status.stdout().strip() == 'true'
|
||||
|
|
@ -51,18 +54,18 @@ if git.found()
|
|||
endif
|
||||
|
||||
if is_git_repo
|
||||
# if current directory is a git repo, get commit hash and latest tag
|
||||
# 如果是 Git 目录,获取 Commit Hash 和最新的 tag
|
||||
commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip()
|
||||
latest_tag = meson.project_version()
|
||||
latest_tag = run_command(git, 'describe', '--tags', '--abbrev=0', check : false).stdout().strip()
|
||||
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
|
||||
else
|
||||
# if not a git repo, use project version and "release" string
|
||||
# 如果不是 Git 目录,使用项目版本号和 "release" 字符串
|
||||
commit_hash = 'release'
|
||||
latest_tag = meson.project_version()
|
||||
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
|
||||
endif
|
||||
|
||||
# define compilation args
|
||||
# 定义编译参数
|
||||
c_args = [
|
||||
'-g',
|
||||
'-Wno-unused-function',
|
||||
|
|
@ -72,7 +75,7 @@ c_args = [
|
|||
'-DSYSCONFDIR="@0@"'.format('/etc'),
|
||||
]
|
||||
|
||||
# add debug args only when debug option is enabled
|
||||
# 仅在 debug 选项启用时添加调试参数
|
||||
if get_option('asan')
|
||||
c_args += [
|
||||
'-fsanitize=address',
|
||||
|
|
@ -85,7 +88,7 @@ if xcb.found() and xlibs.found()
|
|||
c_args += '-DXWAYLAND'
|
||||
endif
|
||||
|
||||
# define link args
|
||||
# 链接参数(根据 debug 状态添加 ASAN)
|
||||
link_args = []
|
||||
if get_option('asan')
|
||||
link_args += '-fsanitize=address'
|
||||
|
|
@ -107,8 +110,6 @@ executable('mango',
|
|||
libinput_dep,
|
||||
libwayland_client_dep,
|
||||
pcre2_dep,
|
||||
pixman_dep,
|
||||
cjson_dep,
|
||||
],
|
||||
install : true,
|
||||
c_args : c_args,
|
||||
|
|
@ -130,7 +131,7 @@ wayland_scanner_private_code = generator(
|
|||
arguments: ['private-code', '@INPUT@', '@OUTPUT@']
|
||||
)
|
||||
|
||||
# use generator in mmsg target
|
||||
# 在 mmsg 目标中使用生成器
|
||||
executable('mmsg',
|
||||
'mmsg/mmsg.c',
|
||||
wayland_scanner_private_code.process(dwl_ipc_protocol),
|
||||
|
|
@ -145,10 +146,6 @@ executable('mmsg',
|
|||
],
|
||||
)
|
||||
|
||||
mandir = get_option('mandir')
|
||||
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
|
||||
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'))
|
||||
install_data('mmsg/mmsg.1', install_dir : join_paths(mandir, 'man1'))
|
||||
install_data('mango.desktop', install_dir : desktop_install_dir)
|
||||
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
|
||||
|
|
|
|||
65
mmsg/arg.h
Normal file
65
mmsg/arg.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copy me if you can.
|
||||
* by 20h
|
||||
*/
|
||||
|
||||
#ifndef ARG_H__
|
||||
#define ARG_H__
|
||||
|
||||
extern char *argv0;
|
||||
|
||||
/* use main(int argc, char *argv[]) */
|
||||
#define ARGBEGIN \
|
||||
for (argv0 = *argv, argv++, argc--; \
|
||||
argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \
|
||||
char argc_; \
|
||||
char **argv_; \
|
||||
int 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
30
mmsg/dynarr.h
Normal 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
|
||||
122
mmsg/mmsg.1
122
mmsg/mmsg.1
|
|
@ -1,122 +0,0 @@
|
|||
.TH "mmsg" "1" "2026-05-31" "mmsg 0.14.0" "mmsg manual"
|
||||
.SH NAME
|
||||
mmsg \- send commands to the mango Wayland compositor and receive JSON responses
|
||||
.SH SYNOPSIS
|
||||
\fBmmsg\fR <command> [\fIargs\fR...]
|
||||
.br
|
||||
\fBmmsg\fR \-\-help | \-h | help
|
||||
.SH DESCRIPTION
|
||||
\fBmmsg\fR connects to a running \fBmango\fR(1) compositor via the UNIX domain socket
|
||||
specified by the \fIMANGO_INSTANCE_SIGNATURE\fR environment variable. It sends
|
||||
the given command and prints the JSON reply to standard output. In \fBwatch\fR
|
||||
mode it keeps the connection open and prints a stream of updates as they
|
||||
occur.
|
||||
.PP
|
||||
If \fB\-\-help\fR, \fB\-h\fR or \fBhelp\fR is given, a complete usage summary is printed and
|
||||
the program exits with success.
|
||||
.SH COMMANDS
|
||||
.SS "One\-shot queries (get)"
|
||||
All \fBget\fR commands print a single JSON object and then close the connection.
|
||||
.TP
|
||||
\fBget version\fR
|
||||
Return compositor version.
|
||||
.TP
|
||||
\fBget keymode\fR
|
||||
Return current keymode.
|
||||
.TP
|
||||
\fBget keyboardlayout\fR
|
||||
Return current keyboard layout.
|
||||
.TP
|
||||
\fBget last_open_surface\fR [\fImonitor\fR]
|
||||
Return the last open surface (application) for the given monitor.
|
||||
If \fImonitor\fR is omitted the focused monitor is used.
|
||||
.TP
|
||||
\fBget monitor\fR <name>
|
||||
Return details of the named monitor.
|
||||
.TP
|
||||
\fBget focusing\-client\fR
|
||||
Return details of the currently focused client.
|
||||
.TP
|
||||
\fBget client\fR <id>
|
||||
Return details of the client with the given numeric ID.
|
||||
.TP
|
||||
\fBget tag\fR <monitor> <index>
|
||||
Return details of a specific tag (1\-based index) on the named monitor.
|
||||
.TP
|
||||
\fBget all\-clients\fR
|
||||
List all clients.
|
||||
.TP
|
||||
\fBget all\-monitors\fR
|
||||
List all monitors.
|
||||
.TP
|
||||
\fBget all\-tags\fR
|
||||
List tags for all monitors.
|
||||
.TP
|
||||
\fBget tags\fR <monitor>
|
||||
List tags for a specific monitor.
|
||||
.TP
|
||||
\fBdispatch\fR <func>[,arg...] [client,<id>]
|
||||
Invoke an internal compositor function.
|
||||
The function name and its arguments are separated by commas.
|
||||
Optionally add \fBclient,<id>\fR (before or after the function) to target a
|
||||
specific client.
|
||||
.br
|
||||
Examples:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBmmsg dispatch togglefloating\fR
|
||||
.IP \(bu 2
|
||||
\fBmmsg dispatch movewin,10,100\fR
|
||||
.IP \(bu 2
|
||||
\fBmmsg dispatch movewin,10,100 client,4\fR
|
||||
.RE
|
||||
.SS "Persistent streams (watch)"
|
||||
\fBWatch\fR commands keep the connection open and continuously output JSON
|
||||
updates. The initial state is sent immediately, followed by updates whenever
|
||||
relevant changes occur.
|
||||
.TP
|
||||
\fBwatch monitor\fR <name>
|
||||
Stream updates for the named monitor.
|
||||
.TP
|
||||
\fBwatch focusing\-client\fR
|
||||
Stream updates for the focused client.
|
||||
.TP
|
||||
\fBwatch client\fR <id>
|
||||
Stream updates for the client with the given ID.
|
||||
.TP
|
||||
\fBwatch tags\fR <monitor>
|
||||
Stream tag updates for the named monitor.
|
||||
.TP
|
||||
\fBwatch all\-monitors\fR
|
||||
Stream updates for all monitors.
|
||||
.TP
|
||||
\fBwatch all\-tags\fR
|
||||
Stream updates for all tags.
|
||||
.TP
|
||||
\fBwatch all\-clients\fR
|
||||
Stream updates for all clients.
|
||||
.TP
|
||||
\fBwatch keymode\fR
|
||||
Stream keymode changes.
|
||||
.TP
|
||||
\fBwatch keyboardlayout\fR
|
||||
Stream keyboard layout changes.
|
||||
.TP
|
||||
\fBwatch last_open_surface\fR [\fImonitor\fR]
|
||||
Stream last open surface changes for a specific monitor (or the focused one).
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
\fIMANGO_INSTANCE_SIGNATURE\fR
|
||||
Path to the compositor's IPC socket. Set automatically by \fBmango\fR(1).
|
||||
If unset, \fBmmsg\fR prints an error and exits.
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
\fB0\fR
|
||||
Success (or help message printed).
|
||||
.TP
|
||||
\fBEXIT_FAILURE\fR
|
||||
An error occurred (connection refused, send/receive error, etc.).
|
||||
.SH SEE ALSO
|
||||
\fBmango\fR(1)
|
||||
.SH BUGS
|
||||
Report issues at the project's issue tracker.
|
||||
881
mmsg/mmsg.c
881
mmsg/mmsg.c
|
|
@ -1,156 +1,789 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdbool.h>
|
||||
#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 <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
static void usage(void) {
|
||||
printf("Usage: mmsg <command> [args...]\n\n");
|
||||
printf("One-shot queries (get):\n");
|
||||
printf(
|
||||
" get version Show compositor version\n");
|
||||
printf(" get keymode Show current keymode\n");
|
||||
printf(" get keyboardlayout Show current keyboard "
|
||||
"layout\n");
|
||||
printf(" get last_open_surface [monitor] Show last open surface "
|
||||
"(default focused monitor)\n");
|
||||
printf(" get monitor <name> Show monitor details\n");
|
||||
printf(" get focusing-client Show focused client "
|
||||
"details\n");
|
||||
printf(" get client <id> Show client details by "
|
||||
"ID\n");
|
||||
printf(" get tag <monitor> <index> Show tag details "
|
||||
"(1‑based index)\n");
|
||||
printf(" get all-clients List all clients\n");
|
||||
printf(" get all-monitors List all monitors\n");
|
||||
printf(" get all-tags List all tags (all "
|
||||
"monitors)\n");
|
||||
printf(
|
||||
" get tags <monitor> List tags for a monitor\n");
|
||||
printf(" dispatch <func>[,arg...] [client,<id>] Call a compositor "
|
||||
"function\n");
|
||||
printf(" <func> and arguments are separated by commas.\n");
|
||||
printf(" Add 'client,<id>' at the beginning or end to target a "
|
||||
"specific client.\n");
|
||||
printf(" Examples:\n");
|
||||
printf(" dispatch togglefloating\n");
|
||||
printf(" dispatch movewin,10,100\n");
|
||||
printf(" dispatch movewin,10,100 client,4\n");
|
||||
printf("Persistent streams (watch):\n");
|
||||
printf(
|
||||
" watch monitor <name> Stream monitor changes\n");
|
||||
printf(" watch focusing-client Stream focused client "
|
||||
"changes\n");
|
||||
printf(
|
||||
" watch client <id> Stream client changes\n");
|
||||
printf(" watch tags <monitor> Stream tag changes for "
|
||||
"a monitor\n");
|
||||
printf(" watch all-monitors Stream all monitors "
|
||||
"changes\n");
|
||||
printf(
|
||||
" watch all-tags Stream all tags changes\n");
|
||||
printf(" watch all-clients Stream all clients "
|
||||
"changes\n");
|
||||
printf(
|
||||
" watch keymode Stream keymode changes\n");
|
||||
printf(" watch keyboardlayout Stream keyboard layout "
|
||||
"changes\n");
|
||||
printf(" watch last_open_surface [monitor] Stream last open "
|
||||
"surface changes\n\n");
|
||||
printf("Environment:\n");
|
||||
printf(" MANGO_INSTANCE_SIGNATURE IPC socket path (set by the "
|
||||
"compositor)\n\n");
|
||||
printf("Run 'mmsg --help', '-h' or 'help' to see this message.\n");
|
||||
#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 int Oflag;
|
||||
static int Tflag;
|
||||
static int Lflag;
|
||||
static int oflag;
|
||||
static int tflag;
|
||||
static int lflag;
|
||||
static int cflag;
|
||||
static int vflag;
|
||||
static int mflag;
|
||||
static int fflag;
|
||||
static int qflag;
|
||||
static int dflag;
|
||||
static int xflag;
|
||||
static int eflag;
|
||||
static int kflag;
|
||||
static int bflag;
|
||||
static int Aflag;
|
||||
|
||||
static uint32_t occ, seltags, total_clients, urg;
|
||||
|
||||
static char *output_name;
|
||||
static int tagcount;
|
||||
static char *tagset;
|
||||
static char *layout_name;
|
||||
static int 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 (int i = 8; i >= 0; i--) {
|
||||
*buf++ = ((n >> i) & 1) ? '1' : '0';
|
||||
}
|
||||
*buf = '\0'; // 字符串结尾
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc >= 2 &&
|
||||
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
|
||||
strcmp(argv[1], "help") == 0)) {
|
||||
usage();
|
||||
return EXIT_SUCCESS;
|
||||
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);
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: mmsg <command> [args...]\n");
|
||||
fprintf(stderr, " get <type> ... one-shot request\n");
|
||||
fprintf(stderr, " watch <type> ... persistent stream\n");
|
||||
return EXIT_FAILURE;
|
||||
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++;
|
||||
}
|
||||
|
||||
const char *socket_path = getenv("MANGO_INSTANCE_SIGNATURE");
|
||||
if (!socket_path) {
|
||||
fprintf(stderr, "Error: MANGO_INSTANCE_SIGNATURE is not set. Did you "
|
||||
"run 'mmsg' in mango?\n");
|
||||
return EXIT_FAILURE;
|
||||
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");
|
||||
}
|
||||
|
||||
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
perror("socket");
|
||||
return EXIT_FAILURE;
|
||||
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);
|
||||
}
|
||||
|
||||
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
||||
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;
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
perror("connect");
|
||||
close(sock);
|
||||
return EXIT_FAILURE;
|
||||
// 累计所有 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);
|
||||
}
|
||||
|
||||
char cmd[4096] = {0};
|
||||
int offset = 0;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
int n = snprintf(cmd + offset, sizeof(cmd) - offset, "%s%s", argv[i],
|
||||
(i == argc - 1) ? "" : " ");
|
||||
if (n < 0 || n >= (int)(sizeof(cmd) - offset)) {
|
||||
fprintf(stderr, "Error: command too long.\n");
|
||||
close(sock);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
offset += n;
|
||||
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);
|
||||
}
|
||||
|
||||
int n = snprintf(cmd + offset, sizeof(cmd) - offset, "\n");
|
||||
if (n < 0 || n >= (int)(sizeof(cmd) - offset)) {
|
||||
fprintf(stderr, "Error: command too long to append newline.\n");
|
||||
close(sock);
|
||||
return EXIT_FAILURE;
|
||||
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);
|
||||
}
|
||||
|
||||
if (send(sock, cmd, strlen(cmd), MSG_NOSIGNAL) < 0) {
|
||||
perror("send");
|
||||
close(sock);
|
||||
return EXIT_FAILURE;
|
||||
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);
|
||||
}
|
||||
|
||||
FILE *stream = fdopen(sock, "r");
|
||||
if (!stream) {
|
||||
perror("fdopen");
|
||||
close(sock);
|
||||
return EXIT_FAILURE;
|
||||
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);
|
||||
}
|
||||
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
while (getline(&line, &len, stream) != -1) {
|
||||
printf("%s", line);
|
||||
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;
|
||||
int 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;
|
||||
int 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);
|
||||
}
|
||||
|
||||
if (ferror(stream)) {
|
||||
perror("recv");
|
||||
free(line);
|
||||
fclose(stream);
|
||||
return EXIT_FAILURE;
|
||||
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));
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(stream);
|
||||
return EXIT_SUCCESS;
|
||||
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,
|
||||
"usage:"
|
||||
"\t%s [-OTLq]\n"
|
||||
"\t%s [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d "
|
||||
"<cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]\n"
|
||||
"\t%s [-o <output>] (-g | -w) [-OotlcvmfxekbA]\n",
|
||||
argv0, argv0, argv0);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int 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, ®istry_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;
|
||||
}
|
||||
|
|
@ -5,14 +5,13 @@
|
|||
libxcb,
|
||||
libxkbcommon,
|
||||
pcre2,
|
||||
cjson,
|
||||
pixman,
|
||||
pkg-config,
|
||||
stdenv,
|
||||
wayland,
|
||||
wayland-protocols,
|
||||
wayland-scanner,
|
||||
libxcb-wm,
|
||||
xcbutilwm,
|
||||
xwayland,
|
||||
meson,
|
||||
ninja,
|
||||
|
|
@ -49,7 +48,6 @@ stdenv.mkDerivation {
|
|||
libxcb
|
||||
libxkbcommon
|
||||
pcre2
|
||||
cjson
|
||||
pixman
|
||||
wayland
|
||||
wayland-protocols
|
||||
|
|
@ -59,7 +57,7 @@ stdenv.mkDerivation {
|
|||
]
|
||||
++ lib.optionals enableXWayland [
|
||||
libX11
|
||||
libxcb-wm
|
||||
xcbutilwm
|
||||
xwayland
|
||||
];
|
||||
|
||||
|
|
@ -69,8 +67,8 @@ stdenv.mkDerivation {
|
|||
|
||||
meta = {
|
||||
mainProgram = "mango";
|
||||
description = "Practical and Powerful wayland compositor (dwm but wayland)";
|
||||
homepage = "https://github.com/mangowm/mango";
|
||||
description = "A streamlined but feature-rich Wayland compositor";
|
||||
homepage = "https://github.com/DreamMaoMao/mango";
|
||||
license = lib.licenses.gpl3Plus;
|
||||
maintainers = [];
|
||||
platforms = lib.platforms.unix;
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
self:
|
||||
{
|
||||
pkgs,
|
||||
lib ? pkgs.lib,
|
||||
module,
|
||||
optionPrefix,
|
||||
}:
|
||||
let
|
||||
# Absolute store path of the flake root, used to compute relative subpaths
|
||||
repoPath = toString self;
|
||||
|
||||
eval = lib.evalModules {
|
||||
modules = [
|
||||
(import module self)
|
||||
{ _module.check = false; }
|
||||
];
|
||||
specialArgs = { inherit pkgs; };
|
||||
};
|
||||
|
||||
# Relative path of the module file within the repo (e.g. "nix/hm-modules.nix")
|
||||
moduleSubpath = lib.removePrefix "/" (lib.removePrefix repoPath (toString module));
|
||||
|
||||
# Declaration entry linking each option back to its source file on GitHub
|
||||
moduleDeclaration = {
|
||||
url = "https://github.com/mangowm/mango/blob/main/${moduleSubpath}";
|
||||
name = "<mango/${moduleSubpath}>";
|
||||
};
|
||||
|
||||
optionsDoc = pkgs.nixosOptionsDoc {
|
||||
options = eval.options;
|
||||
transformOptions =
|
||||
opt:
|
||||
opt
|
||||
// {
|
||||
visible = opt.visible && !opt.internal;
|
||||
# Strip the option prefix so docs show "enable" instead of "programs.mango.enable"
|
||||
name = lib.removePrefix optionPrefix opt.name;
|
||||
declarations = [ moduleDeclaration ];
|
||||
};
|
||||
};
|
||||
in
|
||||
optionsDoc.optionsJSON
|
||||
|
|
@ -1,28 +1,23 @@
|
|||
self:
|
||||
{
|
||||
self: {
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
}: let
|
||||
cfg = config.wayland.windowManager.mango;
|
||||
selflib = import ./lib.nix lib;
|
||||
variables = lib.concatStringsSep " " cfg.systemd.variables;
|
||||
extraCommands = lib.concatStringsSep " && " cfg.systemd.extraCommands;
|
||||
systemdActivation = "${pkgs.dbus}/bin/dbus-update-activation-environment --systemd ${variables}; ${extraCommands}";
|
||||
systemdActivation = ''${pkgs.dbus}/bin/dbus-update-activation-environment --systemd ${variables}; ${extraCommands}'';
|
||||
autostart_sh = pkgs.writeShellScript "autostart.sh" ''
|
||||
${lib.optionalString cfg.systemd.enable systemdActivation}
|
||||
${cfg.autostart_sh}
|
||||
'';
|
||||
in
|
||||
{
|
||||
in {
|
||||
options = {
|
||||
wayland.windowManager.mango = with lib; {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to enable mangowm, a Wayland compositor based on dwl.";
|
||||
};
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
|
|
@ -80,176 +75,31 @@ in
|
|||
'';
|
||||
};
|
||||
settings = mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
let
|
||||
valueType =
|
||||
nullOr (oneOf [
|
||||
bool
|
||||
int
|
||||
float
|
||||
str
|
||||
path
|
||||
(attrsOf valueType)
|
||||
(listOf valueType)
|
||||
])
|
||||
// {
|
||||
description = "Mango configuration value";
|
||||
};
|
||||
in
|
||||
valueType;
|
||||
default = { };
|
||||
description = ''
|
||||
Mango configuration written in Nix. Entries with the same key
|
||||
should be written as lists. Variables and colors names should be
|
||||
quoted. See <https://mangowm.github.io/docs> for more examples.
|
||||
|
||||
::: {.note}
|
||||
This option uses a structured format that is converted to Mango's
|
||||
configuration syntax. Nested attributes are flattened with underscore separators.
|
||||
For example: `animation.duration_open = 400` becomes `animation_duration_open = 400`
|
||||
|
||||
Keymodes (submaps) are supported via the special `keymode` attribute. Each keymode
|
||||
is a nested attribute set under `keymode` that contains its own bindings.
|
||||
:::
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
# Window effects
|
||||
blur = 1;
|
||||
blur_optimized = 1;
|
||||
blur_params = {
|
||||
radius = 5;
|
||||
num_passes = 2;
|
||||
};
|
||||
border_radius = 6;
|
||||
focused_opacity = 1.0;
|
||||
|
||||
# Animations - use underscores for multi-part keys
|
||||
animations = 1;
|
||||
animation_type_open = "slide";
|
||||
animation_type_close = "slide";
|
||||
animation_duration_open = 400;
|
||||
animation_duration_close = 800;
|
||||
|
||||
# Or use nested attrs (will be flattened with underscores)
|
||||
animation_curve = {
|
||||
open = "0.46,1.0,0.29,1";
|
||||
close = "0.08,0.92,0,1";
|
||||
};
|
||||
|
||||
# Use lists for duplicate keys like bind and tagrule
|
||||
bind = [
|
||||
"SUPER,r,reload_config"
|
||||
"Alt,space,spawn,rofi -show drun"
|
||||
"Alt,Return,spawn,foot"
|
||||
"ALT,R,setkeymode,resize" # Enter resize mode
|
||||
];
|
||||
|
||||
tagrule = [
|
||||
"id:1,layout_name:tile"
|
||||
"id:2,layout_name:scroller"
|
||||
];
|
||||
|
||||
# Keymodes (submaps) for modal keybindings
|
||||
keymode = {
|
||||
resize = {
|
||||
bind = [
|
||||
"NONE,Left,resizewin,-10,0"
|
||||
"NONE,Escape,setkeymode,default"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
description = "mango config content";
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Extra configuration lines to add to `~/.config/mango/config.conf`.
|
||||
This is useful for advanced configurations that don't fit the structured
|
||||
settings format, or for options that aren't yet supported by the module.
|
||||
'';
|
||||
example = ''
|
||||
# Advanced config that doesn't fit structured format
|
||||
special_option = 1
|
||||
# menu and terminal
|
||||
bind=Alt,space,spawn,rofi -show drun
|
||||
bind=Alt,Return,spawn,foot
|
||||
'';
|
||||
};
|
||||
topPrefixes = mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
List of prefixes for attributes that should appear at the top of the config file.
|
||||
Attributes starting with these prefixes will be sorted to the beginning.
|
||||
'';
|
||||
example = [ "source" ];
|
||||
};
|
||||
bottomPrefixes = mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
List of prefixes for attributes that should appear at the bottom of the config file.
|
||||
Attributes starting with these prefixes will be sorted to the end.
|
||||
'';
|
||||
example = [ "source" ];
|
||||
};
|
||||
autostart_sh = mkOption {
|
||||
description = ''
|
||||
Shell script to run on mango startup. No shebang needed.
|
||||
|
||||
When this option is set, the script will be written to
|
||||
`~/.config/mango/autostart.sh` and an `exec-once` line
|
||||
will be automatically added to the config to execute it.
|
||||
'';
|
||||
description = "WARRNING: This is a shell script, but no need to add shebang";
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = ''
|
||||
waybar &
|
||||
dunst &
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (
|
||||
let
|
||||
finalConfigText =
|
||||
# Support old string-based config during transition period
|
||||
(
|
||||
if builtins.isString cfg.settings then
|
||||
cfg.settings
|
||||
else
|
||||
lib.optionalString (cfg.settings != { }) (
|
||||
selflib.toMango {
|
||||
topCommandsPrefixes = cfg.topPrefixes;
|
||||
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||
} cfg.settings
|
||||
)
|
||||
)
|
||||
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig
|
||||
+ lib.optionalString (cfg.autostart_sh != "") "\nexec-once=~/.config/mango/autostart.sh\n";
|
||||
|
||||
validatedConfig = pkgs.runCommand "mango-config.conf" { } ''
|
||||
cp ${pkgs.writeText "mango-config.conf" finalConfigText} "$out"
|
||||
${cfg.package}/bin/mango -c "$out" -p || exit 1
|
||||
'';
|
||||
in
|
||||
{
|
||||
# Backwards compatibility warning for old string-based config
|
||||
warnings = lib.optional (builtins.isString cfg.settings) ''
|
||||
wayland.windowManager.mango.settings: Using a string for settings is deprecated.
|
||||
Please migrate to the new structured attribute set format.
|
||||
See the module documentation for examples, or use the 'extraConfig' option for raw config strings.
|
||||
The old string format will be removed in a future release.
|
||||
'';
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
home.packages = [cfg.package];
|
||||
xdg.configFile = {
|
||||
"mango/config.conf" =
|
||||
lib.mkIf (cfg.settings != { } || cfg.extraConfig != "" || cfg.autostart_sh != "")
|
||||
{
|
||||
source = validatedConfig;
|
||||
"mango/config.conf" = lib.mkIf (cfg.settings != "") {
|
||||
text = cfg.settings;
|
||||
};
|
||||
"mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") {
|
||||
source = autostart_sh;
|
||||
|
|
@ -261,7 +111,8 @@ in
|
|||
Description = "mango compositor session";
|
||||
Documentation = ["man:systemd.special(7)"];
|
||||
BindsTo = ["graphical-session.target"];
|
||||
Wants = [
|
||||
Wants =
|
||||
[
|
||||
"graphical-session-pre.target"
|
||||
]
|
||||
++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target";
|
||||
|
|
@ -269,6 +120,5 @@ in
|
|||
Before = lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target";
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
312
nix/lib.nix
312
nix/lib.nix
|
|
@ -1,312 +0,0 @@
|
|||
lib:
|
||||
let
|
||||
inherit (lib)
|
||||
attrNames
|
||||
filterAttrs
|
||||
foldl
|
||||
generators
|
||||
partition
|
||||
removeAttrs
|
||||
;
|
||||
|
||||
inherit (lib.strings)
|
||||
concatMapStrings
|
||||
hasPrefix
|
||||
;
|
||||
|
||||
/**
|
||||
Convert a structured Nix attribute set into Mango's configuration format.
|
||||
|
||||
This function takes a nested attribute set and converts it into Mango-compatible
|
||||
configuration syntax, supporting top, bottom, and regular command sections.
|
||||
|
||||
Commands are flattened using the `flattenAttrs` function, and attributes are formatted as
|
||||
`key = value` pairs. Lists are expanded as duplicate keys to match Mango's expected format.
|
||||
|
||||
Configuration:
|
||||
|
||||
* `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `[]`).
|
||||
* `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`).
|
||||
|
||||
Attention:
|
||||
|
||||
- The function ensures top commands appear **first** and bottom commands **last**.
|
||||
- The generated configuration is a **single string**, suitable for writing to a config file.
|
||||
- Lists are converted into multiple entries, ensuring compatibility with Mango.
|
||||
|
||||
# Inputs
|
||||
|
||||
Structured function argument:
|
||||
|
||||
: topCommandsPrefixes (optional, default: `[]`)
|
||||
: A list of prefixes that define **top** commands. Any key starting with one of these
|
||||
prefixes will be placed at the beginning of the configuration.
|
||||
: bottomCommandsPrefixes (optional, default: `[]`)
|
||||
: A list of prefixes that define **bottom** commands. Any key starting with one of these
|
||||
prefixes will be placed at the end of the configuration.
|
||||
|
||||
Value:
|
||||
|
||||
: The attribute set to be converted to Hyprland configuration format.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
toMango :: AttrSet -> AttrSet -> String
|
||||
```
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
|
||||
## Basic mangowc configuration
|
||||
|
||||
```nix
|
||||
let
|
||||
config = {
|
||||
blur = 1;
|
||||
blur_params_radius = 5;
|
||||
border_radius = 6;
|
||||
animations = 1;
|
||||
animation_duration_open = 400;
|
||||
};
|
||||
in lib.toMango {} config
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
animations = 1
|
||||
animation_duration_open = 400
|
||||
blur = 1
|
||||
blur_params_radius = 5
|
||||
border_radius = 6
|
||||
```
|
||||
|
||||
## Using nested attributes
|
||||
|
||||
```nix
|
||||
let
|
||||
config = {
|
||||
blur = 1;
|
||||
blur_params = {
|
||||
radius = 5;
|
||||
num_passes = 2;
|
||||
noise = 0.02;
|
||||
};
|
||||
animation_curve = {
|
||||
open = "0.46,1.0,0.29,1";
|
||||
close = "0.08,0.92,0,1";
|
||||
};
|
||||
};
|
||||
in lib.toMango {} config
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
animation_curve_close = 0.08,0.92,0,1
|
||||
animation_curve_open = 0.46,1.0,0.29,1
|
||||
blur = 1
|
||||
blur_params_noise = 0.02
|
||||
blur_params_num_passes = 2
|
||||
blur_params_radius = 5
|
||||
```
|
||||
|
||||
## Using lists for duplicate keys
|
||||
|
||||
```nix
|
||||
let
|
||||
config = {
|
||||
bind = [
|
||||
"SUPER,r,reload_config"
|
||||
"Alt,space,spawn,rofi -show drun"
|
||||
"Alt,Return,spawn,foot"
|
||||
];
|
||||
tagrule = [
|
||||
"id:1,layout_name:tile"
|
||||
"id:2,layout_name:scroller"
|
||||
];
|
||||
};
|
||||
in lib.toMango {} config
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
bind = SUPER,r,reload_config
|
||||
bind = Alt,space,spawn,rofi -show drun
|
||||
bind = Alt,Return,spawn,foot
|
||||
tagrule = id:1,layout_name:tile
|
||||
tagrule = id:2,layout_name:scroller
|
||||
```
|
||||
|
||||
## Using keymodes (submaps)
|
||||
|
||||
```nix
|
||||
let
|
||||
config = {
|
||||
bind = [
|
||||
"SUPER,Q,killclient"
|
||||
"ALT,R,setkeymode,resize"
|
||||
];
|
||||
keymode = {
|
||||
resize = {
|
||||
bind = [
|
||||
"NONE,Left,resizewin,-10,0"
|
||||
"NONE,Right,resizewin,10,0"
|
||||
"NONE,Escape,setkeymode,default"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
in lib.toMango {} config
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
bind = SUPER,Q,killclient
|
||||
bind = ALT,R,setkeymode,resize
|
||||
|
||||
keymode = resize
|
||||
bind = NONE,Left,resizewin,-10,0
|
||||
bind = NONE,Right,resizewin,10,0
|
||||
bind = NONE,Escape,setkeymode,default
|
||||
```
|
||||
|
||||
:::
|
||||
*/
|
||||
toMango =
|
||||
{
|
||||
topCommandsPrefixes ? [ ],
|
||||
bottomCommandsPrefixes ? [ ],
|
||||
}:
|
||||
attrs:
|
||||
let
|
||||
toMango' =
|
||||
attrs:
|
||||
let
|
||||
# Specially configured `toKeyValue` generator with support for duplicate keys
|
||||
# and a legible key-value separator.
|
||||
mkCommands = generators.toKeyValue {
|
||||
mkKeyValue = generators.mkKeyValueDefault { } " = ";
|
||||
listsAsDuplicateKeys = true;
|
||||
indent = ""; # No indent, since we don't have nesting
|
||||
};
|
||||
|
||||
# Extract keymode definitions if they exist
|
||||
keymodes = attrs.keymode or { };
|
||||
attrsWithoutKeymodes = removeAttrs attrs [ "keymode" ];
|
||||
|
||||
# Generate keymode blocks
|
||||
# Format: keymode=name\nbind=...\nbind=...\n
|
||||
mkKeymodeBlock =
|
||||
name: modeAttrs:
|
||||
let
|
||||
modeCommands = flattenAttrs (p: k: "${p}_${k}") modeAttrs;
|
||||
in
|
||||
"keymode = ${name}\n${mkCommands modeCommands}";
|
||||
|
||||
keymodeBlocks =
|
||||
if keymodes == { } then
|
||||
""
|
||||
else
|
||||
"\n" + concatMapStrings (name: mkKeymodeBlock name keymodes.${name} + "\n") (attrNames keymodes);
|
||||
|
||||
# Flatten the attrset, combining keys in a "path" like `"a_b_c" = "x"`.
|
||||
# Uses `flattenAttrs` with an underscore separator.
|
||||
commands = flattenAttrs (p: k: "${p}_${k}") attrsWithoutKeymodes;
|
||||
|
||||
# General filtering function to check if a key starts with any prefix in a given list.
|
||||
filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list;
|
||||
|
||||
# Partition keys into top commands and the rest
|
||||
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
|
||||
topCommands = filterAttrs (n: _: builtins.elem n result.right) commands;
|
||||
remainingCommands = removeAttrs commands result.right;
|
||||
|
||||
# Partition remaining commands into bottom commands and regular commands
|
||||
result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong;
|
||||
bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands;
|
||||
regularCommands = removeAttrs remainingCommands result2.right;
|
||||
in
|
||||
# Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands.
|
||||
# Keymodes are appended at the end.
|
||||
concatMapStrings mkCommands [
|
||||
topCommands
|
||||
regularCommands
|
||||
bottomCommands
|
||||
]
|
||||
+ keymodeBlocks;
|
||||
in
|
||||
toMango' attrs;
|
||||
|
||||
/**
|
||||
Flatten a nested attribute set into a flat attribute set, using a custom key separator function.
|
||||
|
||||
This function recursively traverses a nested attribute set and produces a flat attribute set
|
||||
where keys are joined using a user-defined function (`pred`). It allows transforming deeply
|
||||
nested structures into a single-level attribute set while preserving key-value relationships.
|
||||
|
||||
Configuration:
|
||||
|
||||
* `pred` - A function `(string -> string -> string)` defining how keys should be concatenated.
|
||||
|
||||
# Inputs
|
||||
|
||||
Structured function argument:
|
||||
|
||||
: pred (required)
|
||||
: A function that determines how parent and child keys should be combined into a single key.
|
||||
It takes a `prefix` (parent key) and `key` (current key) and returns the joined key.
|
||||
|
||||
Value:
|
||||
|
||||
: The nested attribute set to be flattened.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet
|
||||
```
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
|
||||
```nix
|
||||
let
|
||||
nested = {
|
||||
a = "3";
|
||||
b = { c = "4"; d = "5"; };
|
||||
};
|
||||
|
||||
separator = (prefix: key: "${prefix}.${key}"); # Use dot notation
|
||||
in lib.flattenAttrs separator nested
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```nix
|
||||
{
|
||||
"a" = "3";
|
||||
"b.c" = "4";
|
||||
"b.d" = "5";
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
*/
|
||||
flattenAttrs =
|
||||
pred: attrs:
|
||||
let
|
||||
flattenAttrs' =
|
||||
prefix: attrs:
|
||||
builtins.foldl' (
|
||||
acc: key:
|
||||
let
|
||||
value = attrs.${key};
|
||||
newKey = if prefix == "" then key else pred prefix key;
|
||||
in
|
||||
acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; })
|
||||
) { } (builtins.attrNames attrs);
|
||||
in
|
||||
flattenAttrs' "" attrs;
|
||||
in
|
||||
{
|
||||
inherit flattenAttrs toMango;
|
||||
}
|
||||
|
|
@ -9,11 +9,6 @@ in {
|
|||
options = {
|
||||
programs.mango = {
|
||||
enable = lib.mkEnableOption "mango, a wayland compositor based on dwl";
|
||||
addLoginEntry = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to add a login entry to the display manager for mango. Only has effect if a display manager is configured (e.g. SDDM, GDM via `services.displayManager`).";
|
||||
};
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mango;
|
||||
|
|
@ -60,7 +55,7 @@ in {
|
|||
programs.xwayland.enable = lib.mkDefault true;
|
||||
|
||||
services = {
|
||||
displayManager.sessionPackages = lib.mkIf cfg.addLoginEntry [ cfg.package ];
|
||||
displayManager.sessionPackages = [cfg.package];
|
||||
|
||||
graphical-desktop.enable = lib.mkDefault true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
wayland_scanner = find_program('wayland-scanner')
|
||||
wayland_protos_dep = dependency('wayland-protocols')
|
||||
wl_protocol_dir = wayland_protos_dep.get_variable(pkgconfig:'pkgdatadir')
|
||||
wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir')
|
||||
wayland_scanner_code = generator(
|
||||
wayland_scanner,
|
||||
output: '@BASENAME@-protocol.c',
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
static void client_swap_layout_properties(Client *c1, Client *c2) {
|
||||
// Grid 属性交换
|
||||
double grid_col_per = c1->grid_col_per;
|
||||
double grid_row_per = c1->grid_row_per;
|
||||
int32_t grid_col_idx = c1->grid_col_idx;
|
||||
int32_t grid_row_idx = c1->grid_row_idx;
|
||||
|
||||
c1->grid_col_per = c2->grid_col_per;
|
||||
c1->grid_row_per = c2->grid_row_per;
|
||||
c1->grid_col_idx = c2->grid_col_idx;
|
||||
c1->grid_row_idx = c2->grid_row_idx;
|
||||
|
||||
c2->grid_col_per = grid_col_per;
|
||||
c2->grid_row_per = grid_row_per;
|
||||
c2->grid_col_idx = grid_col_idx;
|
||||
c2->grid_row_idx = grid_row_idx;
|
||||
|
||||
// Master / Stack 属性交换
|
||||
double master_inner_per = c1->master_inner_per;
|
||||
double master_mfact_per = c1->master_mfact_per;
|
||||
double stack_inner_per = c1->stack_inner_per;
|
||||
|
||||
c1->master_inner_per = c2->master_inner_per;
|
||||
c1->master_mfact_per = c2->master_mfact_per;
|
||||
c1->stack_inner_per = c2->stack_inner_per;
|
||||
|
||||
c2->master_inner_per = master_inner_per;
|
||||
c2->master_mfact_per = master_mfact_per;
|
||||
c2->stack_inner_per = stack_inner_per;
|
||||
}
|
||||
|
||||
static void client_swap_monitors_and_tags(Client *c1, Client *c2) {
|
||||
Monitor *tmp_mon = c2->mon;
|
||||
uint32_t tmp_tags = c2->tags;
|
||||
c2->mon = c1->mon;
|
||||
c1->mon = tmp_mon;
|
||||
c2->tags = c1->tags;
|
||||
c1->tags = tmp_tags;
|
||||
}
|
||||
|
||||
static void finish_exchange_arrange_and_focus(Client *c1, Client *c2,
|
||||
Monitor *m1, Monitor *m2) {
|
||||
if (m1 != m2) {
|
||||
arrange(c1->mon, false, false);
|
||||
arrange(c2->mon, false, false);
|
||||
} else {
|
||||
arrange(c1->mon, false, false);
|
||||
}
|
||||
wl_list_remove(&c2->flink);
|
||||
wl_list_insert(&c1->flink, &c2->flink);
|
||||
}
|
||||
|
||||
void client_tile_resize(Client *c, struct wlr_box geo, int32_t interact) {
|
||||
if (!ISFAKETILED(c))
|
||||
return;
|
||||
|
||||
if (!c->isfullscreen && !c->ismaximizescreen) {
|
||||
resize(c, geo, interact);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t next_client_id = 0;
|
||||
uint32_t generate_client_id(void) { return ++next_client_id; }
|
||||
|
||||
void client_active(Client *c) {
|
||||
uint32_t target;
|
||||
|
||||
if (client_is_unmanaged(c)) {
|
||||
focusclient(c, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->swallowing || !c->mon)
|
||||
return;
|
||||
|
||||
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, false);
|
||||
return;
|
||||
}
|
||||
|
||||
target = get_tags_first_tag(c->tags);
|
||||
view_in_mon(&(Arg){.ui = target}, true, c->mon, true);
|
||||
focusclient(c, 1);
|
||||
}
|
||||
|
||||
void client_pending_force_kill(Client *c) {
|
||||
if (!c)
|
||||
return;
|
||||
kill(c->pid, SIGKILL);
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,22 +1,22 @@
|
|||
struct dvec2 calculate_animation_curve_at(double t, int32_t type) {
|
||||
struct dvec2 calculate_animation_curve_at(double t, int type) {
|
||||
struct dvec2 point;
|
||||
double *animation_curve;
|
||||
if (type == MOVE) {
|
||||
animation_curve = config.animation_curve_move;
|
||||
animation_curve = animation_curve_move;
|
||||
} else if (type == OPEN) {
|
||||
animation_curve = config.animation_curve_open;
|
||||
animation_curve = animation_curve_open;
|
||||
} else if (type == TAG) {
|
||||
animation_curve = config.animation_curve_tag;
|
||||
animation_curve = animation_curve_tag;
|
||||
} else if (type == CLOSE) {
|
||||
animation_curve = config.animation_curve_close;
|
||||
animation_curve = animation_curve_close;
|
||||
} else if (type == FOCUS) {
|
||||
animation_curve = config.animation_curve_focus;
|
||||
animation_curve = animation_curve_focus;
|
||||
} else if (type == OPAFADEIN) {
|
||||
animation_curve = config.animation_curve_opafadein;
|
||||
animation_curve = animation_curve_opafadein;
|
||||
} else if (type == OPAFADEOUT) {
|
||||
animation_curve = config.animation_curve_opafadeout;
|
||||
animation_curve = animation_curve_opafadeout;
|
||||
} else {
|
||||
animation_curve = config.animation_curve_move;
|
||||
animation_curve = animation_curve_move;
|
||||
}
|
||||
|
||||
point.x = 3 * t * (1 - t) * (1 - t) * animation_curve[0] +
|
||||
|
|
@ -28,12 +28,6 @@ struct dvec2 calculate_animation_curve_at(double t, int32_t type) {
|
|||
return point;
|
||||
}
|
||||
|
||||
void handle_snapshot_meta_destroy(struct wl_listener *listener, void *data) {
|
||||
SnapshotMetadata *meta = wl_container_of(listener, meta, destroy);
|
||||
wl_list_remove(&meta->destroy.link); // 安全移除监听器
|
||||
free(meta);
|
||||
}
|
||||
|
||||
void init_baked_points(void) {
|
||||
baked_points_move = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_move));
|
||||
baked_points_open = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_open));
|
||||
|
|
@ -47,41 +41,41 @@ void init_baked_points(void) {
|
|||
baked_points_opafadeout =
|
||||
calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_opafadeout));
|
||||
|
||||
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
|
||||
baked_points_move[i] = calculate_animation_curve_at(
|
||||
(double)i / (BAKED_POINTS_COUNT - 1), MOVE);
|
||||
}
|
||||
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
|
||||
baked_points_open[i] = calculate_animation_curve_at(
|
||||
(double)i / (BAKED_POINTS_COUNT - 1), OPEN);
|
||||
}
|
||||
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
|
||||
baked_points_tag[i] = calculate_animation_curve_at(
|
||||
(double)i / (BAKED_POINTS_COUNT - 1), TAG);
|
||||
}
|
||||
for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
|
||||
for (uint32_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++) {
|
||||
for (uint32_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++) {
|
||||
for (uint32_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++) {
|
||||
for (uint32_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, int32_t type) {
|
||||
int32_t down = 0;
|
||||
int32_t up = BAKED_POINTS_COUNT - 1;
|
||||
double find_animation_curve_at(double t, int type) {
|
||||
uint32_t down = 0;
|
||||
uint32_t up = BAKED_POINTS_COUNT - 1;
|
||||
|
||||
int32_t middle = (up + down) / 2;
|
||||
uint32_t middle = (up + down) / 2;
|
||||
struct dvec2 *baked_points;
|
||||
if (type == MOVE) {
|
||||
baked_points = baked_points_move;
|
||||
|
|
@ -112,8 +106,7 @@ double find_animation_curve_at(double t, int32_t type) {
|
|||
return baked_points[up].y;
|
||||
}
|
||||
|
||||
static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx,
|
||||
int32_t ly,
|
||||
static bool scene_node_snapshot(struct wlr_scene_node *node, int lx, int ly,
|
||||
struct wlr_scene_tree *snapshot_tree) {
|
||||
if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) {
|
||||
return true;
|
||||
|
|
@ -160,43 +153,12 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx,
|
|||
struct wlr_scene_buffer *scene_buffer =
|
||||
wlr_scene_buffer_from_node(node);
|
||||
|
||||
// 创建中间包装树节点
|
||||
struct wlr_scene_tree *wrapper = wlr_scene_tree_create(snapshot_tree);
|
||||
if (wrapper == NULL) {
|
||||
return false;
|
||||
}
|
||||
snapshot_node = &wrapper->node; // 坐标位移应用在外层包装盒上
|
||||
|
||||
// 收集表面状态并保存为元数据
|
||||
SnapshotMetadata *meta = calloc(1, sizeof(SnapshotMetadata));
|
||||
if (meta == NULL) {
|
||||
wlr_scene_node_destroy(&wrapper->node);
|
||||
return false;
|
||||
}
|
||||
meta->orig_width = scene_buffer->dst_width;
|
||||
meta->orig_height = scene_buffer->dst_height;
|
||||
|
||||
struct wlr_scene_surface *scene_surface =
|
||||
wlr_scene_surface_try_from_buffer(scene_buffer);
|
||||
if (scene_surface != NULL) {
|
||||
meta->is_subsurface =
|
||||
!!wlr_subsurface_try_from_wlr_surface(scene_surface->surface);
|
||||
}
|
||||
|
||||
// 绑定销毁回调监听,随包装节点销毁而释放内存
|
||||
meta->destroy.notify = handle_snapshot_meta_destroy;
|
||||
wl_signal_add(&wrapper->node.events.destroy, &meta->destroy);
|
||||
wrapper->node.data = meta;
|
||||
|
||||
// 将真正的 buffer 挂靠在 wrapper 下面(相对坐标0,0)
|
||||
struct wlr_scene_buffer *snapshot_buffer =
|
||||
wlr_scene_buffer_create(wrapper, NULL);
|
||||
wlr_scene_buffer_create(snapshot_tree, NULL);
|
||||
if (snapshot_buffer == NULL) {
|
||||
wlr_scene_node_destroy(&wrapper->node);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保留原生的 data 指针(如 Client*),防止事件派发/焦点获取失效
|
||||
snapshot_node = &snapshot_buffer->node;
|
||||
snapshot_buffer->node.data = scene_buffer->node.data;
|
||||
|
||||
wlr_scene_buffer_set_dest_size(snapshot_buffer, scene_buffer->dst_width,
|
||||
|
|
@ -216,8 +178,16 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx,
|
|||
scene_buffer->corner_radius,
|
||||
scene_buffer->corners);
|
||||
|
||||
// wlr_scene_buffer_set_backdrop_blur_optimized(
|
||||
// snapshot_buffer, scene_buffer->backdrop_blur_optimized);
|
||||
// wlr_scene_buffer_set_backdrop_blur_ignore_transparent(
|
||||
// snapshot_buffer, scene_buffer->backdrop_blur_ignore_transparent);
|
||||
wlr_scene_buffer_set_backdrop_blur(snapshot_buffer, false);
|
||||
|
||||
snapshot_buffer->node.data = scene_buffer->node.data;
|
||||
|
||||
struct wlr_scene_surface *scene_surface =
|
||||
wlr_scene_surface_try_from_buffer(scene_buffer);
|
||||
if (scene_surface != NULL && scene_surface->surface->buffer != NULL) {
|
||||
wlr_scene_buffer_set_buffer(snapshot_buffer,
|
||||
&scene_surface->surface->buffer->base);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height) {
|
||||
void layer_actual_size(LayerSurface *l, uint32_t *width, uint32_t *height) {
|
||||
struct wlr_box box;
|
||||
|
||||
if (l->animation.running) {
|
||||
|
|
@ -42,7 +42,7 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) {
|
|||
.height = state->desired_height};
|
||||
|
||||
// 水平方向定位
|
||||
const int32_t both_horiz =
|
||||
const uint32_t both_horiz =
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
||||
if (box.width == 0) {
|
||||
box.x = bounds.x;
|
||||
|
|
@ -57,7 +57,7 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) {
|
|||
}
|
||||
|
||||
// 垂直方向定位
|
||||
const int32_t both_vert =
|
||||
const uint32_t both_vert =
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
||||
if (box.height == 0) {
|
||||
box.y = bounds.y;
|
||||
|
|
@ -101,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) {
|
||||
int32_t slide_direction;
|
||||
int32_t horizontal, horizontal_value;
|
||||
int32_t vertical, vertical_value;
|
||||
int32_t center_x, center_y;
|
||||
int slide_direction;
|
||||
int horizontal, horizontal_value;
|
||||
int vertical, vertical_value;
|
||||
int center_x, center_y;
|
||||
|
||||
if (!l)
|
||||
return;
|
||||
|
|
@ -156,16 +156,17 @@ void layer_draw_shadow(LayerSurface *l) {
|
|||
if (!l->mapped || !l->shadow)
|
||||
return;
|
||||
|
||||
if (!config.shadows || !config.layer_shadows || l->noshadow) {
|
||||
if (!shadows || !layer_shadows || l->noshadow) {
|
||||
wlr_scene_shadow_set_size(l->shadow, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t width, height;
|
||||
uint32_t width, height;
|
||||
layer_actual_size(l, &width, &height);
|
||||
|
||||
int32_t delta = config.shadows_size;
|
||||
uint32_t delta = shadows_size;
|
||||
|
||||
/* we calculate where to clip the shadow */
|
||||
struct wlr_box layer_box = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
|
|
@ -174,21 +175,23 @@ void layer_draw_shadow(LayerSurface *l) {
|
|||
};
|
||||
|
||||
struct wlr_box shadow_box = {
|
||||
.x = config.shadows_position_x,
|
||||
.y = config.shadows_position_y,
|
||||
.x = shadows_position_x,
|
||||
.y = shadows_position_y,
|
||||
.width = width + 2 * delta,
|
||||
.height = height + 2 * delta,
|
||||
};
|
||||
|
||||
struct wlr_box intersection_box;
|
||||
wlr_box_intersection(&intersection_box, &layer_box, &shadow_box);
|
||||
intersection_box.x -= config.shadows_position_x;
|
||||
intersection_box.y -= config.shadows_position_y;
|
||||
/* 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;
|
||||
|
||||
struct clipped_region clipped_region = {
|
||||
.area = intersection_box,
|
||||
.corner_radius = config.border_radius,
|
||||
.corners = config.border_radius_location_default,
|
||||
.corner_radius = border_radius,
|
||||
.corners = border_radius_location_default,
|
||||
};
|
||||
|
||||
wlr_scene_node_set_position(&l->shadow->node, shadow_box.x, shadow_box.y);
|
||||
|
|
@ -197,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,
|
||||
int32_t sx, int32_t sy, void *data) {
|
||||
void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx,
|
||||
int sy, void *data) {
|
||||
BufferData *buffer_data = (BufferData *)data;
|
||||
|
||||
struct wlr_scene_surface *scene_surface =
|
||||
|
|
@ -209,8 +212,8 @@ void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer,
|
|||
|
||||
struct wlr_surface *surface = scene_surface->surface;
|
||||
|
||||
int32_t surface_width = surface->current.width * buffer_data->width_scale;
|
||||
int32_t surface_height =
|
||||
uint32_t surface_width = surface->current.width * buffer_data->width_scale;
|
||||
uint32_t surface_height =
|
||||
surface->current.height * buffer_data->height_scale;
|
||||
|
||||
if (surface_height > 0 && surface_width > 0) {
|
||||
|
|
@ -219,8 +222,7 @@ void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer,
|
|||
}
|
||||
|
||||
void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer,
|
||||
int32_t sx, int32_t sy,
|
||||
void *data) {
|
||||
int sx, int sy, void *data) {
|
||||
BufferData *buffer_data = (BufferData *)data;
|
||||
wlr_scene_buffer_set_dest_size(buffer, buffer_data->width,
|
||||
buffer_data->height);
|
||||
|
|
@ -233,22 +235,23 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) {
|
|||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started;
|
||||
uint32_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;
|
||||
int type = l->animation.action = l->animation.action;
|
||||
double factor = find_animation_curve_at(animation_passed, type);
|
||||
int32_t width = l->animation.initial.width +
|
||||
uint32_t width = l->animation.initial.width +
|
||||
(l->current.width - l->animation.initial.width) * factor;
|
||||
int32_t height = l->animation.initial.height +
|
||||
uint32_t height =
|
||||
l->animation.initial.height +
|
||||
(l->current.height - l->animation.initial.height) * factor;
|
||||
|
||||
int32_t x = l->animation.initial.x +
|
||||
uint32_t x = l->animation.initial.x +
|
||||
(l->current.x - l->animation.initial.x) * factor;
|
||||
int32_t y = l->animation.initial.y +
|
||||
uint32_t y = l->animation.initial.y +
|
||||
(l->current.y - l->animation.initial.y) * factor;
|
||||
|
||||
wlr_scene_node_set_position(&l->scene->node, x, y);
|
||||
|
|
@ -258,7 +261,7 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) {
|
|||
buffer_data.height = height;
|
||||
|
||||
if ((!l->animation_type_close &&
|
||||
strcmp(config.layer_animation_type_close, "zoom") == 0) ||
|
||||
strcmp(layer_animation_type_close, "zoom") == 0) ||
|
||||
(l->animation_type_close &&
|
||||
strcmp(l->animation_type_close, "zoom") == 0)) {
|
||||
wlr_scene_node_for_each_buffer(&l->scene->node,
|
||||
|
|
@ -276,12 +279,12 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) {
|
|||
double opacity_eased_progress =
|
||||
find_animation_curve_at(animation_passed, OPAFADEOUT);
|
||||
|
||||
double percent = config.fadeout_begin_opacity -
|
||||
(opacity_eased_progress * config.fadeout_begin_opacity);
|
||||
double percent = fadeout_begin_opacity -
|
||||
(opacity_eased_progress * fadeout_begin_opacity);
|
||||
|
||||
double opacity = MAX(percent, 0.0f);
|
||||
|
||||
if (config.animation_fade_out)
|
||||
if (animation_fade_out)
|
||||
wlr_scene_node_for_each_buffer(&l->scene->node,
|
||||
scene_buffer_apply_opacity, &opacity);
|
||||
|
||||
|
|
@ -301,34 +304,35 @@ void layer_animation_next_tick(LayerSurface *l) {
|
|||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started;
|
||||
uint32_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;
|
||||
int type = l->animation.action == NONE ? MOVE : l->animation.action;
|
||||
double factor = find_animation_curve_at(animation_passed, type);
|
||||
|
||||
int32_t width = l->animation.initial.width +
|
||||
uint32_t width = l->animation.initial.width +
|
||||
(l->current.width - l->animation.initial.width) * factor;
|
||||
int32_t height = l->animation.initial.height +
|
||||
uint32_t height =
|
||||
l->animation.initial.height +
|
||||
(l->current.height - l->animation.initial.height) * factor;
|
||||
|
||||
int32_t x = l->animation.initial.x +
|
||||
uint32_t x = l->animation.initial.x +
|
||||
(l->current.x - l->animation.initial.x) * factor;
|
||||
int32_t y = l->animation.initial.y +
|
||||
uint32_t y = l->animation.initial.y +
|
||||
(l->current.y - l->animation.initial.y) * factor;
|
||||
|
||||
double opacity_eased_progress =
|
||||
find_animation_curve_at(animation_passed, OPAFADEIN);
|
||||
|
||||
double opacity =
|
||||
MIN(config.fadein_begin_opacity +
|
||||
opacity_eased_progress * (1.0 - config.fadein_begin_opacity),
|
||||
MIN(fadein_begin_opacity +
|
||||
opacity_eased_progress * (1.0 - fadein_begin_opacity),
|
||||
1.0f);
|
||||
|
||||
if (config.animation_fade_in)
|
||||
if (animation_fade_in)
|
||||
wlr_scene_node_for_each_buffer(&l->scene->node,
|
||||
scene_buffer_apply_opacity, &opacity);
|
||||
|
||||
|
|
@ -344,7 +348,7 @@ void layer_animation_next_tick(LayerSurface *l) {
|
|||
}
|
||||
|
||||
if ((!l->animation_type_open &&
|
||||
strcmp(config.layer_animation_type_open, "zoom") == 0) ||
|
||||
strcmp(layer_animation_type_open, "zoom") == 0) ||
|
||||
(l->animation_type_open &&
|
||||
strcmp(l->animation_type_open, "zoom") == 0)) {
|
||||
wlr_scene_node_for_each_buffer(
|
||||
|
|
@ -367,7 +371,7 @@ void layer_animation_next_tick(LayerSurface *l) {
|
|||
|
||||
void init_fadeout_layers(LayerSurface *l) {
|
||||
|
||||
if (!config.animations || !config.layer_animations || l->noanim) {
|
||||
if (!animations || !layer_animations || l->noanim) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +381,7 @@ void init_fadeout_layers(LayerSurface *l) {
|
|||
if ((l->animation_type_close &&
|
||||
strcmp(l->animation_type_close, "none") == 0) ||
|
||||
(!l->animation_type_close &&
|
||||
strcmp(config.layer_animation_type_close, "none") == 0)) {
|
||||
strcmp(layer_animation_type_close, "none") == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -400,7 +404,7 @@ void init_fadeout_layers(LayerSurface *l) {
|
|||
return;
|
||||
}
|
||||
|
||||
fadeout_layer->animation.duration = config.animation_duration_close;
|
||||
fadeout_layer->animation.duration = animation_duration_close;
|
||||
fadeout_layer->geom = fadeout_layer->current =
|
||||
fadeout_layer->animainit_geom = fadeout_layer->animation.initial =
|
||||
l->animation.current;
|
||||
|
|
@ -416,14 +420,14 @@ void init_fadeout_layers(LayerSurface *l) {
|
|||
fadeout_layer->animation.initial.y = 0;
|
||||
|
||||
if ((!l->animation_type_close &&
|
||||
strcmp(config.layer_animation_type_close, "zoom") == 0) ||
|
||||
strcmp(layer_animation_type_close, "zoom") == 0) ||
|
||||
(l->animation_type_close &&
|
||||
strcmp(l->animation_type_close, "zoom") == 0)) {
|
||||
// 算出要设置的绝对坐标和大小
|
||||
fadeout_layer->current.width =
|
||||
(float)l->animation.current.width * config.zoom_end_ratio;
|
||||
(float)l->animation.current.width * zoom_end_ratio;
|
||||
fadeout_layer->current.height =
|
||||
(float)l->animation.current.height * config.zoom_end_ratio;
|
||||
(float)l->animation.current.height * zoom_end_ratio;
|
||||
fadeout_layer->current.x = usable_area.x + usable_area.width / 2 -
|
||||
fadeout_layer->current.width / 2;
|
||||
fadeout_layer->current.y = usable_area.y + usable_area.height / 2 -
|
||||
|
|
@ -435,7 +439,7 @@ void init_fadeout_layers(LayerSurface *l) {
|
|||
fadeout_layer->current.y - l->animation.current.y;
|
||||
|
||||
} else if ((!l->animation_type_close &&
|
||||
strcmp(config.layer_animation_type_close, "slide") == 0) ||
|
||||
strcmp(layer_animation_type_close, "slide") == 0) ||
|
||||
(l->animation_type_close &&
|
||||
strcmp(l->animation_type_close, "slide") == 0)) {
|
||||
// 获取slide动画的结束绝对坐标和大小
|
||||
|
|
@ -463,7 +467,6 @@ void init_fadeout_layers(LayerSurface *l) {
|
|||
wl_list_insert(&fadeout_layers, &fadeout_layer->fadeout_link);
|
||||
|
||||
// 请求刷新屏幕
|
||||
if (l->mon)
|
||||
wlr_output_schedule_frame(l->mon->wlr_output);
|
||||
}
|
||||
|
||||
|
|
@ -480,18 +483,17 @@ void layer_set_pending_state(LayerSurface *l) {
|
|||
if (l->animation.action == OPEN && !l->animation.running) {
|
||||
|
||||
if ((!l->animation_type_open &&
|
||||
strcmp(config.layer_animation_type_open, "zoom") == 0) ||
|
||||
strcmp(layer_animation_type_open, "zoom") == 0) ||
|
||||
(l->animation_type_open &&
|
||||
strcmp(l->animation_type_open, "zoom") == 0)) {
|
||||
l->animainit_geom.width = l->geom.width * config.zoom_initial_ratio;
|
||||
l->animainit_geom.height =
|
||||
l->geom.height * config.zoom_initial_ratio;
|
||||
l->animainit_geom.width = l->geom.width * zoom_initial_ratio;
|
||||
l->animainit_geom.height = l->geom.height * zoom_initial_ratio;
|
||||
l->animainit_geom.x = usable_area.x + usable_area.width / 2 -
|
||||
l->animainit_geom.width / 2;
|
||||
l->animainit_geom.y = usable_area.y + usable_area.height / 2 -
|
||||
l->animainit_geom.height / 2;
|
||||
} else if ((!l->animation_type_open &&
|
||||
strcmp(config.layer_animation_type_open, "slide") == 0) ||
|
||||
strcmp(layer_animation_type_open, "slide") == 0) ||
|
||||
(l->animation_type_open &&
|
||||
strcmp(l->animation_type_open, "slide") == 0)) {
|
||||
|
||||
|
|
@ -505,7 +507,8 @@ void layer_set_pending_state(LayerSurface *l) {
|
|||
} else {
|
||||
l->animainit_geom = l->animation.current;
|
||||
}
|
||||
if (!config.animations || !config.layer_animations || l->noanim ||
|
||||
// 判断是否需要动画
|
||||
if (!animations || !layer_animations || l->noanim ||
|
||||
l->layer_surface->current.layer ==
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ||
|
||||
l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) {
|
||||
|
|
@ -517,7 +520,7 @@ void layer_set_pending_state(LayerSurface *l) {
|
|||
if (((l->animation_type_open &&
|
||||
strcmp(l->animation_type_open, "none") == 0) ||
|
||||
(!l->animation_type_open &&
|
||||
strcmp(config.layer_animation_type_open, "none") == 0)) &&
|
||||
strcmp(layer_animation_type_open, "none") == 0)) &&
|
||||
l->animation.action == OPEN) {
|
||||
l->animation.should_animate = false;
|
||||
}
|
||||
|
|
@ -547,7 +550,6 @@ void layer_commit(LayerSurface *l) {
|
|||
l->animation.should_animate = false;
|
||||
}
|
||||
// 请求刷新屏幕
|
||||
if (l->mon)
|
||||
wlr_output_schedule_frame(l->mon->wlr_output);
|
||||
}
|
||||
|
||||
|
|
@ -564,8 +566,7 @@ bool layer_draw_frame(LayerSurface *l) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (config.animations && config.layer_animations && l->animation.running &&
|
||||
!l->noanim) {
|
||||
if (animations && layer_animations && l->animation.running && !l->noanim) {
|
||||
layer_animation_next_tick(l);
|
||||
layer_draw_shadow(l);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -5,27 +5,13 @@ void set_tagin_animation(Monitor *m, Client *c) {
|
|||
return;
|
||||
}
|
||||
|
||||
if ((c->isglobal || c->isunglobal) ||
|
||||
(c->tags & (1 << (m->pertag->prevtag - 1)) &&
|
||||
c->tags & (1 << (m->pertag->curtag - 1)))) {
|
||||
c->animation.tagouting = false;
|
||||
c->animation.tagouted = false;
|
||||
c->animation.tagining = false;
|
||||
c->animation.action = MOVE;
|
||||
return;
|
||||
}
|
||||
if (m->pertag->curtag > m->pertag->prevtag) {
|
||||
|
||||
bool going_forward = m->carousel_anim_dir
|
||||
? m->carousel_anim_dir > 0
|
||||
: m->pertag->curtag > m->pertag->prevtag;
|
||||
|
||||
if (going_forward) {
|
||||
|
||||
c->animainit_geom.x = config.tag_animation_direction == VERTICAL
|
||||
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 = config.tag_animation_direction == VERTICAL
|
||||
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;
|
||||
|
|
@ -33,11 +19,11 @@ void set_tagin_animation(Monitor *m, Client *c) {
|
|||
} else {
|
||||
|
||||
c->animainit_geom.x =
|
||||
config.tag_animation_direction == VERTICAL
|
||||
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 =
|
||||
config.tag_animation_direction == VERTICAL
|
||||
tag_animation_direction == VERTICAL
|
||||
? MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height)
|
||||
: c->animation.current.y;
|
||||
}
|
||||
|
|
@ -50,10 +36,10 @@ void set_arrange_visible(Monitor *m, Client *c, bool want_animation) {
|
|||
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 &&
|
||||
config.animations) {
|
||||
m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) {
|
||||
c->animation.tagining = true;
|
||||
set_tagin_animation(m, c);
|
||||
} else {
|
||||
|
|
@ -68,27 +54,13 @@ void set_arrange_visible(Monitor *m, Client *c, bool want_animation) {
|
|||
}
|
||||
|
||||
void set_tagout_animation(Monitor *m, Client *c) {
|
||||
|
||||
if ((c->isglobal || c->isunglobal) ||
|
||||
(c->tags & (1 << (m->pertag->prevtag - 1)) &&
|
||||
c->tags & (1 << (m->pertag->curtag - 1)))) {
|
||||
c->animation.tagouting = false;
|
||||
c->animation.tagouted = false;
|
||||
c->animation.tagining = false;
|
||||
c->animation.action = MOVE;
|
||||
return;
|
||||
}
|
||||
|
||||
bool going_forward = m->carousel_anim_dir
|
||||
? m->carousel_anim_dir > 0
|
||||
: m->pertag->curtag > m->pertag->prevtag;
|
||||
if (going_forward) {
|
||||
if (m->pertag->curtag > m->pertag->prevtag) {
|
||||
c->pending = c->geom;
|
||||
c->pending.x =
|
||||
config.tag_animation_direction == VERTICAL
|
||||
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 = config.tag_animation_direction == VERTICAL
|
||||
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;
|
||||
|
|
@ -96,11 +68,11 @@ void set_tagout_animation(Monitor *m, Client *c) {
|
|||
resize(c, c->geom, 0);
|
||||
} else {
|
||||
c->pending = c->geom;
|
||||
c->pending.x = config.tag_animation_direction == VERTICAL
|
||||
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 = config.tag_animation_direction == VERTICAL
|
||||
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;
|
||||
|
|
@ -109,14 +81,13 @@ void set_tagout_animation(Monitor *m, Client *c) {
|
|||
}
|
||||
|
||||
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 &&
|
||||
config.animations) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
/* Leave these functions first; they're used in the others */
|
||||
static inline int32_t client_is_x11(Client *c) {
|
||||
static inline int client_is_x11(Client *c) {
|
||||
#ifdef XWAYLAND
|
||||
return c->type == X11;
|
||||
#endif
|
||||
|
|
@ -21,15 +21,14 @@ static inline struct wlr_surface *client_surface(Client *c) {
|
|||
return c->surface.xdg->surface;
|
||||
}
|
||||
|
||||
static inline int32_t toplevel_from_wlr_surface(struct wlr_surface *s,
|
||||
Client **pc,
|
||||
static inline int 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;
|
||||
int32_t type = -1;
|
||||
int type = -1;
|
||||
#ifdef XWAYLAND
|
||||
struct wlr_xwayland_surface *xsurface;
|
||||
#endif
|
||||
|
|
@ -89,7 +88,7 @@ end:
|
|||
|
||||
/* The others */
|
||||
static inline void client_activate_surface(struct wlr_surface *s,
|
||||
int32_t activated) {
|
||||
int activated) {
|
||||
struct wlr_xdg_toplevel *toplevel;
|
||||
#ifdef XWAYLAND
|
||||
struct wlr_xwayland_surface *xsurface;
|
||||
|
|
@ -114,7 +113,7 @@ static inline const char *client_get_appid(Client *c) {
|
|||
: "broken";
|
||||
}
|
||||
|
||||
static inline int32_t client_get_pid(Client *c) {
|
||||
static inline int client_get_pid(Client *c) {
|
||||
pid_t pid;
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c))
|
||||
|
|
@ -128,8 +127,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 - 2 * c->bw,
|
||||
.height = c->geom.height - 2 * c->bw,
|
||||
.width = c->geom.width - c->bw,
|
||||
.height = c->geom.height - c->bw,
|
||||
};
|
||||
|
||||
#ifdef XWAYLAND
|
||||
|
|
@ -170,7 +169,7 @@ static inline Client *client_get_parent(Client *c) {
|
|||
return p;
|
||||
}
|
||||
|
||||
static inline int32_t client_has_children(Client *c) {
|
||||
static inline int client_has_children(Client *c) {
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c))
|
||||
return !wl_list_empty(&c->surface.xwayland->children);
|
||||
|
|
@ -190,7 +189,7 @@ static inline const char *client_get_title(Client *c) {
|
|||
: "broken";
|
||||
}
|
||||
|
||||
static inline int32_t client_is_float_type(Client *c) {
|
||||
static inline int client_is_float_type(Client *c) {
|
||||
struct wlr_xdg_toplevel *toplevel;
|
||||
struct wlr_xdg_toplevel_state state;
|
||||
|
||||
|
|
@ -230,12 +229,12 @@ static inline int32_t client_is_float_type(Client *c) {
|
|||
state.min_height == state.max_height));
|
||||
}
|
||||
|
||||
static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) {
|
||||
static inline int 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;
|
||||
int32_t unused_lx, unused_ly;
|
||||
int 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,
|
||||
|
|
@ -243,7 +242,32 @@ static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int32_t client_is_unmanaged(Client *c) {
|
||||
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) {
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c))
|
||||
return c->surface.xwayland->override_redirect;
|
||||
|
|
@ -275,7 +299,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, int32_t fullscreen) {
|
||||
static inline void client_set_fullscreen(Client *c, int fullscreen) {
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c)) {
|
||||
wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
|
||||
|
|
@ -294,34 +318,9 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
|
|||
uint32_t height) {
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c)) {
|
||||
struct wlr_xwayland_surface *surface = c->surface.xwayland;
|
||||
struct wlr_surface_state *state = &surface->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;
|
||||
}
|
||||
|
||||
xcb_size_hints_t *size_hints = surface->size_hints;
|
||||
int32_t width = c->geom.width - 2 * c->bw;
|
||||
int32_t height = c->geom.height - 2 * c->bw;
|
||||
|
||||
if (size_hints &&
|
||||
c->geom.width - 2 * (int32_t)c->bw < size_hints->min_width)
|
||||
width = size_hints->min_width;
|
||||
if (size_hints &&
|
||||
c->geom.height - 2 * (int32_t)c->bw < size_hints->min_height)
|
||||
height = size_hints->min_height;
|
||||
|
||||
wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw,
|
||||
c->geom.y + c->bw, width, height);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if ((int32_t)width == c->surface.xdg->toplevel->current.width &&
|
||||
|
|
@ -360,7 +359,7 @@ static inline void client_set_maximized(Client *c, bool maximized) {
|
|||
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_fakemaximize) {
|
||||
if (client_is_x11(c) && c->force_maximize) {
|
||||
wlr_xwayland_surface_set_maximized(c->surface.xwayland,
|
||||
edges != WLR_EDGE_NONE,
|
||||
edges != WLR_EDGE_NONE);
|
||||
|
|
@ -375,12 +374,21 @@ static inline void client_set_tiled(Client *c, uint32_t edges) {
|
|||
wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges);
|
||||
}
|
||||
|
||||
if (c->force_fakemaximize) {
|
||||
if (c->force_maximize) {
|
||||
wlr_xdg_toplevel_set_maximized(toplevel, edges != WLR_EDGE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int32_t client_should_ignore_focus(Client *c) {
|
||||
static inline void client_set_suspended(Client *c, int suspended) {
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c))
|
||||
return;
|
||||
#endif
|
||||
|
||||
wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended);
|
||||
}
|
||||
|
||||
static inline int client_should_ignore_focus(Client *c) {
|
||||
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c)) {
|
||||
|
|
@ -395,7 +403,7 @@ static inline int32_t client_should_ignore_focus(Client *c) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int32_t client_is_x11_popup(Client *c) {
|
||||
static inline int client_is_x11_popup(Client *c) {
|
||||
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c)) {
|
||||
|
|
@ -424,7 +432,7 @@ static inline int32_t client_is_x11_popup(Client *c) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int32_t client_should_global(Client *c) {
|
||||
static inline int client_should_global(Client *c) {
|
||||
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c)) {
|
||||
|
|
@ -437,7 +445,7 @@ static inline int32_t client_should_global(Client *c) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int32_t client_should_overtop(Client *c) {
|
||||
static inline int client_should_overtop(Client *c) {
|
||||
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c)) {
|
||||
|
|
@ -449,7 +457,7 @@ static inline int32_t client_should_overtop(Client *c) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int32_t client_wants_focus(Client *c) {
|
||||
static inline int client_wants_focus(Client *c) {
|
||||
#ifdef XWAYLAND
|
||||
return client_is_unmanaged(c) &&
|
||||
wlr_xwayland_surface_override_redirect_wants_focus(
|
||||
|
|
@ -460,7 +468,7 @@ static inline int32_t client_wants_focus(Client *c) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int32_t client_wants_fullscreen(Client *c) {
|
||||
static inline int client_wants_fullscreen(Client *c) {
|
||||
#ifdef XWAYLAND
|
||||
if (client_is_x11(c))
|
||||
return c->surface.xwayland->fullscreen;
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ void *ecalloc(size_t nmemb, size_t size) {
|
|||
return p;
|
||||
}
|
||||
|
||||
int32_t fd_set_nonblock(int32_t fd) {
|
||||
int32_t flags = fcntl(fd, F_GETFL);
|
||||
int fd_set_nonblock(int fd) {
|
||||
int flags = fcntl(fd, F_GETFL);
|
||||
if (flags < 0) {
|
||||
perror("fcntl(F_GETFL):");
|
||||
return -1;
|
||||
|
|
@ -50,8 +50,8 @@ int32_t fd_set_nonblock(int32_t fd) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int32_t regex_match(const char *pattern, const char *str) {
|
||||
int32_t errnum;
|
||||
int regex_match(const char *pattern, const char *str) {
|
||||
int errnum;
|
||||
PCRE2_SIZE erroffset;
|
||||
|
||||
if (!pattern || !str) {
|
||||
|
|
@ -70,7 +70,7 @@ int32_t regex_match(const char *pattern, const char *str) {
|
|||
|
||||
pcre2_match_data *match_data =
|
||||
pcre2_match_data_create_from_pattern(re, NULL);
|
||||
int32_t ret =
|
||||
int ret =
|
||||
pcre2_match(re, (PCRE2_SPTR)str, strlen(str), 0, 0, match_data, NULL);
|
||||
|
||||
pcre2_match_data_free(match_data);
|
||||
|
|
@ -92,117 +92,3 @@ uint32_t get_now_in_ms(void) {
|
|||
uint32_t timespec_to_ms(struct timespec *ts) {
|
||||
return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
char *join_strings(char *arr[], const char *sep) {
|
||||
if (!arr || !arr[0]) {
|
||||
char *empty = malloc(1);
|
||||
if (empty)
|
||||
empty[0] = '\0';
|
||||
return empty;
|
||||
}
|
||||
|
||||
size_t total_len = 0;
|
||||
int count = 0;
|
||||
for (int i = 0; arr[i] != NULL; i++) {
|
||||
total_len += strlen(arr[i]);
|
||||
count++;
|
||||
}
|
||||
if (count > 0) {
|
||||
total_len += strlen(sep) * (count - 1);
|
||||
}
|
||||
|
||||
char *result = malloc(total_len + 1);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
result[0] = '\0';
|
||||
for (int i = 0; arr[i] != NULL; i++) {
|
||||
if (i > 0)
|
||||
strcat(result, sep);
|
||||
strcat(result, arr[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char *join_strings_with_suffix(char *arr[], const char *suffix,
|
||||
const char *sep) {
|
||||
if (!arr || !arr[0]) {
|
||||
char *empty = malloc(1);
|
||||
if (empty)
|
||||
empty[0] = '\0';
|
||||
return empty;
|
||||
}
|
||||
|
||||
size_t total_len = 0;
|
||||
int count = 0;
|
||||
for (int i = 0; arr[i] != NULL; i++) {
|
||||
total_len += strlen(arr[i]) + strlen(suffix);
|
||||
count++;
|
||||
}
|
||||
if (count > 0) {
|
||||
total_len += strlen(sep) * (count - 1);
|
||||
}
|
||||
|
||||
char *result = malloc(total_len + 1);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
result[0] = '\0';
|
||||
for (int i = 0; arr[i] != NULL; i++) {
|
||||
if (i > 0)
|
||||
strcat(result, sep);
|
||||
strcat(result, arr[i]);
|
||||
strcat(result, suffix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char *string_printf(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vsnprintf(NULL, 0, fmt, args);
|
||||
va_end(args);
|
||||
if (len < 0)
|
||||
return NULL;
|
||||
|
||||
char *str = malloc(len + 1);
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(str, len + 1, fmt, args);
|
||||
va_end(args);
|
||||
return str;
|
||||
}
|
||||
|
||||
void wl_list_swap(struct wl_list *l1, struct wl_list *l2) {
|
||||
struct wl_list *tmp1_prev = l1->prev;
|
||||
struct wl_list *tmp2_prev = l2->prev;
|
||||
struct wl_list *tmp1_next = l1->next;
|
||||
struct wl_list *tmp2_next = l2->next;
|
||||
|
||||
if (l1->next == l2) { /* l1 -> l2 相邻 */
|
||||
l1->next = l2->next;
|
||||
l1->prev = l2;
|
||||
l2->next = l1;
|
||||
l2->prev = tmp1_prev;
|
||||
tmp1_prev->next = l2;
|
||||
tmp2_next->prev = l1;
|
||||
} else if (l2->next == l1) { /* l2 -> l1 相邻 */
|
||||
l2->next = l1->next;
|
||||
l2->prev = l1;
|
||||
l1->next = l2;
|
||||
l1->prev = tmp2_prev;
|
||||
tmp2_prev->next = l1;
|
||||
tmp1_next->prev = l2;
|
||||
} else { /* 不相邻 */
|
||||
l2->next = tmp1_next;
|
||||
l2->prev = tmp1_prev;
|
||||
l1->next = tmp2_next;
|
||||
l1->prev = tmp2_prev;
|
||||
tmp1_prev->next = l2;
|
||||
tmp1_next->prev = l2;
|
||||
tmp2_prev->next = l1;
|
||||
tmp2_next->prev = l1;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,8 @@
|
|||
|
||||
void die(const char *fmt, ...);
|
||||
void *ecalloc(size_t nmemb, size_t size);
|
||||
int32_t fd_set_nonblock(int32_t fd);
|
||||
int32_t regex_match(const char *pattern_mb, const char *str_mb);
|
||||
int fd_set_nonblock(int fd);
|
||||
int 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, ...);
|
||||
void wl_list_swap(struct wl_list *l1, struct wl_list *l2);
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,128 @@
|
|||
#define MODKEY WLR_MODIFIER_ALT
|
||||
// TODO: remove this file in the future, replace all global variables with
|
||||
// config.xxx
|
||||
|
||||
static const char *tags[] = {
|
||||
"1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
};
|
||||
/* speedie's mango config */
|
||||
|
||||
#define COLOR(hex) \
|
||||
{((hex >> 24) & 0xFF) / 255.0f, ((hex >> 16) & 0xFF) / 255.0f, \
|
||||
((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; // 动画结束窗口比例
|
||||
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
|
||||
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 */
|
||||
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数量
|
||||
int center_master_overspread = 0; // 中心master时是否铺满
|
||||
int center_when_single_stack = 1; // 单个stack时是否居中
|
||||
/* logging */
|
||||
int log_level = WLR_ERROR;
|
||||
uint32_t numlockon = 0; // 是否打开右边小键盘
|
||||
uint32_t capslock = 0; // 是否启用快捷键
|
||||
|
||||
uint32_t ov_tab_mode = 0; // alt tab切换模式
|
||||
uint32_t hotarea_size = 10; // 热区大小,10x10
|
||||
uint32_t enable_hotarea = 1; // 是否启用鼠标热区
|
||||
int smartgaps = 0; /* 1 means no outer gap when there is only one window */
|
||||
int 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;
|
||||
float scroller_default_proportion = 0.9;
|
||||
float scroller_default_proportion_single = 1.0;
|
||||
int scroller_ignore_proportion_single = 0;
|
||||
int scroller_focus_center = 0;
|
||||
int scroller_prefer_center = 0;
|
||||
int focus_cross_monitor = 0;
|
||||
int focus_cross_tag = 0;
|
||||
int exchange_cross_monitor = 0;
|
||||
int scratchpad_cross_monitor = 0;
|
||||
int view_current_to_back = 1;
|
||||
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;
|
||||
uint32_t cursor_size = 24;
|
||||
uint32_t cursor_hide_timeout = 0;
|
||||
|
||||
uint32_t swipe_min_threshold = 1;
|
||||
|
||||
int inhibit_regardless_of_visibility =
|
||||
0; /* 1 means idle inhibitors will disable idle tracking even if it's
|
||||
surface isn't visible */
|
||||
uint32_t borderpx = 4; /* border pixel of windows */
|
||||
float rootcolor[] = COLOR(0x323232ff);
|
||||
float bordercolor[] = COLOR(0x444444ff);
|
||||
float focuscolor[] = COLOR(0xc66b25ff);
|
||||
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时 窗口与窗口 缝隙大小 */
|
||||
|
||||
/* 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;
|
||||
int adaptive_sync = 0;
|
||||
int allow_lock_transparent = 0;
|
||||
double drag_refresh_interval = 30.0;
|
||||
int allow_tearing = TEARING_DISABLED;
|
||||
int allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;
|
||||
|
||||
/* keyboard */
|
||||
|
||||
/*
|
||||
only layout can modify after fisrt init
|
||||
other fields change will be ignored.
|
||||
*/
|
||||
char xkb_rules_rules[256];
|
||||
char xkb_rules_model[256];
|
||||
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,
|
||||
|
|
@ -11,3 +130,105 @@ static const struct xkb_rule_names xkb_fallback_rules = {
|
|||
.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:
|
||||
.options = "ctrl:nocaps",
|
||||
*/
|
||||
.rules = xkb_rules_rules, .model = xkb_rules_model,
|
||||
.layout = xkb_rules_layout, .variant = xkb_rules_variant,
|
||||
.options = xkb_rules_options,
|
||||
};
|
||||
|
||||
int repeat_rate = 25;
|
||||
int 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;
|
||||
int edge_scroller_pointer_focus = 1;
|
||||
|
||||
/* You can choose between:
|
||||
LIBINPUT_CONFIG_SCROLL_NO_SCROLL
|
||||
LIBINPUT_CONFIG_SCROLL_2FG
|
||||
LIBINPUT_CONFIG_SCROLL_EDGE
|
||||
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
|
||||
*/
|
||||
enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
|
||||
uint32_t scroll_button = 274;
|
||||
|
||||
/* You can choose between:
|
||||
LIBINPUT_CONFIG_CLICK_METHOD_NONE
|
||||
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
|
||||
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
|
||||
*/
|
||||
enum libinput_config_click_method click_method =
|
||||
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
|
||||
|
||||
/* You can choose between:
|
||||
LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
|
||||
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
|
||||
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
|
||||
*/
|
||||
uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
|
||||
|
||||
/* You can choose between:
|
||||
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
|
||||
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
|
||||
*/
|
||||
enum libinput_config_accel_profile accel_profile =
|
||||
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
|
||||
double accel_speed = 0.0;
|
||||
/* You can choose between:
|
||||
LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
|
||||
LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
|
||||
*/
|
||||
enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
|
||||
|
||||
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
|
||||
#define MODKEY WLR_MODIFIER_ALT
|
||||
|
||||
static const char *tags[] = {
|
||||
"1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
struct blur_data blur_params;
|
||||
|
||||
int blur_params_num_passes = 1;
|
||||
int 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;
|
||||
uint32_t shadows_size = 10;
|
||||
double shadows_blur = 15;
|
||||
int shadows_position_x = 0;
|
||||
int shadows_position_y = 0;
|
||||
float shadowscolor[] = COLOR(0x000000ff);
|
||||
;
|
||||
|
|
|
|||
|
|
@ -1,77 +1,71 @@
|
|||
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);
|
||||
int32_t toggle_all_floating(const Arg *arg);
|
||||
int32_t dwindle_toggle_split_direction(const Arg *arg);
|
||||
int32_t dwindle_split_horizontal(const Arg *arg);
|
||||
int32_t dwindle_split_vertical(const Arg *arg);
|
||||
int32_t focusid(const Arg *arg);
|
||||
int minimized(const Arg *arg);
|
||||
int restore_minimized(const Arg *arg);
|
||||
int toggle_scratchpad(const Arg *arg);
|
||||
int focusdir(const Arg *arg);
|
||||
int toggleoverview(const Arg *arg);
|
||||
int set_proportion(const Arg *arg);
|
||||
int switch_proportion_preset(const Arg *arg);
|
||||
int zoom(const Arg *arg);
|
||||
int tagsilent(const Arg *arg);
|
||||
int tagtoleft(const Arg *arg);
|
||||
int tagtoright(const Arg *arg);
|
||||
int tagcrossmon(const Arg *arg);
|
||||
int viewtoleft(const Arg *arg);
|
||||
int viewtoright(const Arg *arg);
|
||||
int viewtoleft_have_client(const Arg *arg);
|
||||
int viewtoright_have_client(const Arg *arg);
|
||||
int viewcrossmon(const Arg *arg);
|
||||
int togglefloating(const Arg *arg);
|
||||
int togglefullscreen(const Arg *arg);
|
||||
int togglemaximizescreen(const Arg *arg);
|
||||
int togglegaps(const Arg *arg);
|
||||
int tagmon(const Arg *arg);
|
||||
int spawn(const Arg *arg);
|
||||
int spawn_shell(const Arg *arg);
|
||||
int spawn_on_empty(const Arg *arg);
|
||||
int setkeymode(const Arg *arg);
|
||||
int switch_keyboard_layout(const Arg *arg);
|
||||
int setlayout(const Arg *arg);
|
||||
int switch_layout(const Arg *arg);
|
||||
int setmfact(const Arg *arg);
|
||||
int quit(const Arg *arg);
|
||||
int moveresize(const Arg *arg);
|
||||
int exchange_client(const Arg *arg);
|
||||
int exchange_stack_client(const Arg *arg);
|
||||
int killclient(const Arg *arg);
|
||||
int toggleglobal(const Arg *arg);
|
||||
int incnmaster(const Arg *arg);
|
||||
int focusmon(const Arg *arg);
|
||||
int focusstack(const Arg *arg);
|
||||
int chvt(const Arg *arg);
|
||||
int reload_config(const Arg *arg);
|
||||
int smartmovewin(const Arg *arg);
|
||||
int smartresizewin(const Arg *arg);
|
||||
int centerwin(const Arg *arg);
|
||||
int bind_to_view(const Arg *arg);
|
||||
int toggletag(const Arg *arg);
|
||||
int toggleview(const Arg *arg);
|
||||
int tag(const Arg *arg);
|
||||
int comboview(const Arg *arg);
|
||||
int incgaps(const Arg *arg);
|
||||
int incigaps(const Arg *arg);
|
||||
int incihgaps(const Arg *arg);
|
||||
int incivgaps(const Arg *arg);
|
||||
int incogaps(const Arg *arg);
|
||||
int incohgaps(const Arg *arg);
|
||||
int incovgaps(const Arg *arg);
|
||||
int defaultgaps(const Arg *arg);
|
||||
int togglefakefullscreen(const Arg *arg);
|
||||
int toggleoverlay(const Arg *arg);
|
||||
int movewin(const Arg *arg);
|
||||
int resizewin(const Arg *arg);
|
||||
int toggle_named_scratchpad(const Arg *arg);
|
||||
int toggle_render_border(const Arg *arg);
|
||||
int create_virtual_output(const Arg *arg);
|
||||
int destroy_all_virtual_output(const Arg *arg);
|
||||
int focuslast(const Arg *arg);
|
||||
int toggle_trackpad_enable(const Arg *arg);
|
||||
int setoption(const Arg *arg);
|
||||
int disable_monitor(const Arg *arg);
|
||||
int enable_monitor(const Arg *arg);
|
||||
int toggle_monitor(const Arg *arg);
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,5 @@
|
|||
#include "dwl-ipc.h"
|
||||
#include "ext-workspace.h"
|
||||
#include "foreign-toplevel.h"
|
||||
#include "tablet.h"
|
||||
#include "tearing.h"
|
||||
#include "text-input.h"
|
||||
|
|
@ -112,7 +112,7 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
|
|||
Client *c = NULL, *focused = NULL;
|
||||
struct wlr_keyboard *keyboard;
|
||||
xkb_layout_index_t current;
|
||||
int32_t tagmask, state, numclients, focused_client, tag;
|
||||
int tagmask, state, numclients, focused_client, tag;
|
||||
const char *title, *appid, *symbol;
|
||||
char kb_layout[32];
|
||||
focused = focustop(monitor);
|
||||
|
|
@ -192,7 +192,7 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
|
|||
if (wl_resource_get_version(ipc_output->resource) >=
|
||||
ZDWL_IPC_OUTPUT_V2_LAST_LAYER_SINCE_VERSION) {
|
||||
zdwl_ipc_output_v2_send_last_layer(ipc_output->resource,
|
||||
monitor->last_open_surface);
|
||||
monitor->last_surface_ws_name);
|
||||
}
|
||||
|
||||
if (wl_resource_get_version(ipc_output->resource) >=
|
||||
|
|
@ -238,8 +238,8 @@ 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, false);
|
||||
printstatus(IPC_WATCH_ARRANGGE);
|
||||
arrange(selmon, false);
|
||||
printstatus();
|
||||
}
|
||||
|
||||
void dwl_ipc_output_set_layout(struct wl_client *client,
|
||||
|
|
@ -257,8 +257,8 @@ void dwl_ipc_output_set_layout(struct wl_client *client,
|
|||
|
||||
monitor->pertag->ltidxs[monitor->pertag->curtag] = &layouts[index];
|
||||
clear_fullscreen_and_maximized_state(monitor);
|
||||
arrange(monitor, false, false);
|
||||
printstatus(IPC_WATCH_ARRANGGE);
|
||||
arrange(monitor, false);
|
||||
printstatus();
|
||||
}
|
||||
|
||||
void dwl_ipc_output_set_tags(struct wl_client *client,
|
||||
|
|
@ -287,7 +287,7 @@ void dwl_ipc_output_dispatch(struct wl_client *client,
|
|||
const char *arg3, const char *arg4,
|
||||
const char *arg5) {
|
||||
|
||||
int32_t (*func)(const Arg *);
|
||||
int (*func)(const Arg *);
|
||||
Arg arg;
|
||||
func = parse_func_name((char *)dispatch, &arg, (char *)arg1, (char *)arg2,
|
||||
(char *)arg3, (char *)arg4, (char *)arg5);
|
||||
|
|
|
|||
|
|
@ -7,20 +7,20 @@
|
|||
typedef struct Monitor Monitor;
|
||||
|
||||
struct workspace {
|
||||
struct wl_list link;
|
||||
uint32_t tag;
|
||||
Monitor *m;
|
||||
struct wlr_ext_workspace_handle_v1 *ext_workspace;
|
||||
struct wl_listener commit;
|
||||
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;
|
||||
|
||||
static void handle_ext_commit(struct wl_listener *listener, void *data);
|
||||
static struct wl_listener ext_manager_commit_listener = {.notify =
|
||||
handle_ext_commit};
|
||||
|
||||
void goto_workspace(struct workspace *target) {
|
||||
uint32_t tag;
|
||||
tag = 1 << (target->tag - 1);
|
||||
|
|
@ -43,60 +43,30 @@ void toggle_workspace(struct workspace *target) {
|
|||
}
|
||||
}
|
||||
|
||||
static void handle_ext_commit(struct wl_listener *listener, void *data) {
|
||||
struct wlr_ext_workspace_v1_commit_event *event = data;
|
||||
struct wlr_ext_workspace_v1_request *request;
|
||||
static void handle_ext_workspace_activate(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct workspace *workspace =
|
||||
wl_container_of(listener, workspace, activate);
|
||||
|
||||
wl_list_for_each(request, event->requests, link) {
|
||||
switch (request->type) {
|
||||
case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE: {
|
||||
if (!request->activate.workspace) {
|
||||
break;
|
||||
}
|
||||
|
||||
struct workspace *workspace = NULL;
|
||||
struct workspace *w;
|
||||
wl_list_for_each(w, &workspaces, link) {
|
||||
if (w->ext_workspace == request->activate.workspace) {
|
||||
workspace = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!workspace || workspace->m->isoverview) {
|
||||
break;
|
||||
if (workspace->m->isoverview) {
|
||||
return;
|
||||
}
|
||||
|
||||
goto_workspace(workspace);
|
||||
wlr_log(WLR_INFO, "ext activating workspace %d", workspace->tag);
|
||||
break;
|
||||
}
|
||||
case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE: {
|
||||
if (!request->deactivate.workspace) {
|
||||
break;
|
||||
}
|
||||
|
||||
struct workspace *workspace = NULL;
|
||||
struct workspace *w;
|
||||
wl_list_for_each(w, &workspaces, link) {
|
||||
if (w->ext_workspace == request->deactivate.workspace) {
|
||||
workspace = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
static void handle_ext_workspace_deactivate(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct workspace *workspace =
|
||||
wl_container_of(listener, workspace, deactivate);
|
||||
|
||||
if (!workspace || workspace->m->isoverview) {
|
||||
break;
|
||||
if (workspace->m->isoverview) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggle_workspace(workspace);
|
||||
wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *get_name_from_tag(uint32_t tag) {
|
||||
|
|
@ -106,6 +76,8 @@ static const char *get_name_from_tag(uint32_t tag) {
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
@ -130,7 +102,7 @@ static void remove_workspace_by_tag(uint32_t tag, Monitor *m) {
|
|||
}
|
||||
}
|
||||
|
||||
static void add_workspace_by_tag(int32_t tag, Monitor *m) {
|
||||
static void add_workspace_by_tag(int tag, Monitor *m) {
|
||||
const char *name = get_name_from_tag(tag);
|
||||
|
||||
struct workspace *workspace = ecalloc(1, sizeof(*workspace));
|
||||
|
|
@ -140,12 +112,17 @@ static void add_workspace_by_tag(int32_t tag, Monitor *m) {
|
|||
workspace->m = m;
|
||||
workspace->ext_workspace = wlr_ext_workspace_handle_v1_create(
|
||||
ext_manager, name, EXT_WORKSPACE_ENABLE_CAPS);
|
||||
|
||||
workspace->ext_workspace->data = workspace;
|
||||
|
||||
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) {
|
||||
|
|
@ -185,7 +162,7 @@ void dwl_ext_workspace_printstatus(Monitor *m) {
|
|||
}
|
||||
|
||||
void refresh_monitors_workspaces_status(Monitor *m) {
|
||||
int32_t i;
|
||||
int i;
|
||||
|
||||
if (m->isoverview) {
|
||||
for (i = 1; i <= LENGTH(tags); i++) {
|
||||
|
|
@ -203,9 +180,8 @@ void refresh_monitors_workspaces_status(Monitor *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);
|
||||
|
||||
wl_signal_add(&ext_manager->events.commit, &ext_manager_commit_listener);
|
||||
}
|
||||
|
|
@ -4,15 +4,31 @@ 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);
|
||||
uint32_t target;
|
||||
|
||||
client_active(c);
|
||||
if (c->swallowing)
|
||||
return;
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
target = get_tags_first_tag(c->tags);
|
||||
view_in_mon(&(Arg){.ui = target}, true, c->mon, true);
|
||||
focusclient(c, 1);
|
||||
}
|
||||
|
||||
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 || !c->mon)
|
||||
if (c->swallowing)
|
||||
return;
|
||||
|
||||
if (c->ismaximizescreen && !event->maximized) {
|
||||
|
|
@ -30,7 +46,7 @@ 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 || !c->mon)
|
||||
if (c->swallowing)
|
||||
return;
|
||||
|
||||
if (!c->isminimized && event->minimized) {
|
||||
|
|
@ -44,7 +60,7 @@ void handle_foreign_minimize_request(struct wl_listener *listener, void *data) {
|
|||
c->is_scratchpad_show = 0;
|
||||
setborder_color(c);
|
||||
show_hide_client(c);
|
||||
arrange(c->mon, true, false);
|
||||
arrange(c->mon, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +71,7 @@ void handle_foreign_fullscreen_request(struct wl_listener *listener,
|
|||
Client *c = wl_container_of(listener, c, foreign_fullscreen_request);
|
||||
struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
|
||||
|
||||
if (c->swallowing || !c->mon)
|
||||
if (c->swallowing)
|
||||
return;
|
||||
|
||||
if (c->isfullscreen && !event->fullscreen) {
|
||||
|
|
@ -82,6 +98,10 @@ void handle_foreign_destroy(struct wl_listener *listener, void *data) {
|
|||
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) {
|
||||
wlr_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel);
|
||||
c->foreign_toplevel = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -124,23 +144,7 @@ void add_foreign_toplevel(Client *c) {
|
|||
}
|
||||
}
|
||||
|
||||
void reset_foreign_tolevel(Client *c, Monitor *oldmon, Monitor *newmon) {
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (!c->foreign_toplevel) {
|
||||
void reset_foreign_tolevel(Client *c) {
|
||||
remove_foreign_topleve(c);
|
||||
add_foreign_toplevel(c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldmon == newmon)
|
||||
return;
|
||||
|
||||
if (oldmon)
|
||||
wlr_foreign_toplevel_handle_v1_output_leave(c->foreign_toplevel,
|
||||
oldmon->wlr_output);
|
||||
|
||||
if (newmon)
|
||||
wlr_foreign_toplevel_handle_v1_output_enter(c->foreign_toplevel,
|
||||
newmon->wlr_output);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,458 +0,0 @@
|
|||
#include <wlr/types/wlr_tablet_pad.h>
|
||||
#include <wlr/types/wlr_tablet_tool.h>
|
||||
#include <wlr/types/wlr_tablet_v2.h>
|
||||
|
||||
static void createtablet(struct wlr_input_device *device);
|
||||
static void destroytablet(struct wl_listener *listener, void *data);
|
||||
static void createtabletpad(struct wlr_input_device *device);
|
||||
static void destroytabletpad(struct wl_listener *listener, void *data);
|
||||
static void tabletpadtabletdestroy(struct wl_listener *listener, void *data);
|
||||
static void tabletpadattach(struct wl_listener *listener, void *data);
|
||||
static void destroytabletsurfacenotify(struct wl_listener *listener,
|
||||
void *data);
|
||||
static void destroytablettool(struct wl_listener *listener, void *data);
|
||||
static void tablettoolsetcursor(struct wl_listener *listener, void *data);
|
||||
|
||||
static void tablettoolproximity(struct wl_listener *listener, void *data);
|
||||
static void tablettoolaxis(struct wl_listener *listener, void *data);
|
||||
static void tablettoolbutton(struct wl_listener *listener, void *data);
|
||||
static void tablettooltip(struct wl_listener *listener, void *data);
|
||||
static struct wlr_tablet_manager_v2 *tablet_mgr;
|
||||
|
||||
struct Tablet {
|
||||
struct wlr_tablet_v2_tablet *tablet_v2;
|
||||
struct wl_listener destroy;
|
||||
struct wlr_input_device *device;
|
||||
struct wl_list link;
|
||||
};
|
||||
static struct wl_list tablets;
|
||||
|
||||
struct TabletTool {
|
||||
struct wlr_tablet_v2_tablet_tool *tool_v2;
|
||||
struct Tablet *tablet;
|
||||
struct wlr_surface *curr_surface;
|
||||
struct wl_listener destroy;
|
||||
struct wl_listener surface_destroy;
|
||||
struct wl_listener set_cursor;
|
||||
double tilt_x, tilt_y;
|
||||
};
|
||||
|
||||
struct TabletPad {
|
||||
struct wlr_tablet_v2_tablet_pad *pad_v2;
|
||||
struct wlr_input_device *device;
|
||||
struct Tablet *tablet;
|
||||
struct wl_listener tablet_destroy;
|
||||
struct wl_listener attach;
|
||||
struct wl_listener destroy;
|
||||
struct wl_list link;
|
||||
};
|
||||
static struct wl_list tablet_pads;
|
||||
|
||||
static void attach_tablet_pad(struct TabletPad *tablet_pad,
|
||||
struct Tablet *tablet);
|
||||
static void tablettoolmotion(struct TabletTool *tool, bool change_x,
|
||||
bool change_y, double x, double y, double dx,
|
||||
double dy);
|
||||
|
||||
static struct wl_listener tablet_tool_axis = {.notify = tablettoolaxis};
|
||||
static struct wl_listener tablet_tool_button = {.notify = tablettoolbutton};
|
||||
static struct wl_listener tablet_tool_proximity = {.notify =
|
||||
tablettoolproximity};
|
||||
static struct wl_listener tablet_tool_tip = {.notify = tablettooltip};
|
||||
|
||||
void createtablet(struct wlr_input_device *device) {
|
||||
struct Tablet *tablet = calloc(1, sizeof(struct Tablet));
|
||||
if (!tablet) {
|
||||
wlr_log(WLR_ERROR, "could not allocate tablet");
|
||||
return;
|
||||
}
|
||||
|
||||
struct libinput_device *device_handle = NULL;
|
||||
if (!wlr_input_device_is_libinput(device) ||
|
||||
!(device_handle = wlr_libinput_get_device_handle(device))) {
|
||||
free(tablet);
|
||||
return;
|
||||
}
|
||||
|
||||
tablet->device = device;
|
||||
tablet->tablet_v2 = wlr_tablet_create(tablet_mgr, seat, device);
|
||||
|
||||
if (!tablet->tablet_v2) {
|
||||
free(tablet);
|
||||
return;
|
||||
}
|
||||
|
||||
tablet->tablet_v2->wlr_tablet->data = tablet;
|
||||
tablet->destroy.notify = destroytablet;
|
||||
wl_signal_add(&tablet->device->events.destroy, &tablet->destroy);
|
||||
|
||||
if (libinput_device_config_send_events_get_modes(device_handle)) {
|
||||
libinput_device_config_send_events_set_mode(device_handle,
|
||||
config.send_events_mode);
|
||||
wlr_cursor_attach_input_device(cursor, device);
|
||||
}
|
||||
|
||||
wl_list_insert(&tablets, &tablet->link);
|
||||
|
||||
/* Search for a sibling tablet pad */
|
||||
struct libinput_device_group *group = libinput_device_get_device_group(
|
||||
wlr_libinput_get_device_handle(device));
|
||||
struct TabletPad *tablet_pad;
|
||||
wl_list_for_each(tablet_pad, &tablet_pads, link) {
|
||||
struct wlr_input_device *pad_device = tablet_pad->device;
|
||||
if (!wlr_input_device_is_libinput(pad_device)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct libinput_device_group *pad_group =
|
||||
libinput_device_get_device_group(
|
||||
wlr_libinput_get_device_handle(pad_device));
|
||||
|
||||
if (pad_group == group) {
|
||||
attach_tablet_pad(tablet_pad, tablet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void destroytablet(struct wl_listener *listener, void *data) {
|
||||
struct Tablet *tablet = wl_container_of(listener, tablet, destroy);
|
||||
|
||||
wl_list_remove(&listener->link);
|
||||
wl_list_remove(&tablet->link);
|
||||
free(tablet);
|
||||
}
|
||||
|
||||
void tabletpadtabletdestroy(struct wl_listener *listener, void *data) {
|
||||
struct TabletPad *tablet_pad =
|
||||
wl_container_of(listener, tablet_pad, tablet_destroy);
|
||||
|
||||
tablet_pad->tablet = NULL;
|
||||
|
||||
wl_list_remove(&tablet_pad->tablet_destroy.link);
|
||||
wl_list_init(&tablet_pad->tablet_destroy.link);
|
||||
}
|
||||
|
||||
void attach_tablet_pad(struct TabletPad *tablet_pad, struct Tablet *tablet) {
|
||||
tablet_pad->tablet = tablet;
|
||||
|
||||
wl_list_remove(&tablet_pad->tablet_destroy.link);
|
||||
tablet_pad->tablet_destroy.notify = tabletpadtabletdestroy;
|
||||
wl_signal_add(&tablet->device->events.destroy, &tablet_pad->tablet_destroy);
|
||||
}
|
||||
|
||||
void tabletpadattach(struct wl_listener *listener, void *data) {
|
||||
struct TabletPad *tablet_pad =
|
||||
wl_container_of(listener, tablet_pad, attach);
|
||||
struct wlr_tablet_tool *wlr_tool = data;
|
||||
struct TabletTool *tool = wlr_tool->data;
|
||||
|
||||
if (!tool) {
|
||||
return;
|
||||
}
|
||||
|
||||
attach_tablet_pad(tablet_pad, tool->tablet);
|
||||
}
|
||||
|
||||
void createtabletpad(struct wlr_input_device *device) {
|
||||
struct TabletPad *tablet_pad = calloc(1, sizeof(struct TabletPad));
|
||||
if (!tablet_pad) {
|
||||
wlr_log(WLR_ERROR, "could not allocate tablet_pad");
|
||||
return;
|
||||
}
|
||||
|
||||
tablet_pad->device = device;
|
||||
tablet_pad->pad_v2 = wlr_tablet_pad_create(tablet_mgr, seat, device);
|
||||
|
||||
if (!tablet_pad->pad_v2) {
|
||||
wlr_log(WLR_ERROR, "could not create tablet_pad_v2 wrapper");
|
||||
free(tablet_pad);
|
||||
return;
|
||||
}
|
||||
|
||||
tablet_pad->destroy.notify = destroytabletpad;
|
||||
tablet_pad->attach.notify = tabletpadattach;
|
||||
wl_list_init(&tablet_pad->tablet_destroy.link);
|
||||
|
||||
wl_signal_add(&device->events.destroy, &tablet_pad->destroy);
|
||||
|
||||
wl_signal_add(&tablet_pad->pad_v2->wlr_pad->events.attach_tablet,
|
||||
&tablet_pad->attach);
|
||||
wl_list_insert(&tablet_pads, &tablet_pad->link);
|
||||
|
||||
/* Search for a sibling tablet */
|
||||
if (!wlr_input_device_is_libinput(device)) {
|
||||
/* We can only do this on libinput devices */
|
||||
return;
|
||||
}
|
||||
|
||||
struct libinput_device_group *group = libinput_device_get_device_group(
|
||||
wlr_libinput_get_device_handle(device));
|
||||
|
||||
struct Tablet *tablet;
|
||||
wl_list_for_each(tablet, &tablets, link) {
|
||||
struct wlr_input_device *tablet_device = tablet->device;
|
||||
if (!wlr_input_device_is_libinput(tablet_device)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct libinput_device_group *tablet_group =
|
||||
libinput_device_get_device_group(
|
||||
wlr_libinput_get_device_handle(tablet_device));
|
||||
|
||||
if (tablet_group == group) {
|
||||
attach_tablet_pad(tablet_pad, tablet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void destroytabletpad(struct wl_listener *listener, void *data) {
|
||||
struct TabletPad *tablet_pad =
|
||||
wl_container_of(listener, tablet_pad, destroy);
|
||||
|
||||
wl_list_remove(&listener->link);
|
||||
wl_list_remove(&tablet_pad->link);
|
||||
wl_list_remove(&tablet_pad->tablet_destroy.link);
|
||||
wl_list_remove(&tablet_pad->attach.link);
|
||||
free(tablet_pad);
|
||||
}
|
||||
|
||||
void destroytabletsurfacenotify(struct wl_listener *listener, void *data) {
|
||||
struct TabletTool *tool = wl_container_of(listener, tool, surface_destroy);
|
||||
wl_list_remove(&tool->surface_destroy.link);
|
||||
tool->curr_surface = NULL;
|
||||
}
|
||||
|
||||
void destroytablettool(struct wl_listener *listener, void *data) {
|
||||
struct TabletTool *tool = wl_container_of(listener, tool, destroy);
|
||||
|
||||
if (tool->curr_surface)
|
||||
wl_list_remove(&tool->surface_destroy.link);
|
||||
wl_list_remove(&tool->set_cursor.link);
|
||||
wl_list_remove(&listener->link);
|
||||
free(tool);
|
||||
}
|
||||
|
||||
static void tablettoolsetcursor(struct wl_listener *listener, void *data) {
|
||||
struct TabletTool *tool = wl_container_of(listener, tool, set_cursor);
|
||||
struct wlr_tablet_v2_event_cursor *event = data;
|
||||
|
||||
struct wlr_seat_client *focused_client = NULL;
|
||||
if (tool->tool_v2->focused_surface) {
|
||||
focused_client = wlr_seat_client_for_wl_client(
|
||||
seat,
|
||||
wl_resource_get_client(tool->tool_v2->focused_surface->resource));
|
||||
}
|
||||
|
||||
if (focused_client != event->seat_client)
|
||||
return;
|
||||
|
||||
wlr_cursor_set_surface(cursor, event->surface, event->hotspot_x,
|
||||
event->hotspot_y);
|
||||
}
|
||||
|
||||
void tablettoolmotion(struct TabletTool *tool, bool change_x, bool change_y,
|
||||
double x, double y, double dx, double dy) {
|
||||
struct wlr_surface *surface = NULL;
|
||||
Client *c = NULL, *w = NULL;
|
||||
LayerSurface *l = NULL;
|
||||
struct Tablet *tablet = tool->tablet;
|
||||
struct TabletPad *tablet_pad;
|
||||
double sx, sy;
|
||||
|
||||
if (!change_x && !change_y)
|
||||
return;
|
||||
|
||||
// TODO: apply constraints
|
||||
switch (tool->tool_v2->wlr_tool->type) {
|
||||
case WLR_TABLET_TOOL_TYPE_LENS:
|
||||
case WLR_TABLET_TOOL_TYPE_MOUSE:
|
||||
wlr_cursor_move(cursor, tablet->device, dx, dy);
|
||||
break;
|
||||
default:
|
||||
wlr_cursor_warp_absolute(cursor, tablet->device, change_x ? x : NAN,
|
||||
change_y ? y : NAN);
|
||||
break;
|
||||
}
|
||||
|
||||
motionnotify(0, NULL, 0, 0, 0, 0);
|
||||
|
||||
if (config.sloppyfocus)
|
||||
selmon = xytomon(cursor->x, cursor->y);
|
||||
|
||||
xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy);
|
||||
if (cursor_mode == CurPressed && !seat->drag &&
|
||||
surface != seat->pointer_state.focused_surface &&
|
||||
toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w,
|
||||
&l) >= 0) {
|
||||
c = w;
|
||||
surface = seat->pointer_state.focused_surface;
|
||||
sx = cursor->x - (l ? l->scene->node.x : w->geom.x);
|
||||
sy = cursor->y - (l ? l->scene->node.y : w->geom.y);
|
||||
}
|
||||
|
||||
if (config.sloppyfocus && c && c->scene->node.enabled &&
|
||||
(surface != seat->pointer_state.focused_surface ||
|
||||
(selmon && selmon->sel && c != selmon->sel)) &&
|
||||
!client_is_unmanaged(c))
|
||||
focusclient(c, 0);
|
||||
|
||||
if (surface && !wlr_surface_accepts_tablet_v2(surface, tablet->tablet_v2))
|
||||
surface = NULL;
|
||||
|
||||
if (surface != tool->curr_surface) {
|
||||
if (tool->curr_surface) {
|
||||
// TODO: wait until all buttons released before leaving
|
||||
wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tool_v2);
|
||||
wl_list_for_each(tablet_pad, &tablet_pads, link) {
|
||||
if (tablet_pad->tablet && tablet_pad->tablet == tablet)
|
||||
wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->pad_v2,
|
||||
tool->curr_surface);
|
||||
}
|
||||
wl_list_remove(&tool->surface_destroy.link);
|
||||
}
|
||||
if (surface) {
|
||||
wl_list_for_each(tablet_pad, &tablet_pads, link) {
|
||||
if (tablet_pad->tablet && tablet_pad->tablet == tablet)
|
||||
wlr_tablet_v2_tablet_pad_notify_enter(
|
||||
tablet_pad->pad_v2, tablet->tablet_v2, surface);
|
||||
}
|
||||
wlr_tablet_v2_tablet_tool_notify_proximity_in(
|
||||
tool->tool_v2, tablet->tablet_v2, surface);
|
||||
wl_signal_add(&surface->events.destroy, &tool->surface_destroy);
|
||||
}
|
||||
tool->curr_surface = surface;
|
||||
}
|
||||
|
||||
if (surface)
|
||||
wlr_tablet_v2_tablet_tool_notify_motion(tool->tool_v2, sx, sy);
|
||||
|
||||
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
|
||||
handlecursoractivity();
|
||||
}
|
||||
|
||||
void tablettoolproximity(struct wl_listener *listener, void *data) {
|
||||
struct wlr_tablet_tool_proximity_event *event = data;
|
||||
struct wlr_tablet_tool *wlr_tool = event->tool;
|
||||
struct TabletTool *tool = wlr_tool->data;
|
||||
Monitor *m_iter;
|
||||
|
||||
if (!tool) {
|
||||
tool = calloc(1, sizeof(struct TabletTool));
|
||||
if (!tool) {
|
||||
wlr_log(WLR_ERROR, "could not allocate tablet_tool");
|
||||
return;
|
||||
}
|
||||
tool->tool_v2 = wlr_tablet_tool_create(tablet_mgr, seat, wlr_tool);
|
||||
tool->surface_destroy.notify = destroytabletsurfacenotify;
|
||||
tool->destroy.notify = destroytablettool;
|
||||
tool->set_cursor.notify = tablettoolsetcursor;
|
||||
tool->tablet = event->tablet->data;
|
||||
wlr_tool->data = tool;
|
||||
wl_signal_add(&tool->tool_v2->wlr_tool->events.destroy, &tool->destroy);
|
||||
wl_signal_add(&tool->tool_v2->events.set_cursor, &tool->set_cursor);
|
||||
|
||||
if (config.tablet_map_to_mon) {
|
||||
wl_list_for_each(m_iter, &mons, link) {
|
||||
if (match_monitor_spec(config.tablet_map_to_mon, m_iter)) {
|
||||
wlr_log(WLR_DEBUG, "Mapping tablet %s to output %s",
|
||||
event->tablet->base.name, config.tablet_map_to_mon);
|
||||
wlr_cursor_map_input_to_output(cursor, &event->tablet->base,
|
||||
m_iter->wlr_output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (event->state) {
|
||||
case WLR_TABLET_TOOL_PROXIMITY_OUT:
|
||||
wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tool_v2);
|
||||
if (tool->curr_surface)
|
||||
wl_list_remove(&tool->surface_destroy.link);
|
||||
tool->curr_surface = NULL;
|
||||
break;
|
||||
case WLR_TABLET_TOOL_PROXIMITY_IN:
|
||||
tablettoolmotion(tool, true, true, event->x, event->y, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void tablettoolaxis(struct wl_listener *listener, void *data) {
|
||||
struct wlr_tablet_tool_axis_event *event = data;
|
||||
struct TabletTool *tool = event->tool->data;
|
||||
if (!tool)
|
||||
return;
|
||||
|
||||
tablettoolmotion(tool, event->updated_axes & WLR_TABLET_TOOL_AXIS_X,
|
||||
event->updated_axes & WLR_TABLET_TOOL_AXIS_Y, event->x,
|
||||
event->y, event->dx, event->dy);
|
||||
|
||||
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
|
||||
wlr_tablet_v2_tablet_tool_notify_pressure(tool->tool_v2,
|
||||
event->pressure);
|
||||
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
|
||||
wlr_tablet_v2_tablet_tool_notify_distance(tool->tool_v2,
|
||||
event->distance);
|
||||
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X)
|
||||
tool->tilt_x = event->tilt_x;
|
||||
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y)
|
||||
tool->tilt_y = event->tilt_y;
|
||||
if (event->updated_axes &
|
||||
(WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) {
|
||||
wlr_tablet_v2_tablet_tool_notify_tilt(tool->tool_v2, tool->tilt_x,
|
||||
tool->tilt_y);
|
||||
}
|
||||
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
|
||||
wlr_tablet_v2_tablet_tool_notify_rotation(tool->tool_v2,
|
||||
event->rotation);
|
||||
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
|
||||
wlr_tablet_v2_tablet_tool_notify_slider(tool->tool_v2, event->slider);
|
||||
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
|
||||
wlr_tablet_v2_tablet_tool_notify_wheel(tool->tool_v2,
|
||||
event->wheel_delta, 0);
|
||||
}
|
||||
|
||||
void tablettoolbutton(struct wl_listener *listener, void *data) {
|
||||
struct wlr_tablet_tool_button_event *event = data;
|
||||
struct TabletTool *tool = event->tool->data;
|
||||
if (!tool)
|
||||
return;
|
||||
wlr_tablet_v2_tablet_tool_notify_button(
|
||||
tool->tool_v2, event->button,
|
||||
(enum zwp_tablet_pad_v2_button_state)event->state);
|
||||
}
|
||||
|
||||
void tablettooltip(struct wl_listener *listener, void *data) {
|
||||
struct wlr_tablet_tool_tip_event *event = data;
|
||||
struct TabletTool *tool = event->tool->data;
|
||||
if (!tool)
|
||||
return;
|
||||
|
||||
struct wlr_pointer_button_event fakeptrbtnevent = {
|
||||
.button = BTN_LEFT,
|
||||
.state = event->state == WLR_TABLET_TOOL_TIP_UP
|
||||
? WL_POINTER_BUTTON_STATE_RELEASED
|
||||
: WL_POINTER_BUTTON_STATE_PRESSED,
|
||||
.time_msec = event->time_msec,
|
||||
};
|
||||
|
||||
if (handle_buttonpress(&fakeptrbtnevent))
|
||||
return;
|
||||
|
||||
if (!tool->curr_surface) {
|
||||
wlr_seat_pointer_notify_button(seat, fakeptrbtnevent.time_msec,
|
||||
fakeptrbtnevent.button,
|
||||
fakeptrbtnevent.state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->state == WLR_TABLET_TOOL_TIP_UP) {
|
||||
wlr_tablet_v2_tablet_tool_notify_up(tool->tool_v2);
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_tablet_v2_tablet_tool_notify_down(tool->tool_v2);
|
||||
wlr_tablet_tool_v2_start_implicit_grab(tool->tool_v2);
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ void handle_tearing_new_object(struct wl_listener *listener, void *data) {
|
|||
|
||||
bool check_tearing_frame_allow(Monitor *m) {
|
||||
/* never allow tearing when disabled */
|
||||
if (!config.allow_tearing) {
|
||||
if (!allow_tearing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ bool check_tearing_frame_allow(Monitor *m) {
|
|||
}
|
||||
|
||||
/* allow tearing for any window when requested or forced */
|
||||
if (config.allow_tearing == TEARING_ENABLED) {
|
||||
if (allow_tearing == TEARING_ENABLED) {
|
||||
if (c->force_tearing == STATE_UNSPECIFIED) {
|
||||
return c->tearing_hint;
|
||||
} else {
|
||||
|
|
@ -87,8 +87,7 @@ bool check_tearing_frame_allow(Monitor *m) {
|
|||
|
||||
if (c->force_tearing == STATE_UNSPECIFIED) {
|
||||
/* honor the tearing hint or the fullscreen-force preference */
|
||||
return c->tearing_hint ||
|
||||
config.allow_tearing == TEARING_FULLSCREEN_ONLY;
|
||||
return c->tearing_hint || allow_tearing == TEARING_FULLSCREEN_ONLY;
|
||||
}
|
||||
|
||||
/* honor tearing as requested by action */
|
||||
|
|
|
|||
|
|
@ -77,6 +77,15 @@ 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
|
||||
|
|
@ -216,7 +225,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
|
|||
Monitor *output = NULL;
|
||||
struct wlr_xdg_positioner_rules pointer_rules;
|
||||
struct wlr_box output_box;
|
||||
int32_t lx, ly;
|
||||
int lx, ly;
|
||||
struct wlr_box popup_box;
|
||||
|
||||
if (!text_input || !relay->focused_surface ||
|
||||
|
|
@ -246,7 +255,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
|
|||
cursor_rect = (struct wlr_box){0};
|
||||
}
|
||||
|
||||
output = get_monitor_nearest_to(cursor_rect.x, cursor_rect.y);
|
||||
output = output_nearest_to(cursor_rect.x, cursor_rect.y);
|
||||
if (!output_is_usable(output)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// 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>
|
||||
|
|
@ -7,6 +11,27 @@
|
|||
|
||||
#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;
|
||||
|
|
@ -92,7 +117,7 @@ static void workspace_handle_activate(struct wl_client *client,
|
|||
return;
|
||||
}
|
||||
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE;
|
||||
req->activate.workspace = workspace_res->workspace;
|
||||
req->workspace = workspace_res->workspace;
|
||||
wl_list_insert(workspace_res->manager->requests.prev, &req->link);
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +136,7 @@ workspace_handle_deactivate(struct wl_client *client,
|
|||
return;
|
||||
}
|
||||
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE;
|
||||
req->deactivate.workspace = workspace_res->workspace;
|
||||
req->workspace = workspace_res->workspace;
|
||||
wl_list_insert(workspace_res->manager->requests.prev, &req->link);
|
||||
}
|
||||
|
||||
|
|
@ -132,8 +157,8 @@ static void workspace_handle_assign(struct wl_client *client,
|
|||
return;
|
||||
}
|
||||
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN;
|
||||
req->assign.group = group_res->group;
|
||||
req->assign.workspace = workspace_res->workspace;
|
||||
req->group = group_res->group;
|
||||
req->workspace = workspace_res->workspace;
|
||||
wl_list_insert(workspace_res->manager->requests.prev, &req->link);
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +176,7 @@ static void workspace_handle_remove(struct wl_client *client,
|
|||
return;
|
||||
}
|
||||
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE;
|
||||
req->remove.workspace = workspace_res->workspace;
|
||||
req->workspace = workspace_res->workspace;
|
||||
wl_list_insert(workspace_res->manager->requests.prev, &req->link);
|
||||
}
|
||||
|
||||
|
|
@ -177,14 +202,14 @@ static void group_handle_create_workspace(struct wl_client *client,
|
|||
wl_resource_post_no_memory(group_resource);
|
||||
return;
|
||||
}
|
||||
req->create_workspace.name = strdup(name);
|
||||
if (!req->create_workspace.name) {
|
||||
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->create_workspace.group = group_res->group;
|
||||
req->group = group_res->group;
|
||||
wl_list_insert(group_res->manager->requests.prev, &req->link);
|
||||
}
|
||||
|
||||
|
|
@ -288,56 +313,11 @@ static struct wlr_ext_workspace_group_v1_resource *create_group_resource(
|
|||
return group_res;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_requests(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) {
|
||||
if (req->type == WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE) {
|
||||
free(req->create_workspace.name);
|
||||
}
|
||||
static void destroy_request(struct wlr_ext_workspace_v1_request *req) {
|
||||
wl_list_remove(&req->link);
|
||||
free(req->name);
|
||||
free(req);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clear_requests_by(struct wlr_ext_workspace_manager_v1_resource *manager_res,
|
||||
struct wlr_ext_workspace_group_handle_v1 *group,
|
||||
struct wlr_ext_workspace_handle_v1 *workspace) {
|
||||
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:
|
||||
if (group && req->create_workspace.group == group) {
|
||||
req->create_workspace.group = NULL;
|
||||
}
|
||||
break;
|
||||
case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE:
|
||||
if (workspace && req->activate.workspace == workspace) {
|
||||
req->activate.workspace = NULL;
|
||||
}
|
||||
break;
|
||||
case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE:
|
||||
if (workspace && req->deactivate.workspace == workspace) {
|
||||
req->deactivate.workspace = NULL;
|
||||
}
|
||||
break;
|
||||
case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN:
|
||||
if (workspace && req->assign.workspace == workspace) {
|
||||
req->assign.workspace = NULL;
|
||||
}
|
||||
if (group && req->assign.group == group) {
|
||||
req->assign.group = NULL;
|
||||
}
|
||||
break;
|
||||
case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE:
|
||||
if (workspace && req->remove.workspace == workspace) {
|
||||
req->remove.workspace = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void manager_handle_commit(struct wl_client *client,
|
||||
struct wl_resource *resource) {
|
||||
|
|
@ -347,11 +327,32 @@ static void manager_handle_commit(struct wl_client *client,
|
|||
return;
|
||||
}
|
||||
|
||||
struct wlr_ext_workspace_v1_commit_event commit_event = {
|
||||
.requests = &manager_res->requests,
|
||||
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(&manager_res->manager->events.commit, &commit_event);
|
||||
destroy_requests(manager_res);
|
||||
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) {
|
||||
|
|
@ -405,8 +406,10 @@ static const struct ext_workspace_manager_v1_interface manager_impl = {
|
|||
|
||||
static void destroy_manager_resource(
|
||||
struct wlr_ext_workspace_manager_v1_resource *manager_res) {
|
||||
destroy_requests(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,
|
||||
|
|
@ -528,7 +531,6 @@ static void manager_handle_display_destroy(struct wl_listener *listener,
|
|||
wl_container_of(listener, manager, display_destroy);
|
||||
|
||||
wl_signal_emit_mutable(&manager->events.destroy, NULL);
|
||||
assert(wl_list_empty(&manager->events.commit.listener_list));
|
||||
assert(wl_list_empty(&manager->events.destroy.listener_list));
|
||||
|
||||
struct wlr_ext_workspace_group_handle_v1 *group, *tmp;
|
||||
|
|
@ -581,7 +583,6 @@ wlr_ext_workspace_manager_v1_create(struct wl_display *display,
|
|||
wl_list_init(&manager->groups);
|
||||
wl_list_init(&manager->workspaces);
|
||||
wl_list_init(&manager->resources);
|
||||
wl_signal_init(&manager->events.commit);
|
||||
wl_signal_init(&manager->events.destroy);
|
||||
|
||||
return manager;
|
||||
|
|
@ -600,6 +601,7 @@ wlr_ext_workspace_group_handle_v1_create(
|
|||
|
||||
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);
|
||||
|
|
@ -687,6 +689,7 @@ void wlr_ext_workspace_group_handle_v1_destroy(
|
|||
|
||||
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;
|
||||
|
|
@ -705,7 +708,12 @@ void wlr_ext_workspace_group_handle_v1_destroy(
|
|||
|
||||
struct wlr_ext_workspace_manager_v1_resource *manager_res;
|
||||
wl_list_for_each(manager_res, &group->manager->resources, link) {
|
||||
clear_requests_by(manager_res, group, NULL);
|
||||
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;
|
||||
|
|
@ -814,9 +822,13 @@ wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager,
|
|||
|
||||
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.prev, &workspace->link);
|
||||
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) {
|
||||
|
|
@ -843,6 +855,10 @@ void wlr_ext_workspace_handle_v1_destroy(
|
|||
|
||||
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) {
|
||||
|
|
@ -857,7 +873,12 @@ void wlr_ext_workspace_handle_v1_destroy(
|
|||
|
||||
struct wlr_ext_workspace_manager_v1_resource *manager_res;
|
||||
wl_list_for_each(manager_res, &workspace->manager->resources, link) {
|
||||
clear_requests_by(manager_res, NULL, workspace);
|
||||
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);
|
||||
|
|
@ -908,22 +929,23 @@ void wlr_ext_workspace_handle_v1_set_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, const uint32_t *coords,
|
||||
size_t coords_len) {
|
||||
size_t size = coords_len * sizeof(coords[0]);
|
||||
if (size == workspace->coordinates.size &&
|
||||
(size == 0 || memcmp(workspace->coordinates.data, coords, size) == 0)) {
|
||||
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);
|
||||
struct wl_array arr = {
|
||||
.data = (void *)coords,
|
||||
.size = size,
|
||||
};
|
||||
wl_array_copy(&workspace->coordinates, &arr);
|
||||
wl_array_copy(&workspace->coordinates, coordinates);
|
||||
|
||||
struct wlr_ext_workspace_v1_resource *workspace_res;
|
||||
wl_list_for_each(workspace_res, &workspace->resources, link) {
|
||||
|
|
|
|||
|
|
@ -1,69 +1,21 @@
|
|||
/*
|
||||
* This an unstable interface of wlroots. No guarantees are made regarding the
|
||||
* future consistency of this API.
|
||||
*/
|
||||
#ifndef WLR_USE_UNSTABLE
|
||||
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
|
||||
#endif
|
||||
|
||||
#ifndef WLR_TYPES_WLR_EXT_WORKSPACE_V1_H
|
||||
#define WLR_TYPES_WLR_EXT_WORKSPACE_V1_H
|
||||
// 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;
|
||||
|
||||
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;
|
||||
struct wl_list link; // wlr_ext_workspace_manager_v1_resource.requests
|
||||
union {
|
||||
struct {
|
||||
char *name;
|
||||
struct wlr_ext_workspace_group_handle_v1
|
||||
*group; // NULL if destroyed
|
||||
} create_workspace;
|
||||
struct {
|
||||
struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed
|
||||
} activate;
|
||||
struct {
|
||||
struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed
|
||||
} deactivate;
|
||||
struct {
|
||||
struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed
|
||||
struct wlr_ext_workspace_group_handle_v1
|
||||
*group; // NULL if destroyed
|
||||
} assign;
|
||||
struct {
|
||||
struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed
|
||||
} remove;
|
||||
};
|
||||
};
|
||||
|
||||
struct wlr_ext_workspace_v1_commit_event {
|
||||
struct wl_list *requests; // wlr_ext_workspace_v1_request.link
|
||||
};
|
||||
|
||||
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 commit; // wlr_ext_workspace_v1_commit_event
|
||||
struct wl_signal destroy;
|
||||
} events;
|
||||
|
||||
void *data;
|
||||
|
||||
struct {
|
||||
struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link
|
||||
struct wl_event_source *idle_source;
|
||||
|
|
@ -72,17 +24,21 @@ struct wlr_ext_workspace_manager_v1 {
|
|||
};
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
void *data;
|
||||
|
||||
struct {
|
||||
struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link
|
||||
struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link
|
||||
|
|
@ -99,13 +55,15 @@ struct wlr_ext_workspace_handle_v1 {
|
|||
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
|
||||
|
||||
void *data;
|
||||
|
||||
struct {
|
||||
struct wl_list resources; // wlr_ext_workspace_v1_resource.link
|
||||
};
|
||||
|
|
@ -138,13 +96,11 @@ void wlr_ext_workspace_handle_v1_set_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, const uint32_t *coords,
|
||||
size_t coords_len);
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,29 +1,22 @@
|
|||
bool check_hit_no_border(Client *c) {
|
||||
int i;
|
||||
bool hit_no_border = false;
|
||||
|
||||
if (!c->mon)
|
||||
return false;
|
||||
|
||||
if (c->tags <= 0)
|
||||
return false;
|
||||
|
||||
if (!render_border) {
|
||||
hit_no_border = true;
|
||||
}
|
||||
|
||||
if (c->mon && !c->mon->isoverview &&
|
||||
c->mon->pertag->no_render_border[get_tags_first_tag_num(c->tags)]) {
|
||||
for (i = 0; i < config.tag_rules_count; i++) {
|
||||
if (c->tags & (1 << (config.tag_rules[i].id - 1)) &&
|
||||
config.tag_rules[i].no_render_border) {
|
||||
hit_no_border = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.no_border_when_single && c && c->mon &&
|
||||
((ISSCROLLTILED(c) && c->mon->visible_scroll_tiling_clients == 1) ||
|
||||
c->mon->visible_clients == 1)) {
|
||||
if (no_border_when_single && c && c->mon && c->mon->visible_clients == 1) {
|
||||
hit_no_border = true;
|
||||
}
|
||||
return hit_no_border;
|
||||
}
|
||||
|
||||
Client *termforwin(Client *w) {
|
||||
Client *c = NULL;
|
||||
|
||||
|
|
@ -44,7 +37,7 @@ 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 (!config.scratchpad_cross_monitor && c->mon != selmon) {
|
||||
if (!scratchpad_cross_monitor && c->mon != selmon) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -81,19 +74,16 @@ 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, Monitor *tm, struct wlr_box geom,
|
||||
int32_t offsetx, int32_t offsety) {
|
||||
setclient_coordinate_center(Client *c, struct wlr_box geom, int offsetx,
|
||||
int offsety) {
|
||||
struct wlr_box tempbox;
|
||||
int32_t offset = 0;
|
||||
int32_t len = 0;
|
||||
Monitor *m = tm ? tm : selmon;
|
||||
int offset = 0;
|
||||
int len = 0;
|
||||
Monitor *m = c->mon ? c->mon : selmon;
|
||||
|
||||
if (!m)
|
||||
return geom;
|
||||
uint32_t cbw = check_hit_no_border(c) ? c->bw : 0;
|
||||
|
||||
uint32_t cbw = c && check_hit_no_border(c) ? c->bw : 0;
|
||||
|
||||
if ((!c || !c->no_force_center) && m) {
|
||||
if (!c->no_force_center) {
|
||||
tempbox.x = m->w.x + (m->w.width - geom.width) / 2;
|
||||
tempbox.y = m->w.y + (m->w.height - geom.height) / 2;
|
||||
} else {
|
||||
|
|
@ -145,9 +135,9 @@ static bool is_window_rule_matches(const ConfigWinRule *r, const char *appid,
|
|||
Client *center_tiled_select(Monitor *m) {
|
||||
Client *c = NULL;
|
||||
Client *target_c = NULL;
|
||||
int64_t mini_distance = -1;
|
||||
int32_t dirx, diry;
|
||||
int64_t distance;
|
||||
long int mini_distance = -1;
|
||||
int dirx, diry;
|
||||
long int distance;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c && VISIBLEON(c, m) && ISSCROLLTILED(c) &&
|
||||
client_surface(c)->mapped && !c->isfloating &&
|
||||
|
|
@ -167,12 +157,12 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
|
|||
bool ignore_align) {
|
||||
Client *c = NULL;
|
||||
Client **tempClients = NULL; // 初始化为 NULL
|
||||
int32_t last = -1;
|
||||
int last = -1;
|
||||
|
||||
// 第一次遍历,计算客户端数量
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c && (findfloating || !c->isfloating) && !c->isunglobal &&
|
||||
(config.focus_cross_monitor || c->mon == tc->mon) &&
|
||||
(focus_cross_monitor || c->mon == tc->mon) &&
|
||||
(c->tags & c->mon->tagset[c->mon->seltags])) {
|
||||
last++;
|
||||
}
|
||||
|
|
@ -193,30 +183,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 &&
|
||||
(config.focus_cross_monitor || c->mon == tc->mon) &&
|
||||
(focus_cross_monitor || c->mon == tc->mon) &&
|
||||
(c->tags & c->mon->tagset[c->mon->seltags])) {
|
||||
last++;
|
||||
tempClients[last] = c;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int sel_x = tc->geom.x;
|
||||
int sel_y = tc->geom.y;
|
||||
long long int distance = LLONG_MAX;
|
||||
long long int same_monitor_distance = LLONG_MAX;
|
||||
Client *tempFocusClients = NULL;
|
||||
Client *tempSameMonitorFocusClients = NULL;
|
||||
|
||||
switch (arg->i) {
|
||||
case UP:
|
||||
if (!ignore_align) {
|
||||
for (int32_t _i = 0; _i <= last; _i++) {
|
||||
for (int _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 =
|
||||
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;
|
||||
|
|
@ -226,32 +216,11 @@ 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++) {
|
||||
for (int _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 =
|
||||
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;
|
||||
|
|
@ -268,13 +237,13 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
|
|||
break;
|
||||
case DOWN:
|
||||
if (!ignore_align) {
|
||||
for (int32_t _i = 0; _i <= last; _i++) {
|
||||
for (int _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 =
|
||||
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;
|
||||
|
|
@ -284,32 +253,11 @@ 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++) {
|
||||
for (int _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 =
|
||||
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;
|
||||
|
|
@ -326,13 +274,13 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
|
|||
break;
|
||||
case LEFT:
|
||||
if (!ignore_align) {
|
||||
for (int32_t _i = 0; _i <= last; _i++) {
|
||||
for (int _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 =
|
||||
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;
|
||||
|
|
@ -342,32 +290,11 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
|
|||
}
|
||||
}
|
||||
if (!tempFocusClients) {
|
||||
for (int32_t _i = 0; _i <= last; _i++) {
|
||||
if (tempClients[_i]->geom.x < sel_x &&
|
||||
tempClients[_i]->mon == tc->mon &&
|
||||
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
|
||||
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
|
||||
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
|
||||
int64_t tmp_distance =
|
||||
dis_x * dis_x + dis_y * dis_y; // 计算距离
|
||||
if (tmp_distance < distance) {
|
||||
distance = tmp_distance;
|
||||
tempFocusClients = tempClients[_i];
|
||||
}
|
||||
if (tempClients[_i]->mon == tc->mon &&
|
||||
tmp_distance < same_monitor_distance) {
|
||||
same_monitor_distance = tmp_distance;
|
||||
tempSameMonitorFocusClients = tempClients[_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tempFocusClients) {
|
||||
for (int32_t _i = 0; _i <= last; _i++) {
|
||||
for (int _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 =
|
||||
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;
|
||||
|
|
@ -384,13 +311,13 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
|
|||
break;
|
||||
case RIGHT:
|
||||
if (!ignore_align) {
|
||||
for (int32_t _i = 0; _i <= last; _i++) {
|
||||
for (int _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 =
|
||||
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;
|
||||
|
|
@ -400,32 +327,11 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
|
|||
}
|
||||
}
|
||||
if (!tempFocusClients) {
|
||||
for (int32_t _i = 0; _i <= last; _i++) {
|
||||
if (tempClients[_i]->geom.x > sel_x &&
|
||||
tempClients[_i]->mon == tc->mon &&
|
||||
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
|
||||
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
|
||||
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
|
||||
int64_t tmp_distance =
|
||||
dis_x * dis_x + dis_y * dis_y; // 计算距离
|
||||
if (tmp_distance < distance) {
|
||||
distance = tmp_distance;
|
||||
tempFocusClients = tempClients[_i];
|
||||
}
|
||||
if (tempClients[_i]->mon == tc->mon &&
|
||||
tmp_distance < same_monitor_distance) {
|
||||
same_monitor_distance = tmp_distance;
|
||||
tempSameMonitorFocusClients = tempClients[_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tempFocusClients) {
|
||||
for (int32_t _i = 0; _i <= last; _i++) {
|
||||
for (int _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 =
|
||||
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;
|
||||
|
|
@ -452,7 +358,7 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
|
|||
|
||||
Client *direction_select(const Arg *arg) {
|
||||
|
||||
Client *tc = arg->tc ? arg->tc : selmon->sel;
|
||||
Client *tc = selmon->sel;
|
||||
|
||||
if (!tc)
|
||||
return NULL;
|
||||
|
|
@ -463,9 +369,7 @@ Client *direction_select(const Arg *arg) {
|
|||
}
|
||||
|
||||
return find_client_by_direction(
|
||||
tc, arg, true,
|
||||
(is_scroller_layout(selmon) || is_centertile_layout(selmon)) &&
|
||||
!selmon->isoverview);
|
||||
tc, arg, true, is_scroller_layout(selmon) && !selmon->isoverview);
|
||||
}
|
||||
|
||||
/* We probably should change the name of this, it sounds like
|
||||
|
|
@ -492,9 +396,6 @@ Client *get_next_stack_client(Client *c, bool reverse) {
|
|||
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;
|
||||
}
|
||||
|
|
@ -503,9 +404,6 @@ Client *get_next_stack_client(Client *c, bool reverse) {
|
|||
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;
|
||||
}
|
||||
|
|
@ -516,104 +414,20 @@ Client *get_next_stack_client(Client *c, bool reverse) {
|
|||
float *get_border_color(Client *c) {
|
||||
|
||||
if (c->mon != selmon) {
|
||||
return config.bordercolor;
|
||||
return bordercolor;
|
||||
} else if (c->isurgent) {
|
||||
return config.urgentcolor;
|
||||
return urgentcolor;
|
||||
} else if (c->is_in_scratchpad && selmon && c == selmon->sel) {
|
||||
return config.scratchpadcolor;
|
||||
return scratchpadcolor;
|
||||
} else if (c->isglobal && selmon && c == selmon->sel) {
|
||||
return config.globalcolor;
|
||||
return globalcolor;
|
||||
} else if (c->isoverlay && selmon && c == selmon->sel) {
|
||||
return config.overlaycolor;
|
||||
return overlaycolor;
|
||||
} else if (c->ismaximizescreen && selmon && c == selmon->sel) {
|
||||
return config.maximizescreencolor;
|
||||
return maximizescreencolor;
|
||||
} else if (selmon && c == selmon->sel) {
|
||||
return config.focuscolor;
|
||||
return focuscolor;
|
||||
} else {
|
||||
return config.bordercolor;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
return false;
|
||||
|
||||
if (id == SCROLLER || id == VERTICAL_SCROLLER) {
|
||||
Client *source_stack_head = scroll_get_stack_head_client(sc);
|
||||
Client *target_stack_head = scroll_get_stack_head_client(tc);
|
||||
Client *fc_head = fc ? scroll_get_stack_head_client(fc) : NULL;
|
||||
|
||||
if (fc && 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 == 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, Client *custom_focus_client) {
|
||||
if (!sc || sc->isfloating)
|
||||
return sc;
|
||||
|
||||
Client *tc = NULL;
|
||||
Client *fc = custom_focus_client ? custom_focus_client : 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;
|
||||
}
|
||||
|
|
@ -19,11 +19,11 @@ pid_t getparentprocess(pid_t p) {
|
|||
return (pid_t)v;
|
||||
}
|
||||
|
||||
int32_t isdescprocess(pid_t p, pid_t c) {
|
||||
int isdescprocess(pid_t p, pid_t c) {
|
||||
while (p != c && c != 0)
|
||||
c = getparentprocess(c);
|
||||
|
||||
return (int32_t)c;
|
||||
return (int)c;
|
||||
}
|
||||
|
||||
void get_layout_abbr(char *abbr, const char *full_name) {
|
||||
|
|
@ -31,7 +31,7 @@ void get_layout_abbr(char *abbr, const char *full_name) {
|
|||
abbr[0] = '\0';
|
||||
|
||||
// 1. 尝试在映射表中查找
|
||||
for (int32_t i = 0; layout_mappings[i].full_name != NULL; i++) {
|
||||
for (int i = 0; layout_mappings[i].full_name != NULL; i++) {
|
||||
if (strcmp(full_name, layout_mappings[i].full_name) == 0) {
|
||||
strcpy(abbr, layout_mappings[i].abbr);
|
||||
return;
|
||||
|
|
@ -77,36 +77,13 @@ void get_layout_abbr(char *abbr, const char *full_name) {
|
|||
}
|
||||
}
|
||||
|
||||
Client *xytoclient(double x, double y) {
|
||||
Client *c = NULL, *tmp = NULL;
|
||||
wl_list_for_each_safe(c, tmp, &clients, link) {
|
||||
if (VISIBLEON(c, c->mon) && c->animation.current.x <= x &&
|
||||
c->animation.current.y <= y &&
|
||||
c->animation.current.x + c->animation.current.width >= x &&
|
||||
c->animation.current.y + c->animation.current.height >= y) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool layer_ignores_focus(LayerSurface *l) {
|
||||
if (!l || !l->layer_surface)
|
||||
return true;
|
||||
struct wlr_surface *s = l->layer_surface->surface;
|
||||
return !pixman_region32_not_empty(&s->input_region) ||
|
||||
l->layer_surface->current.keyboard_interactive ==
|
||||
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
||||
}
|
||||
|
||||
void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
|
||||
LayerSurface **pl, double *nx, double *ny) {
|
||||
struct wlr_scene_node *node, *pnode;
|
||||
struct wlr_surface *surface = NULL;
|
||||
Client *c = NULL;
|
||||
LayerSurface *l = NULL;
|
||||
int32_t layer;
|
||||
Client *ovc = NULL;
|
||||
int layer;
|
||||
|
||||
for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
|
||||
|
||||
|
|
@ -116,18 +93,13 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
|
|||
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) {
|
||||
struct wlr_scene_surface *scene_surface =
|
||||
wlr_scene_surface_try_from_buffer(
|
||||
wlr_scene_buffer_from_node(node));
|
||||
if (scene_surface) {
|
||||
surface = scene_surface->surface;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (node->type == WLR_SCENE_NODE_BUFFER)
|
||||
surface = wlr_scene_surface_try_from_buffer(
|
||||
wlr_scene_buffer_from_node(node))
|
||||
->surface;
|
||||
else if (node->type == WLR_SCENE_NODE_RECT) {
|
||||
surface = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* start from the topmost layer,
|
||||
|
|
@ -140,17 +112,10 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
|
|||
for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
|
||||
c = pnode->data;
|
||||
if (c && c->type == LayerShell) {
|
||||
l = (LayerSurface *)c;
|
||||
c = NULL;
|
||||
l = pnode->data;
|
||||
}
|
||||
}
|
||||
|
||||
if (node->type == WLR_SCENE_NODE_RECT) {
|
||||
if (c) {
|
||||
surface = client_surface(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (psurface)
|
||||
|
|
@ -159,12 +124,4 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
|
|||
*pc = c;
|
||||
if (pl)
|
||||
*pl = l;
|
||||
|
||||
if (selmon && selmon->isoverview && (!l || layer_ignores_focus(l))) {
|
||||
ovc = xytoclient(x, y);
|
||||
if (pc)
|
||||
*pc = ovc;
|
||||
if (psurface && ovc)
|
||||
*psurface = client_surface(ovc);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,14 +26,6 @@ bool is_scroller_layout(Monitor *m) {
|
|||
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;
|
||||
|
|
@ -54,7 +46,7 @@ uint32_t get_tags_first_tag_num(uint32_t source_tags) {
|
|||
tag = 0;
|
||||
|
||||
if (!source_tags) {
|
||||
return 0;
|
||||
return selmon->pertag->curtag;
|
||||
}
|
||||
|
||||
for (i = 0; !(tag & 1) && source_tags != 0 && i < LENGTH(tags); i++) {
|
||||
|
|
@ -96,81 +88,3 @@ 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;
|
||||
}
|
||||
1051
src/ipc/ipc.h
1051
src/ipc/ipc.h
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,628 +0,0 @@
|
|||
static DwindleNode *dwindle_locked_h_node = NULL;
|
||||
static DwindleNode *dwindle_locked_v_node = NULL;
|
||||
|
||||
static DwindleNode *dwindle_new_leaf(Client *c) {
|
||||
DwindleNode *n = calloc(1, sizeof(DwindleNode));
|
||||
n->client = c;
|
||||
return n;
|
||||
}
|
||||
|
||||
// 统计同方向上的节点总和 (N_old)
|
||||
static int count_block_items(DwindleNode *node, bool split_h) {
|
||||
if (!node)
|
||||
return 0;
|
||||
if (!node->is_split || node->split_h != split_h)
|
||||
return 1;
|
||||
return count_block_items(node->first, split_h) +
|
||||
count_block_items(node->second, split_h);
|
||||
}
|
||||
|
||||
// 向上查找方向块路径,并计算每个祖先节点的绝对占比
|
||||
static int get_block_path_and_ratios(DwindleNode *target, bool split_h,
|
||||
DwindleNode **path, float *p) {
|
||||
int path_len = 0;
|
||||
path[path_len++] = target;
|
||||
DwindleNode *curr = target->parent;
|
||||
while (curr && curr->split_h == split_h) {
|
||||
path[path_len++] = curr;
|
||||
curr = curr->parent;
|
||||
}
|
||||
|
||||
p[path_len - 1] = 1.0f; // 方向块根节点占比为 100%
|
||||
for (int i = path_len - 1; i > 0; i--) {
|
||||
DwindleNode *S = path[i];
|
||||
DwindleNode *child = path[i - 1];
|
||||
if (S->first == child)
|
||||
p[i - 1] = p[i] * S->ratio;
|
||||
else
|
||||
p[i - 1] = p[i] * (1.0f - S->ratio);
|
||||
}
|
||||
return path_len;
|
||||
}
|
||||
|
||||
static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c) {
|
||||
if (!node)
|
||||
return NULL;
|
||||
if (!node->is_split)
|
||||
return node->client == c ? node : NULL;
|
||||
DwindleNode *r = dwindle_find_leaf(node->first, c);
|
||||
return r ? r : dwindle_find_leaf(node->second, c);
|
||||
}
|
||||
|
||||
static DwindleNode *dwindle_first_leaf(DwindleNode *node) {
|
||||
if (!node)
|
||||
return NULL;
|
||||
while (node->is_split)
|
||||
node = node->first;
|
||||
return node;
|
||||
}
|
||||
|
||||
static void dwindle_free_tree(DwindleNode *node) {
|
||||
if (!node)
|
||||
return;
|
||||
dwindle_free_tree(node->first);
|
||||
dwindle_free_tree(node->second);
|
||||
free(node);
|
||||
}
|
||||
|
||||
static void dwindle_insert(DwindleNode **root, Client *new_c, Client *focused,
|
||||
float ratio, bool as_first, bool split_h,
|
||||
bool lock) {
|
||||
DwindleNode *new_leaf = dwindle_new_leaf(new_c);
|
||||
|
||||
if (!*root) {
|
||||
new_leaf->custom_leaf_split_h = true;
|
||||
*root = new_leaf;
|
||||
return;
|
||||
}
|
||||
|
||||
DwindleNode *target = focused ? dwindle_find_leaf(*root, focused) : NULL;
|
||||
if (!target)
|
||||
target = dwindle_first_leaf(*root);
|
||||
|
||||
// ================= 保持其他窗口比例缩减逻辑 =================
|
||||
if (config.dwindle_manual_split) {
|
||||
DwindleNode *path[512];
|
||||
float p[512];
|
||||
int path_len = get_block_path_and_ratios(target, split_h, path, p);
|
||||
|
||||
int n_old = 1;
|
||||
if (path_len > 1) {
|
||||
n_old = count_block_items(path[path_len - 1], split_h);
|
||||
}
|
||||
float N = (float)(n_old + 1);
|
||||
|
||||
for (int i = path_len - 1; i > 0; i--) {
|
||||
DwindleNode *S = path[i];
|
||||
DwindleNode *child = path[i - 1];
|
||||
float p_S = p[i];
|
||||
float p_first = p_S * S->ratio;
|
||||
|
||||
if (S->first == child) {
|
||||
float p_first_new = p_first * (N - 1.0f) / N + 1.0f / N;
|
||||
float p_S_new = p_S * (N - 1.0f) / N + 1.0f / N;
|
||||
S->ratio = p_first_new / p_S_new;
|
||||
} else {
|
||||
float p_first_new = p_first * (N - 1.0f) / N;
|
||||
float p_S_new = p_S * (N - 1.0f) / N + 1.0f / N;
|
||||
S->ratio = p_first_new / p_S_new;
|
||||
}
|
||||
if (S->ratio < 0.001f)
|
||||
S->ratio = 0.001f;
|
||||
if (S->ratio > 0.999f)
|
||||
S->ratio = 0.999f;
|
||||
}
|
||||
}
|
||||
// ============================================================
|
||||
|
||||
DwindleNode *split = calloc(1, sizeof(DwindleNode));
|
||||
split->is_split = true;
|
||||
split->split_h = split_h;
|
||||
split->split_locked = lock;
|
||||
split->custom_leaf_split_h = target->custom_leaf_split_h;
|
||||
new_leaf->custom_leaf_split_h = target->custom_leaf_split_h;
|
||||
|
||||
if (as_first) {
|
||||
split->first = new_leaf;
|
||||
split->second = target;
|
||||
} else {
|
||||
split->first = target;
|
||||
split->second = new_leaf;
|
||||
}
|
||||
|
||||
// 通用逻辑
|
||||
split->ratio = ratio;
|
||||
|
||||
split->parent = target->parent;
|
||||
target->parent = split;
|
||||
new_leaf->parent = split;
|
||||
|
||||
if (!split->parent) {
|
||||
*root = split;
|
||||
} else {
|
||||
if (split->parent->first == target)
|
||||
split->parent->first = split;
|
||||
else
|
||||
split->parent->second = split;
|
||||
}
|
||||
}
|
||||
|
||||
static void dwindle_remove(DwindleNode **root, Client *c) {
|
||||
DwindleNode *leaf = dwindle_find_leaf(*root, c);
|
||||
if (!leaf)
|
||||
return;
|
||||
|
||||
DwindleNode *parent = leaf->parent;
|
||||
|
||||
if (dwindle_locked_h_node == leaf || dwindle_locked_h_node == parent)
|
||||
dwindle_locked_h_node = NULL;
|
||||
if (dwindle_locked_v_node == leaf || dwindle_locked_v_node == parent)
|
||||
dwindle_locked_v_node = NULL;
|
||||
|
||||
if (!parent) {
|
||||
free(leaf);
|
||||
*root = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始删除空间的比例回退逻辑
|
||||
|
||||
// 查找连续的同方向块路径
|
||||
if (config.dwindle_manual_split) {
|
||||
bool split_h = parent->split_h;
|
||||
DwindleNode *path[512];
|
||||
int path_len = 0;
|
||||
path[path_len++] = parent;
|
||||
DwindleNode *curr = parent->parent;
|
||||
while (curr && curr->split_h == split_h) {
|
||||
path[path_len++] = curr;
|
||||
curr = curr->parent;
|
||||
}
|
||||
|
||||
// 计算各祖先的旧绝对占比
|
||||
float p[512];
|
||||
p[path_len - 1] = 1.0f;
|
||||
for (int i = path_len - 1; i > 0; i--) {
|
||||
DwindleNode *S = path[i];
|
||||
DwindleNode *child = path[i - 1];
|
||||
if (S->first == child)
|
||||
p[i - 1] = p[i] * S->ratio;
|
||||
else
|
||||
p[i - 1] = p[i] * (1.0f - S->ratio);
|
||||
}
|
||||
|
||||
// 计算即将被删除的叶子节点,在该方向块中所占的绝对面积比例 (P_del)
|
||||
float p_del = p[0] * (parent->first == leaf ? parent->ratio
|
||||
: (1.0f - parent->ratio));
|
||||
if (p_del > 0.999f)
|
||||
p_del = 0.999f; // 兜底
|
||||
|
||||
// 重算祖先比例:将 P_del 空出来的空间,按原定比例无缝分配给其他窗口
|
||||
for (int i = path_len - 1; i > 0; i--) {
|
||||
DwindleNode *S = path[i];
|
||||
DwindleNode *child = path[i - 1];
|
||||
float p_S = p[i];
|
||||
float p_first = p_S * S->ratio;
|
||||
|
||||
float denom = p_S - p_del;
|
||||
if (denom < 0.0001f)
|
||||
denom = 0.0001f;
|
||||
|
||||
if (S->first == child) {
|
||||
S->ratio = (p_first - p_del) / denom;
|
||||
} else {
|
||||
S->ratio = p_first / denom;
|
||||
}
|
||||
|
||||
if (S->ratio < 0.001f)
|
||||
S->ratio = 0.001f;
|
||||
if (S->ratio > 0.999f)
|
||||
S->ratio = 0.999f;
|
||||
}
|
||||
}
|
||||
|
||||
// 比例重算结束
|
||||
|
||||
// 基础的二叉树摘除节点逻辑
|
||||
DwindleNode *sibling =
|
||||
(parent->first == leaf) ? parent->second : parent->first;
|
||||
DwindleNode *grandparent = parent->parent;
|
||||
|
||||
sibling->parent = grandparent;
|
||||
|
||||
if (!sibling->is_split ||
|
||||
(!config.dwindle_preserve_split && !config.dwindle_smart_split)) {
|
||||
sibling->container_w = 0;
|
||||
sibling->container_h = 0;
|
||||
}
|
||||
|
||||
if (!grandparent) {
|
||||
*root = sibling;
|
||||
} else {
|
||||
if (grandparent->first == parent)
|
||||
grandparent->first = sibling;
|
||||
else
|
||||
grandparent->second = sibling;
|
||||
}
|
||||
|
||||
free(leaf);
|
||||
free(parent);
|
||||
}
|
||||
|
||||
static void dwindle_assign(DwindleNode *node, int32_t ax, int32_t ay,
|
||||
int32_t aw, int32_t ah, int32_t gap_h,
|
||||
int32_t gap_v) {
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
if (!node->is_split) {
|
||||
if (node->client) {
|
||||
if (!node->client->isfullscreen &&
|
||||
!node->client->ismaximizescreen) {
|
||||
struct wlr_box box = {ax, ay, MAX(1, aw), MAX(1, ah)};
|
||||
resize(node->client, box, 0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node->split_locked && node->container_w == 0 && node->container_h == 0)
|
||||
node->split_h = (aw >= ah);
|
||||
node->container_x = ax;
|
||||
node->container_y = ay;
|
||||
node->container_w = aw;
|
||||
node->container_h = ah;
|
||||
if (node->split_h) {
|
||||
int32_t w1 = MAX(1, (int32_t)(aw * node->ratio) - gap_h / 2);
|
||||
dwindle_assign(node->first, ax, ay, w1, ah, gap_h, gap_v);
|
||||
dwindle_assign(node->second, ax + w1 + gap_h, ay, aw - w1 - gap_h, ah,
|
||||
gap_h, gap_v);
|
||||
} else {
|
||||
int32_t h1 = MAX(1, (int32_t)(ah * node->ratio) - gap_v / 2);
|
||||
dwindle_assign(node->first, ax, ay, aw, h1, gap_h, gap_v);
|
||||
dwindle_assign(node->second, ax, ay + h1 + gap_v, aw, ah - h1 - gap_v,
|
||||
gap_h, gap_v);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwindle_move_client(DwindleNode **root, Client *c, Client *target,
|
||||
float ratio, int32_t dir) {
|
||||
if (!c || !target || c == target)
|
||||
return;
|
||||
if (!dwindle_find_leaf(*root, c) || !dwindle_find_leaf(*root, target))
|
||||
return;
|
||||
dwindle_remove(root, c);
|
||||
bool as_first = (dir == UP || dir == LEFT);
|
||||
bool split_h = (dir == LEFT || dir == RIGHT);
|
||||
dwindle_insert(root, c, target, ratio, as_first, split_h, true);
|
||||
}
|
||||
|
||||
static void dwindle_swap_clients(Client *c1, Client *c2) {
|
||||
|
||||
if (!c1 || !c2 || !c1->mon || !c2->mon || c1 == c2)
|
||||
return;
|
||||
|
||||
Monitor *m1 = c1->mon;
|
||||
Monitor *m2 = c2->mon;
|
||||
|
||||
DwindleNode **c1_root = &m1->pertag->dwindle_root[m1->pertag->curtag];
|
||||
DwindleNode *c1node = dwindle_find_leaf(*c1_root, c1);
|
||||
DwindleNode **c2_root = &m2->pertag->dwindle_root[m2->pertag->curtag];
|
||||
DwindleNode *c2node = dwindle_find_leaf(*c2_root, c2);
|
||||
|
||||
client_swap_layout_properties(c1, c2);
|
||||
|
||||
if (c1node)
|
||||
c1node->client = c2;
|
||||
if (c2node)
|
||||
c2node->client = c1;
|
||||
|
||||
if (m1 != m2) {
|
||||
client_swap_monitors_and_tags(c1, c2);
|
||||
}
|
||||
|
||||
wl_list_swap(&c1->link, &c2->link);
|
||||
finish_exchange_arrange_and_focus(c1, c2, m1, m2);
|
||||
}
|
||||
|
||||
static void dwindle_resize_client(Monitor *m, Client *c) {
|
||||
uint32_t tag = m->pertag->curtag;
|
||||
DwindleNode *leaf = dwindle_find_leaf(m->pertag->dwindle_root[tag], c);
|
||||
if (!leaf)
|
||||
return;
|
||||
|
||||
if (!start_drag_window) {
|
||||
start_drag_window = true;
|
||||
dwindle_locked_h_node = NULL;
|
||||
dwindle_locked_v_node = NULL;
|
||||
drag_begin_cursorx = cursor->x;
|
||||
drag_begin_cursory = cursor->y;
|
||||
DwindleNode *node = leaf->parent;
|
||||
while (node) {
|
||||
if (node->split_h && !dwindle_locked_h_node) {
|
||||
dwindle_locked_h_node = node;
|
||||
node->drag_init_ratio = node->ratio;
|
||||
}
|
||||
if (!node->split_h && !dwindle_locked_v_node) {
|
||||
dwindle_locked_v_node = node;
|
||||
node->drag_init_ratio = node->ratio;
|
||||
}
|
||||
if (dwindle_locked_h_node && dwindle_locked_v_node)
|
||||
break;
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dwindle_locked_h_node && !dwindle_locked_v_node)
|
||||
return;
|
||||
|
||||
if (dwindle_locked_h_node) {
|
||||
float cw = (float)MAX(1, dwindle_locked_h_node->container_w);
|
||||
float ox = (float)(cursor->x - drag_begin_cursorx);
|
||||
if (config.dwindle_smart_resize) {
|
||||
/* Move the boundary toward the cursor: invert direction when
|
||||
* the drag started on the right side of the split line. */
|
||||
float split_x = dwindle_locked_h_node->container_x +
|
||||
cw * dwindle_locked_h_node->drag_init_ratio;
|
||||
if (drag_begin_cursorx >= split_x)
|
||||
ox = -ox;
|
||||
}
|
||||
dwindle_locked_h_node->ratio =
|
||||
dwindle_locked_h_node->drag_init_ratio + ox / cw;
|
||||
dwindle_locked_h_node->ratio =
|
||||
CLAMP_FLOAT(dwindle_locked_h_node->ratio, 0.05f, 0.95f);
|
||||
}
|
||||
|
||||
if (dwindle_locked_v_node) {
|
||||
float ch = (float)MAX(1, dwindle_locked_v_node->container_h);
|
||||
float oy = (float)(cursor->y - drag_begin_cursory);
|
||||
if (config.dwindle_smart_resize) {
|
||||
/* Same logic for the vertical split line. */
|
||||
float split_y = dwindle_locked_v_node->container_y +
|
||||
ch * dwindle_locked_v_node->drag_init_ratio;
|
||||
if (drag_begin_cursory >= split_y)
|
||||
oy = -oy;
|
||||
}
|
||||
dwindle_locked_v_node->ratio =
|
||||
dwindle_locked_v_node->drag_init_ratio + oy / ch;
|
||||
dwindle_locked_v_node->ratio =
|
||||
CLAMP_FLOAT(dwindle_locked_v_node->ratio, 0.05f, 0.95f);
|
||||
}
|
||||
|
||||
int32_t n = m->visible_tiling_clients;
|
||||
int32_t gap_ih = enablegaps ? m->gappih : 0;
|
||||
int32_t gap_iv = enablegaps ? m->gappiv : 0;
|
||||
int32_t gap_oh = enablegaps ? m->gappoh : 0;
|
||||
int32_t gap_ov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && n == 1)
|
||||
gap_ih = gap_iv = gap_oh = gap_ov = 0;
|
||||
|
||||
dwindle_assign(m->pertag->dwindle_root[tag], m->w.x + gap_oh,
|
||||
m->w.y + gap_ov, m->w.width - 2 * gap_oh,
|
||||
m->w.height - 2 * gap_ov, gap_ih, gap_iv);
|
||||
}
|
||||
|
||||
static void dwindle_resize_client_step(Monitor *m, Client *c, int32_t dx,
|
||||
int32_t dy) {
|
||||
uint32_t tag = m->pertag->curtag;
|
||||
DwindleNode *leaf = dwindle_find_leaf(m->pertag->dwindle_root[tag], c);
|
||||
if (!leaf)
|
||||
return;
|
||||
|
||||
DwindleNode *h_node = NULL;
|
||||
DwindleNode *v_node = NULL;
|
||||
DwindleNode *node = leaf->parent;
|
||||
|
||||
while (node) {
|
||||
if (node->split_h && !h_node)
|
||||
h_node = node;
|
||||
if (!node->split_h && !v_node)
|
||||
v_node = node;
|
||||
if (h_node && v_node)
|
||||
break;
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
if (!h_node && !v_node)
|
||||
return;
|
||||
|
||||
if (h_node && dx) {
|
||||
float cw = (float)MAX(1, h_node->container_w);
|
||||
float delta = (float)dx / cw;
|
||||
h_node->ratio = CLAMP_FLOAT(h_node->ratio + delta, 0.05f, 0.95f);
|
||||
}
|
||||
|
||||
if (v_node && dy) {
|
||||
float ch = (float)MAX(1, v_node->container_h);
|
||||
float delta = (float)dy / ch;
|
||||
v_node->ratio = CLAMP_FLOAT(v_node->ratio + delta, 0.05f, 0.95f);
|
||||
}
|
||||
|
||||
int32_t n_clients = m->visible_tiling_clients;
|
||||
int32_t gap_ih = enablegaps ? m->gappih : 0;
|
||||
int32_t gap_iv = enablegaps ? m->gappiv : 0;
|
||||
int32_t gap_oh = enablegaps ? m->gappoh : 0;
|
||||
int32_t gap_ov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && n_clients == 1)
|
||||
gap_ih = gap_iv = gap_oh = gap_ov = 0;
|
||||
|
||||
dwindle_assign(m->pertag->dwindle_root[tag], m->w.x + gap_oh,
|
||||
m->w.y + gap_ov, m->w.width - 2 * gap_oh,
|
||||
m->w.height - 2 * gap_ov, gap_ih, gap_iv);
|
||||
}
|
||||
|
||||
static void dwindle_remove_client(Client *c) {
|
||||
Monitor *m;
|
||||
wl_list_for_each(m, &mons, link) {
|
||||
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++)
|
||||
dwindle_remove(&m->pertag->dwindle_root[t], c);
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert a new client respecting dwindle_vsplit, dwindle_hsplit, and
|
||||
* dwindle_smart_split config options. */
|
||||
static void dwindle_insert_with_config(DwindleNode **root, Client *new_c,
|
||||
Client *focused, float ratio) {
|
||||
|
||||
if (!new_c || !focused)
|
||||
return;
|
||||
|
||||
bool as_first = false;
|
||||
bool split_h = false;
|
||||
bool lock = false;
|
||||
|
||||
struct wlr_box *fg = &focused->geom;
|
||||
double fcx = fg->x + fg->width * 0.5;
|
||||
double fcy = fg->y + fg->height * 0.5;
|
||||
|
||||
if (config.dwindle_smart_split) {
|
||||
double nx = (cursor->x - fcx) / (fg->width * 0.5);
|
||||
double ny = (cursor->y - fcy) / (fg->height * 0.5);
|
||||
|
||||
if (fabs(ny) > fabs(nx)) {
|
||||
split_h = false; // vertical split
|
||||
as_first = (ny < 0); // top → new window on top
|
||||
} else {
|
||||
split_h = true; // horizontal split
|
||||
as_first = (nx < 0); // left → new window on left
|
||||
}
|
||||
lock = true; // lock split direction
|
||||
} else {
|
||||
// normal mode, auto split
|
||||
bool likely_h = (fg->width >= fg->height);
|
||||
split_h = likely_h;
|
||||
|
||||
if (likely_h) {
|
||||
if (config.dwindle_hsplit == 0)
|
||||
as_first = (cursor->x < fcx);
|
||||
else
|
||||
as_first = (config.dwindle_hsplit == 2);
|
||||
} else {
|
||||
if (config.dwindle_vsplit == 0)
|
||||
as_first = (cursor->y < fcy);
|
||||
else
|
||||
as_first = (config.dwindle_vsplit == 2);
|
||||
}
|
||||
}
|
||||
|
||||
DwindleNode *target = focused ? dwindle_find_leaf(*root, focused) : NULL;
|
||||
if (!target && *root)
|
||||
target = dwindle_first_leaf(*root);
|
||||
|
||||
// 当且仅当 manual_split=1 时,计算精确的 1/N 新节点比例
|
||||
if (config.dwindle_manual_split && target) {
|
||||
split_h = target->custom_leaf_split_h;
|
||||
lock = true;
|
||||
as_first = false;
|
||||
|
||||
// ================= 计算新节点的 1/N 比例 =================
|
||||
DwindleNode *path[512];
|
||||
float p[512];
|
||||
int path_len = get_block_path_and_ratios(target, split_h, path, p);
|
||||
|
||||
int n_old = 1;
|
||||
if (path_len > 1) {
|
||||
n_old = count_block_items(path[path_len - 1], split_h);
|
||||
}
|
||||
float N = (float)(n_old + 1);
|
||||
|
||||
float p_target_old = p[0];
|
||||
float p_split_new = p_target_old * (N - 1.0f) / N + 1.0f / N;
|
||||
|
||||
if (as_first) {
|
||||
ratio = (1.0f / N) / p_split_new;
|
||||
} else {
|
||||
ratio = (p_target_old * (N - 1.0f) / N) / p_split_new;
|
||||
}
|
||||
|
||||
if (ratio < 0.001f)
|
||||
ratio = 0.001f;
|
||||
if (ratio > 0.999f)
|
||||
ratio = 0.999f;
|
||||
// =========================================================
|
||||
}
|
||||
|
||||
// 调用通用插入函数
|
||||
dwindle_insert(root, new_c, focused, ratio, as_first, split_h, lock);
|
||||
}
|
||||
|
||||
void dwindle(Monitor *m) {
|
||||
int32_t n = m->visible_tiling_clients;
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
uint32_t tag = m->pertag->curtag;
|
||||
DwindleNode **root = &m->pertag->dwindle_root[tag];
|
||||
float ratio = config.dwindle_split_ratio;
|
||||
|
||||
Client *vis[512];
|
||||
int32_t count = 0;
|
||||
Client *c;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (VISIBLEON(c, m) && ISTILED(c))
|
||||
vis[count++] = c;
|
||||
if (count >= 512)
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
DwindleNode *leaves[512];
|
||||
int32_t lc = 0;
|
||||
|
||||
DwindleNode *stack[1024];
|
||||
int32_t sp = 0;
|
||||
if (*root)
|
||||
stack[sp++] = *root;
|
||||
while (sp > 0) {
|
||||
DwindleNode *nd = stack[--sp];
|
||||
if (!nd->is_split) {
|
||||
leaves[lc++] = nd;
|
||||
} else {
|
||||
if (nd->second)
|
||||
stack[sp++] = nd->second;
|
||||
if (nd->first)
|
||||
stack[sp++] = nd->first;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < lc; i++) {
|
||||
bool found = false;
|
||||
for (int32_t j = 0; j < count; j++)
|
||||
if (vis[j] == leaves[i]->client) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
if (VISIBLEON(leaves[i]->client, m) &&
|
||||
(leaves[i]->client->isfullscreen ||
|
||||
leaves[i]->client->ismaximizescreen))
|
||||
continue;
|
||||
dwindle_remove(root, leaves[i]->client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Client *focused = focustop(m);
|
||||
if (focused && !dwindle_find_leaf(*root, focused))
|
||||
focused = m->sel;
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
if (!dwindle_find_leaf(*root, vis[i]))
|
||||
dwindle_insert_with_config(root, vis[i], focused, ratio);
|
||||
}
|
||||
|
||||
int32_t gap_ih = enablegaps ? m->gappih : 0;
|
||||
int32_t gap_iv = enablegaps ? m->gappiv : 0;
|
||||
int32_t gap_oh = enablegaps ? m->gappoh : 0;
|
||||
int32_t gap_ov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && n == 1)
|
||||
gap_ih = gap_iv = gap_oh = gap_ov = 0;
|
||||
|
||||
dwindle_assign(*root, m->w.x + gap_oh, m->w.y + gap_ov,
|
||||
m->w.width - 2 * gap_oh, m->w.height - 2 * gap_ov, gap_ih,
|
||||
gap_iv);
|
||||
}
|
||||
|
||||
void cleanup_monitor_dwindle(Monitor *m) {
|
||||
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++)
|
||||
dwindle_free_tree(m->pertag->dwindle_root[t]);
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -11,16 +11,14 @@ 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 dwindle(Monitor *m);
|
||||
static void fair(Monitor *m);
|
||||
static void vertical_fair(Monitor *m);
|
||||
static void tgmix(Monitor *m);
|
||||
|
||||
/* layout(s) */
|
||||
Layout overviewlayout = {"", overview, "overview"};
|
||||
|
||||
enum {
|
||||
TILE,
|
||||
SCROLLER,
|
||||
TILE,
|
||||
GRID,
|
||||
MONOCLE,
|
||||
DECK,
|
||||
|
|
@ -30,16 +28,14 @@ enum {
|
|||
VERTICAL_GRID,
|
||||
VERTICAL_DECK,
|
||||
RIGHT_TILE,
|
||||
DWINDLE,
|
||||
FAIR,
|
||||
VERTICAL_FAIR,
|
||||
TGMIX,
|
||||
};
|
||||
|
||||
Layout layouts[] = {
|
||||
// 最少两个,不能删除少于两个
|
||||
/* symbol arrange function name */
|
||||
{"T", tile, "tile", TILE}, // 平铺布局
|
||||
{"S", scroller, "scroller", SCROLLER}, // 滚动布局
|
||||
{"T", tile, "tile", TILE}, // 平铺布局
|
||||
{"G", grid, "grid", GRID}, // 格子布局
|
||||
{"M", monocle, "monocle", MONOCLE}, // 单屏布局
|
||||
{"K", deck, "deck", DECK}, // 卡片布局
|
||||
|
|
@ -50,7 +46,5 @@ Layout layouts[] = {
|
|||
{"VT", vertical_tile, "vertical_tile", VERTICAL_TILE}, // 垂直平铺布局
|
||||
{"VG", vertical_grid, "vertical_grid", VERTICAL_GRID}, // 垂直格子布局
|
||||
{"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局
|
||||
{"DW", dwindle, "dwindle", DWINDLE},
|
||||
{"F", fair, "fair", FAIR},
|
||||
{"VF", vertical_fair, "vertical_fair", VERTICAL_FAIR},
|
||||
{"TG", tgmix, "tgmix", TGMIX}, // 混合布局
|
||||
};
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
|
||||
typedef struct {
|
||||
float x, y, w, h;
|
||||
} OvPlacedRect;
|
||||
|
||||
typedef struct {
|
||||
float x, y;
|
||||
} OvPoint;
|
||||
|
||||
typedef struct {
|
||||
Client *c;
|
||||
float orig_w;
|
||||
float orig_h;
|
||||
float area;
|
||||
} OvLayoutItem;
|
||||
|
||||
static int compare_layout_items(const void *a, const void *b) {
|
||||
float area_a = ((const OvLayoutItem *)a)->area;
|
||||
float area_b = ((const OvLayoutItem *)b)->area;
|
||||
if (area_a < area_b)
|
||||
return 1;
|
||||
if (area_a > area_b)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool try_place(OvPlacedRect *placed, int placed_cnt, float w, float h,
|
||||
float gap, float avail_w, float avail_h,
|
||||
OvPlacedRect *out, OvPoint *cands, OvPoint *feas) {
|
||||
int cand_cnt = 0;
|
||||
cands[cand_cnt++] = (OvPoint){0.0f, 0.0f};
|
||||
|
||||
for (int i = 0; i < placed_cnt; i++) {
|
||||
OvPlacedRect p = placed[i];
|
||||
cands[cand_cnt++] = (OvPoint){p.x + p.w + gap, p.y};
|
||||
cands[cand_cnt++] = (OvPoint){p.x, p.y + p.h + gap};
|
||||
cands[cand_cnt++] = (OvPoint){p.x + p.w + gap, p.y + p.h + gap};
|
||||
}
|
||||
|
||||
int unique_cnt = 0;
|
||||
for (int i = 0; i < cand_cnt; i++) {
|
||||
bool dup = false;
|
||||
for (int j = 0; j < unique_cnt; j++) {
|
||||
if (fabs(cands[i].x - cands[j].x) < 0.5f &&
|
||||
fabs(cands[i].y - cands[j].y) < 0.5f) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dup)
|
||||
cands[unique_cnt++] = cands[i];
|
||||
}
|
||||
cand_cnt = unique_cnt;
|
||||
|
||||
int feas_cnt = 0;
|
||||
for (int i = 0; i < cand_cnt; i++) {
|
||||
float cx = cands[i].x;
|
||||
float cy = cands[i].y;
|
||||
|
||||
if (cx < 0 || cy < 0 || cx + w > avail_w || cy + h > avail_h)
|
||||
continue;
|
||||
|
||||
bool overlap = false;
|
||||
for (int j = 0; j < placed_cnt; j++) {
|
||||
OvPlacedRect p = placed[j];
|
||||
if (!(cx + w + gap <= p.x || cx >= p.x + p.w + gap ||
|
||||
cy + h + gap <= p.y || cy >= p.y + p.h + gap)) {
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!overlap) {
|
||||
feas[feas_cnt++] = (OvPoint){cx, cy};
|
||||
}
|
||||
}
|
||||
|
||||
if (feas_cnt == 0)
|
||||
return false;
|
||||
|
||||
int best = 0;
|
||||
for (int i = 1; i < feas_cnt; i++) {
|
||||
if (feas[i].y < feas[best].y ||
|
||||
(fabs(feas[i].y - feas[best].y) < 0.5f &&
|
||||
feas[i].x < feas[best].x)) {
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
out->x = feas[best].x;
|
||||
out->y = feas[best].y;
|
||||
out->w = w;
|
||||
out->h = h;
|
||||
return true;
|
||||
}
|
||||
|
||||
void overview_scale(Monitor *m) {
|
||||
int32_t target_gappo = config.overviewgappo;
|
||||
int32_t target_gappi = config.overviewgappi;
|
||||
|
||||
int orig_n = m->visible_clients;
|
||||
if (orig_n == 0)
|
||||
return;
|
||||
|
||||
OvLayoutItem *items = calloc(orig_n, sizeof(OvLayoutItem));
|
||||
if (!items)
|
||||
return;
|
||||
|
||||
int n = 0;
|
||||
Client *c;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
if (VISIBLEON(c, m) && !c->isunglobal && !client_is_x11_popup(c)) {
|
||||
items[n].c = c;
|
||||
float w = c->overview_backup_geom.width;
|
||||
float h = c->overview_backup_geom.height;
|
||||
if (w <= 0 || h <= 0) {
|
||||
w = 100.0f;
|
||||
h = 100.0f;
|
||||
}
|
||||
items[n].orig_w = w;
|
||||
items[n].orig_h = h;
|
||||
items[n].area = w * h;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
free(items);
|
||||
return;
|
||||
}
|
||||
|
||||
qsort(items, n, sizeof(OvLayoutItem), compare_layout_items);
|
||||
|
||||
float max_avail_w = fmaxf(1.0f, m->w.width - 2 * target_gappo);
|
||||
float max_avail_h = fmaxf(1.0f, m->w.height - 2 * target_gappo);
|
||||
|
||||
int max_points = 1 + 3 * n;
|
||||
OvPlacedRect *placed = calloc(n, sizeof(OvPlacedRect));
|
||||
OvPoint *cands = calloc(max_points, sizeof(OvPoint));
|
||||
OvPoint *feas = calloc(max_points, sizeof(OvPoint));
|
||||
|
||||
if (!placed || !cands || !feas) {
|
||||
free(items);
|
||||
free(placed);
|
||||
free(cands);
|
||||
free(feas);
|
||||
return;
|
||||
}
|
||||
|
||||
float low = 0.0f, high = 1.0f, best_s = 0.0f;
|
||||
for (int iter = 0; iter < 50; iter++) {
|
||||
float mid = (low + high) / 2.0f;
|
||||
bool ok = true;
|
||||
int placed_cnt = 0;
|
||||
|
||||
for (int k = 0; k < n; k++) {
|
||||
float w = items[k].orig_w * mid;
|
||||
float h = items[k].orig_h * mid;
|
||||
OvPlacedRect out;
|
||||
if (!try_place(placed, placed_cnt, w, h, (float)target_gappi,
|
||||
max_avail_w, max_avail_h, &out, cands, feas)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
placed[placed_cnt++] = out;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
best_s = mid;
|
||||
low = mid;
|
||||
} else {
|
||||
high = mid;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_s > 0.0f) {
|
||||
int placed_cnt = 0;
|
||||
|
||||
for (int k = 0; k < n; k++) {
|
||||
float w = items[k].orig_w * best_s;
|
||||
float h = items[k].orig_h * best_s;
|
||||
OvPlacedRect out;
|
||||
try_place(placed, placed_cnt, w, h, (float)target_gappi,
|
||||
max_avail_w, max_avail_h, &out, cands, feas);
|
||||
placed[placed_cnt++] = out;
|
||||
}
|
||||
|
||||
if (n > 1) {
|
||||
float grid_box_w = 0;
|
||||
for (int k = 0; k < n - 1; k++) {
|
||||
float r = placed[k].x + placed[k].w;
|
||||
if (r > grid_box_w)
|
||||
grid_box_w = r;
|
||||
}
|
||||
|
||||
OvPlacedRect *last = &placed[n - 1];
|
||||
float max_x = grid_box_w - last->w;
|
||||
|
||||
if (max_x > last->x) {
|
||||
for (int k = 0; k < n - 1; k++) {
|
||||
OvPlacedRect p = placed[k];
|
||||
if (!(last->y + last->h + target_gappi <= p.y ||
|
||||
last->y >= p.y + p.h + target_gappi)) {
|
||||
if (p.x > last->x) {
|
||||
float limit = p.x - target_gappi - last->w;
|
||||
if (limit < max_x) {
|
||||
max_x = limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (max_x > last->x) {
|
||||
last->x += (max_x - last->x) / 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float box_w = 0, box_h = 0;
|
||||
for (int k = 0; k < n; k++) {
|
||||
float r = placed[k].x + placed[k].w;
|
||||
float b = placed[k].y + placed[k].h;
|
||||
if (r > box_w)
|
||||
box_w = r;
|
||||
if (b > box_h)
|
||||
box_h = b;
|
||||
}
|
||||
|
||||
float dx = (max_avail_w - box_w) / 2.0f;
|
||||
float dy = (max_avail_h - box_h) / 2.0f;
|
||||
float base_x = m->w.x + target_gappo + dx;
|
||||
float base_y = m->w.y + target_gappo + dy;
|
||||
|
||||
for (int k = 0; k < n; k++) {
|
||||
Client *cl = items[k].c;
|
||||
struct wlr_box geom;
|
||||
geom.x = (int)(base_x + placed[k].x + 0.5f);
|
||||
geom.y = (int)(base_y + placed[k].y + 0.5f);
|
||||
float w = items[k].orig_w * best_s;
|
||||
float h = items[k].orig_h * best_s;
|
||||
geom.width = (int)(geom.x + w + 0.5f) - geom.x;
|
||||
geom.height = (int)(geom.y + h + 0.5f) - geom.y;
|
||||
resize(cl, geom, 0);
|
||||
}
|
||||
}
|
||||
|
||||
free(items);
|
||||
free(placed);
|
||||
free(cands);
|
||||
free(feas);
|
||||
}
|
||||
|
||||
void overview_resize(Monitor *m) {
|
||||
int32_t target_gappo = config.overviewgappo;
|
||||
int32_t target_gappi = config.overviewgappi;
|
||||
float single_width_ratio = 0.7f;
|
||||
float single_height_ratio = 0.8f;
|
||||
|
||||
int orig_n = m->visible_clients;
|
||||
if (orig_n == 0)
|
||||
return;
|
||||
|
||||
Client **c_arr = malloc(orig_n * sizeof(Client *));
|
||||
if (!c_arr)
|
||||
return;
|
||||
|
||||
int n = 0;
|
||||
Client *c;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
if (VISIBLEON(c, m) && !c->isunglobal && !client_is_x11_popup(c)) {
|
||||
c_arr[n++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
free(c_arr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == 1) {
|
||||
int32_t cw = (m->w.width - 2 * target_gappo) * single_width_ratio;
|
||||
int32_t ch = (m->w.height - 2 * target_gappo) * single_height_ratio;
|
||||
c_arr[0]->geom.x = m->w.x + (m->w.width - cw) / 2;
|
||||
c_arr[0]->geom.y = m->w.y + (m->w.height - ch) / 2;
|
||||
c_arr[0]->geom.width = cw;
|
||||
c_arr[0]->geom.height = ch;
|
||||
resize(c_arr[0], c_arr[0]->geom, 0);
|
||||
free(c_arr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == 2) {
|
||||
int32_t cw = (m->w.width - 2 * target_gappo - target_gappi) / 2;
|
||||
int32_t ch = (m->w.height - 2 * target_gappo) * 0.65f;
|
||||
|
||||
c_arr[0]->geom.x = m->w.x + target_gappo;
|
||||
c_arr[0]->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
|
||||
c_arr[0]->geom.width = cw;
|
||||
c_arr[0]->geom.height = ch;
|
||||
resize(c_arr[0], c_arr[0]->geom, 0);
|
||||
|
||||
c_arr[1]->geom.x = m->w.x + cw + target_gappo + target_gappi;
|
||||
c_arr[1]->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
|
||||
c_arr[1]->geom.width = cw;
|
||||
c_arr[1]->geom.height = ch;
|
||||
resize(c_arr[1], c_arr[1]->geom, 0);
|
||||
|
||||
free(c_arr);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t cols = 1;
|
||||
while (cols * cols < n) {
|
||||
cols++;
|
||||
}
|
||||
int32_t rows = (n + cols - 1) / cols;
|
||||
|
||||
int32_t ch =
|
||||
(m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows;
|
||||
int32_t cw =
|
||||
(m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols;
|
||||
|
||||
if (ch < 1)
|
||||
ch = 1;
|
||||
if (cw < 1)
|
||||
cw = 1;
|
||||
|
||||
int32_t overcols = n % cols;
|
||||
int32_t dx = 0;
|
||||
if (overcols) {
|
||||
dx = (m->w.width - overcols * cw - (overcols - 1) * target_gappi) / 2 -
|
||||
target_gappo;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int32_t cx = m->w.x + (i % cols) * (cw + target_gappi);
|
||||
int32_t cy = m->w.y + (i / cols) * (ch + target_gappi);
|
||||
|
||||
if (overcols && i >= n - overcols) {
|
||||
cx += dx;
|
||||
}
|
||||
|
||||
c_arr[i]->geom.x = cx + target_gappo;
|
||||
c_arr[i]->geom.y = cy + target_gappo;
|
||||
c_arr[i]->geom.width = cw;
|
||||
c_arr[i]->geom.height = ch;
|
||||
resize(c_arr[i], c_arr[i]->geom, 0);
|
||||
}
|
||||
|
||||
free(c_arr);
|
||||
}
|
||||
|
||||
void overview(Monitor *m) {
|
||||
if (config.ov_no_resize) {
|
||||
overview_scale(m);
|
||||
} else {
|
||||
overview_resize(m);
|
||||
}
|
||||
}
|
||||
1063
src/layout/scroll.h
1063
src/layout/scroll.h
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,12 @@
|
|||
void vertical_tile(Monitor *m) {
|
||||
int32_t i, n = 0, w, r, ie = enablegaps, mh, mx, tx;
|
||||
uint32_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;
|
||||
int master_num = 0;
|
||||
int stack_num = 0;
|
||||
|
||||
n = m->visible_fake_tiling_clients;
|
||||
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;
|
||||
|
|
@ -14,22 +14,18 @@ void vertical_tile(Monitor *m) {
|
|||
if (n == 0)
|
||||
return;
|
||||
|
||||
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;
|
||||
uint32_t cur_gapih = enablegaps ? m->gappih : 0;
|
||||
uint32_t cur_gapiv = enablegaps ? m->gappiv : 0;
|
||||
uint32_t cur_gapoh = enablegaps ? m->gappoh : 0;
|
||||
uint32_t cur_gapov = enablegaps ? m->gappov : 0;
|
||||
|
||||
cur_gapih =
|
||||
config.smartgaps && m->visible_fake_tiling_clients == 1 ? 0 : cur_gapih;
|
||||
cur_gapiv =
|
||||
config.smartgaps && m->visible_fake_tiling_clients == 1 ? 0 : cur_gapiv;
|
||||
cur_gapoh =
|
||||
config.smartgaps && m->visible_fake_tiling_clients == 1 ? 0 : cur_gapoh;
|
||||
cur_gapov =
|
||||
config.smartgaps && m->visible_fake_tiling_clients == 1 ? 0 : cur_gapov;
|
||||
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;
|
||||
|
||||
wl_list_for_each(fc, &clients, link) {
|
||||
if (VISIBLEON(fc, m) && ISFAKETILED(fc))
|
||||
if (VISIBLEON(fc, m) && ISTILED(fc))
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -44,18 +40,18 @@ void vertical_tile(Monitor *m) {
|
|||
mh = m->w.height - 2 * cur_gapov + cur_gapiv * ie;
|
||||
|
||||
i = 0;
|
||||
mx = tx = cur_gapoh;
|
||||
mx = tx = cur_gapih;
|
||||
|
||||
int32_t master_surplus_width =
|
||||
(m->w.width - 2 * cur_gapoh - cur_gapih * ie * (master_num - 1));
|
||||
uint32_t master_surplus_width =
|
||||
(m->w.width - 2 * cur_gapih - 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));
|
||||
uint32_t slave_surplus_width =
|
||||
(m->w.width - 2 * cur_gapih - cur_gapih * ie * (stack_num - 1));
|
||||
float slave_surplus_ratio = 1.0;
|
||||
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (!VISIBLEON(c, m) || !ISFAKETILED(c))
|
||||
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;
|
||||
|
|
@ -73,13 +69,13 @@ void vertical_tile(Monitor *m) {
|
|||
cur_gapih * ie * (r - 1));
|
||||
c->master_mfact_per = mfact;
|
||||
}
|
||||
client_tile_resize(c,
|
||||
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 += w + cur_gapih * ie; // 使用理论宽度累加
|
||||
mx += c->geom.width + cur_gapih * ie;
|
||||
} else {
|
||||
r = n - i;
|
||||
if (c->stack_inner_per > 0.0f) {
|
||||
|
|
@ -96,83 +92,74 @@ void vertical_tile(Monitor *m) {
|
|||
c->master_mfact_per = mfact;
|
||||
}
|
||||
|
||||
client_tile_resize(
|
||||
c,
|
||||
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 += w + cur_gapih * ie; // 使用理论宽度累加
|
||||
tx += c->geom.width + cur_gapih * ie;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void vertical_deck(Monitor *m) {
|
||||
int32_t mh, mx;
|
||||
int32_t i, n = 0;
|
||||
uint32_t mh, mx;
|
||||
int 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;
|
||||
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||
uint32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||
|
||||
cur_gappiv = config.smartgaps && m->visible_fake_tiling_clients == 1
|
||||
? 0
|
||||
: cur_gappiv;
|
||||
cur_gappoh = config.smartgaps && m->visible_fake_tiling_clients == 1
|
||||
? 0
|
||||
: cur_gappoh;
|
||||
cur_gappov = config.smartgaps && m->visible_fake_tiling_clients == 1
|
||||
? 0
|
||||
: cur_gappov;
|
||||
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;
|
||||
|
||||
n = m->visible_fake_tiling_clients;
|
||||
n = m->visible_tiling_clients;
|
||||
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
wl_list_for_each(fc, &clients, link) {
|
||||
if (VISIBLEON(fc, m) && ISFAKETILED(fc))
|
||||
|
||||
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;
|
||||
if (n > m->nmaster)
|
||||
mh = m->nmaster ? round((m->w.height - 2 * cur_gappov) * mfact) : 0;
|
||||
else
|
||||
mh = m->w.height - 2 * cur_gappov;
|
||||
|
||||
i = mx = 0;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (!VISIBLEON(c, m) || !ISFAKETILED(c))
|
||||
if (!VISIBLEON(c, m) || !ISTILED(c))
|
||||
continue;
|
||||
if (i < nmasters) {
|
||||
c->master_mfact_per = mfact;
|
||||
int32_t w =
|
||||
(m->w.width - 2 * cur_gappoh - mx) / (MIN(n, nmasters) - i);
|
||||
client_tile_resize(c,
|
||||
if (i < m->nmaster) {
|
||||
resize(
|
||||
c,
|
||||
(struct wlr_box){.x = m->w.x + cur_gappoh + mx,
|
||||
.y = m->w.y + cur_gappov,
|
||||
.width = w,
|
||||
.width = (m->w.width - 2 * cur_gappoh - mx) /
|
||||
(MIN(n, m->nmaster) - i),
|
||||
.height = mh},
|
||||
0);
|
||||
mx += w;
|
||||
mx += c->geom.width;
|
||||
} else {
|
||||
c->master_mfact_per = mfact;
|
||||
client_tile_resize(
|
||||
c,
|
||||
resize(c,
|
||||
(struct wlr_box){.x = m->w.x + cur_gappoh,
|
||||
.y = m->w.y + mh + cur_gappov + cur_gappiv,
|
||||
.width = m->w.width - 2 * cur_gappoh,
|
||||
.height = m->w.height - mh - 2 * cur_gappov -
|
||||
cur_gappiv},
|
||||
.height = m->w.height - mh -
|
||||
2 * cur_gappov - cur_gappiv},
|
||||
0);
|
||||
if (c == focustop(m))
|
||||
wlr_scene_node_raise_to_top(&c->scene->node);
|
||||
|
|
@ -181,345 +168,292 @@ void vertical_deck(Monitor *m) {
|
|||
}
|
||||
}
|
||||
|
||||
void vertical_grid(Monitor *m) {
|
||||
int32_t i, n;
|
||||
int32_t cw, ch;
|
||||
int32_t rows, cols, overrows;
|
||||
Client *c = NULL;
|
||||
int32_t target_gappo = enablegaps ? config.gappov : 0;
|
||||
int32_t target_gappi = enablegaps ? config.gappiv : 0;
|
||||
float single_width_ratio = 0.9;
|
||||
float single_height_ratio = 0.9;
|
||||
struct wlr_box target_geom;
|
||||
void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) {
|
||||
Monitor *m = c->mon;
|
||||
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||
uint32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||
|
||||
n = m->visible_fake_tiling_clients;
|
||||
if (n == 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;
|
||||
|
||||
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 vertical_scroller(Monitor *m) {
|
||||
uint32_t i, n, j;
|
||||
float single_proportion = 1.0;
|
||||
|
||||
Client *c = NULL, *root_client = NULL;
|
||||
Client **tempClients = NULL;
|
||||
struct wlr_box target_geom;
|
||||
int focus_client_index = 0;
|
||||
bool need_scroller = false;
|
||||
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||
uint32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||
uint32_t cur_gappoh = enablegaps ? m->gappoh : 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;
|
||||
|
||||
uint32_t max_client_height =
|
||||
m->w.height - 2 * scroller_structs - cur_gappiv;
|
||||
|
||||
n = m->visible_scroll_tiling_clients;
|
||||
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tempClients = malloc(n * sizeof(Client *));
|
||||
if (!tempClients) {
|
||||
return;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (VISIBLEON(c, m) && ISSCROLLTILED(c)) {
|
||||
tempClients[j] = c;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
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) * single_proportion;
|
||||
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
||||
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
||||
resize(c, target_geom, 0);
|
||||
free(tempClients);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m->sel && !client_is_unmanaged(m->sel) && ISSCROLLTILED(m->sel)) {
|
||||
root_client = m->sel;
|
||||
} else if (m->prevsel && ISSCROLLTILED(m->prevsel) &&
|
||||
VISIBLEON(m->prevsel, m) && !client_is_unmanaged(m->prevsel)) {
|
||||
root_client = m->prevsel;
|
||||
} else {
|
||||
root_client = center_tiled_select(m);
|
||||
}
|
||||
|
||||
if (!root_client) {
|
||||
free(tempClients);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
c = tempClients[i];
|
||||
if (root_client == c) {
|
||||
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;
|
||||
} else {
|
||||
need_scroller = true;
|
||||
}
|
||||
focus_client_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 (tempClients[focus_client_index]->isfullscreen) {
|
||||
target_geom.y = m->m.y;
|
||||
resize(tempClients[focus_client_index], target_geom, 0);
|
||||
} else if (tempClients[focus_client_index]->ismaximizescreen) {
|
||||
target_geom.y = m->w.y + cur_gappov;
|
||||
resize(tempClients[focus_client_index], target_geom, 0);
|
||||
} else if (need_scroller) {
|
||||
if (scroller_focus_center ||
|
||||
((!m->prevsel ||
|
||||
(ISSCROLLTILED(m->prevsel) &&
|
||||
(m->prevsel->scroller_proportion * max_client_height) +
|
||||
(root_client->scroller_proportion * max_client_height) >
|
||||
m->w.height - 2 * scroller_structs - cur_gappiv)) &&
|
||||
scroller_prefer_center)) {
|
||||
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
||||
} 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 *
|
||||
max_client_height -
|
||||
scroller_structs)
|
||||
: m->w.y + scroller_structs;
|
||||
}
|
||||
resize(tempClients[focus_client_index], target_geom, 0);
|
||||
} else {
|
||||
target_geom.y = c->geom.y;
|
||||
resize(tempClients[focus_client_index], target_geom, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
free(tempClients);
|
||||
}
|
||||
|
||||
void vertical_grid(Monitor *m) {
|
||||
uint32_t i, n;
|
||||
uint32_t cx, cy, cw, ch;
|
||||
uint32_t dy;
|
||||
uint32_t rows, cols, overrows;
|
||||
Client *c = NULL;
|
||||
int target_gappo = enablegaps ? m->isoverview ? overviewgappo : gappov : 0;
|
||||
int 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;
|
||||
|
||||
n = m->isoverview ? m->visible_clients : m->visible_tiling_clients;
|
||||
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == 1) {
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
|
||||
if (VISIBLEON(c, m) && !c->isunglobal &&
|
||||
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
|
||||
((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;
|
||||
target_geom.x = m->w.x + (m->w.width - cw) / 2;
|
||||
target_geom.y = m->w.y + (m->w.height - ch) / 2;
|
||||
target_geom.width = cw;
|
||||
target_geom.height = ch;
|
||||
client_tile_resize(c, target_geom, 0);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 2) {
|
||||
float row_pers[2] = {1.0f, 1.0f};
|
||||
// 先提取这两个窗口现有的行比例
|
||||
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 &&
|
||||
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
|
||||
if (i < 2)
|
||||
row_pers[i] =
|
||||
(c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
float sum_row = row_pers[0] + row_pers[1];
|
||||
float avail_h = m->w.height - 2 * target_gappo - target_gappi;
|
||||
cw = (m->w.width - 2 * target_gappo) * 0.65; // 依然保持 0.65 的美观宽度
|
||||
|
||||
i = 0;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
if (VISIBLEON(c, m) && !c->isunglobal &&
|
||||
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
|
||||
c->grid_col_idx = 0;
|
||||
c->grid_row_idx = i;
|
||||
c->grid_col_per = 1.0f;
|
||||
c->grid_row_per = row_pers[i];
|
||||
|
||||
// 根据分配的权重动态计算当前窗口的高度
|
||||
ch = avail_h * (row_pers[i] / sum_row);
|
||||
|
||||
target_geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo;
|
||||
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
|
||||
if (i == 0) {
|
||||
target_geom.y = m->w.y + target_gappo;
|
||||
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) {
|
||||
// 第二个窗口的 Y 坐标紧跟第一个窗口下面
|
||||
float ch0 = avail_h * (row_pers[0] / sum_row);
|
||||
target_geom.y = m->w.y + target_gappo + ch0 + target_gappi;
|
||||
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);
|
||||
}
|
||||
target_geom.width = cw;
|
||||
target_geom.height = ch;
|
||||
client_tile_resize(c, target_geom, 0);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (rows = 0; rows <= n / 2; rows++) {
|
||||
if (rows * rows >= n)
|
||||
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;
|
||||
|
||||
float col_pers[cols];
|
||||
float row_pers[rows];
|
||||
for (i = 0; i < cols; i++)
|
||||
col_pers[i] = 1.0f;
|
||||
for (i = 0; i < rows; i++)
|
||||
row_pers[i] = 1.0f;
|
||||
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 (c->mon != m)
|
||||
continue;
|
||||
|
||||
if (VISIBLEON(c, m) && !c->isunglobal &&
|
||||
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
|
||||
int32_t c_idx = i / rows;
|
||||
int32_t r_idx = i % rows;
|
||||
if (r_idx == 0)
|
||||
col_pers[c_idx] =
|
||||
(c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
|
||||
if (c_idx == 0)
|
||||
row_pers[r_idx] =
|
||||
(c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
float sum_col = 0.0f, sum_row = 0.0f;
|
||||
for (i = 0; i < cols; i++)
|
||||
sum_col += col_pers[i];
|
||||
for (i = 0; i < rows; i++)
|
||||
sum_row += row_pers[i];
|
||||
|
||||
float avail_w = m->w.width - 2 * target_gappo - (cols - 1) * target_gappi;
|
||||
float avail_h = m->w.height - 2 * target_gappo - (rows - 1) * target_gappi;
|
||||
|
||||
i = 0;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
if (VISIBLEON(c, m) && !c->isunglobal &&
|
||||
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
|
||||
int32_t c_idx = i / rows;
|
||||
int32_t r_idx = i % rows;
|
||||
|
||||
c->grid_col_per = col_pers[c_idx];
|
||||
c->grid_row_per = row_pers[r_idx];
|
||||
c->grid_col_idx = c_idx;
|
||||
c->grid_row_idx = r_idx;
|
||||
|
||||
float fl_cy = m->w.y + target_gappo;
|
||||
float fl_ch = 0.0f;
|
||||
|
||||
((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) {
|
||||
float over_h = 0.0f;
|
||||
for (int j = 0; j < overrows; j++)
|
||||
over_h += avail_h * (row_pers[j] / sum_row);
|
||||
over_h += (overrows - 1) * target_gappi;
|
||||
float dy = (m->w.height - over_h) / 2.0f - target_gappo;
|
||||
|
||||
fl_cy += dy;
|
||||
for (int j = 0; j < r_idx; j++)
|
||||
fl_cy += avail_h * (row_pers[j] / sum_row) + target_gappi;
|
||||
fl_ch = avail_h * (row_pers[r_idx] / sum_row);
|
||||
} else {
|
||||
for (int j = 0; j < r_idx; j++)
|
||||
fl_cy += avail_h * (row_pers[j] / sum_row) + target_gappi;
|
||||
fl_ch = (r_idx == rows - 1)
|
||||
? (m->w.y + m->w.height - target_gappo - fl_cy)
|
||||
: avail_h * (row_pers[r_idx] / sum_row);
|
||||
cy += dy;
|
||||
}
|
||||
|
||||
float fl_cx = m->w.x + target_gappo;
|
||||
for (int j = 0; j < c_idx; j++)
|
||||
fl_cx += avail_w * (col_pers[j] / sum_col) + target_gappi;
|
||||
float fl_cw = (c_idx == cols - 1)
|
||||
? (m->w.x + m->w.width - target_gappo - fl_cx)
|
||||
: avail_w * (col_pers[c_idx] / sum_col);
|
||||
|
||||
target_geom.x = (int32_t)fl_cx;
|
||||
target_geom.y = (int32_t)fl_cy;
|
||||
target_geom.width = (int32_t)fl_cw;
|
||||
target_geom.height = (int32_t)fl_ch;
|
||||
client_tile_resize(c, target_geom, 0);
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vertical_fair(Monitor *m) {
|
||||
int32_t i, n = 0;
|
||||
Client *c = NULL;
|
||||
|
||||
n = m->visible_fake_tiling_clients;
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||
|
||||
if (config.smartgaps && n == 1) {
|
||||
cur_gappiv = cur_gappih = cur_gappov = cur_gappoh = 0;
|
||||
}
|
||||
|
||||
int32_t rows;
|
||||
for (rows = 0; rows <= n; rows++) {
|
||||
if (rows * rows >= n)
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t base_cols = n / rows;
|
||||
int32_t remainder = n % rows;
|
||||
int32_t first_group_rows = rows - remainder;
|
||||
int32_t first_group_count = first_group_rows * base_cols;
|
||||
int32_t max_cols = base_cols + (remainder > 0 ? 1 : 0);
|
||||
|
||||
Client *arr[n];
|
||||
int32_t arr_idx = 0;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (VISIBLEON(c, m) && ISFAKETILED(c)) {
|
||||
arr[arr_idx++] = c;
|
||||
if (arr_idx >= n)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float row_pers[rows];
|
||||
float col_pers[max_cols];
|
||||
for (i = 0; i < rows; i++)
|
||||
row_pers[i] = 0.0f;
|
||||
for (i = 0; i < max_cols; i++)
|
||||
col_pers[i] = 0.0f;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
c = arr[i];
|
||||
int32_t row_idx =
|
||||
(i < first_group_count)
|
||||
? (i / base_cols)
|
||||
: (first_group_rows + (i - first_group_count) / max_cols);
|
||||
int32_t col_idx = (i < first_group_count)
|
||||
? (i % base_cols)
|
||||
: ((i - first_group_count) % max_cols);
|
||||
|
||||
if (c->grid_row_idx == row_idx && c->grid_row_per > 0.0f)
|
||||
row_pers[row_idx] = c->grid_row_per;
|
||||
if (c->grid_col_idx == col_idx && c->grid_col_per > 0.0f)
|
||||
col_pers[col_idx] = c->grid_col_per;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
c = arr[i];
|
||||
int32_t row_idx =
|
||||
(i < first_group_count)
|
||||
? (i / base_cols)
|
||||
: (first_group_rows + (i - first_group_count) / max_cols);
|
||||
int32_t col_idx = (i < first_group_count)
|
||||
? (i % base_cols)
|
||||
: ((i - first_group_count) % max_cols);
|
||||
|
||||
if (row_pers[row_idx] == 0.0f && c->grid_row_per > 0.0f)
|
||||
row_pers[row_idx] = c->grid_row_per;
|
||||
if (col_pers[col_idx] == 0.0f && c->grid_col_per > 0.0f)
|
||||
col_pers[col_idx] = c->grid_col_per;
|
||||
}
|
||||
|
||||
float sum_row = 0.0f;
|
||||
for (i = 0; i < rows; i++) {
|
||||
if (row_pers[i] == 0.0f)
|
||||
row_pers[i] = 1.0f;
|
||||
sum_row += row_pers[i];
|
||||
}
|
||||
for (i = 0; i < max_cols; i++) {
|
||||
if (col_pers[i] == 0.0f)
|
||||
col_pers[i] = 1.0f;
|
||||
}
|
||||
|
||||
float row_y[rows], row_h[rows];
|
||||
float avail_h = m->w.height - 2 * cur_gappov - (rows - 1) * cur_gappiv;
|
||||
float next_y = m->w.y + cur_gappov;
|
||||
for (i = 0; i < rows; i++) {
|
||||
row_y[i] = next_y;
|
||||
row_h[i] = (i == rows - 1)
|
||||
? (m->w.y + m->w.height - cur_gappov - next_y)
|
||||
: (avail_h * (row_pers[i] / sum_row));
|
||||
next_y += row_h[i] + cur_gappiv;
|
||||
}
|
||||
|
||||
float col_x_base[base_cols], col_w_base[base_cols];
|
||||
float sum_col_base = 0.0f;
|
||||
for (i = 0; i < base_cols; i++)
|
||||
sum_col_base += col_pers[i];
|
||||
float avail_w_base =
|
||||
m->w.width - 2 * cur_gappoh - (base_cols - 1) * cur_gappih;
|
||||
float next_x = m->w.x + cur_gappoh;
|
||||
for (i = 0; i < base_cols; i++) {
|
||||
col_x_base[i] = next_x;
|
||||
col_w_base[i] = (i == base_cols - 1)
|
||||
? (m->w.x + m->w.width - cur_gappoh - next_x)
|
||||
: (avail_w_base * (col_pers[i] / sum_col_base));
|
||||
next_x += col_w_base[i] + cur_gappih;
|
||||
}
|
||||
|
||||
float col_x_max[max_cols], col_w_max[max_cols];
|
||||
if (remainder > 0) {
|
||||
float sum_col_max = 0.0f;
|
||||
for (i = 0; i < max_cols; i++)
|
||||
sum_col_max += col_pers[i];
|
||||
float avail_w_max =
|
||||
m->w.width - 2 * cur_gappoh - (max_cols - 1) * cur_gappih;
|
||||
next_x = m->w.x + cur_gappoh;
|
||||
for (i = 0; i < max_cols; i++) {
|
||||
col_x_max[i] = next_x;
|
||||
col_w_max[i] = (i == max_cols - 1)
|
||||
? (m->w.x + m->w.width - cur_gappoh - next_x)
|
||||
: (avail_w_max * (col_pers[i] / sum_col_max));
|
||||
next_x += col_w_max[i] + cur_gappih;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
c = arr[i];
|
||||
int32_t row_idx, col_idx;
|
||||
float fl_cx, fl_cy, fl_cw, fl_ch;
|
||||
|
||||
if (i < first_group_count) {
|
||||
row_idx = i / base_cols;
|
||||
col_idx = i % base_cols;
|
||||
fl_cx = col_x_base[col_idx];
|
||||
fl_cw = col_w_base[col_idx];
|
||||
} else {
|
||||
int32_t offset = i - first_group_count;
|
||||
row_idx = first_group_rows + (offset / max_cols);
|
||||
col_idx = offset % max_cols;
|
||||
fl_cx = col_x_max[col_idx];
|
||||
fl_cw = col_w_max[col_idx];
|
||||
}
|
||||
|
||||
c->grid_row_per = row_pers[row_idx];
|
||||
c->grid_col_per = col_pers[col_idx];
|
||||
c->grid_row_idx = row_idx;
|
||||
c->grid_col_idx = col_idx;
|
||||
|
||||
fl_cy = row_y[row_idx];
|
||||
fl_ch = row_h[row_idx];
|
||||
|
||||
client_tile_resize(c,
|
||||
(struct wlr_box){.x = (int32_t)fl_cx,
|
||||
.y = (int32_t)fl_cy,
|
||||
.width = (int32_t)fl_cw,
|
||||
.height = (int32_t)fl_ch},
|
||||
0);
|
||||
}
|
||||
}
|
||||
2890
src/mango.c
2890
src/mango.c
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue