diff --git a/README.md b/README.md
index 20ac1c78..087bf13b 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
-# MangoWC
-
-
-
+# Mango Wayland Compositor
+
+

+
This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
@@ -16,18 +16,14 @@ This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
- Base tags not workspaces (supports separate window layouts for each tag)
- Smooth and customizable complete animations (window open/move/close, tag enter/leave,layer open/close/move)
- Excellent input method support (text input v2/v3)
- - Flexible window layouts with easy switching (scroller, master, monocle, spiral, etc.)
+ - Flexible window layouts with easy switching (scroller, master-stack, monocle,center-master, etc.)
- Rich window states (swallow, minimize, maximize, unglobal, global, fakefullscreen, overlay, etc.)
- - Simple yet powerful external configuration
+ - Simple yet powerful external configuration(support shortcuts hot-reload)
- Sway-like scratchpad and named scratchpad
- - Minimize window to scratchpad
+ - Ipc support(get/send message from/to compositor by external program)
- Hycov-like overview
- Window effects from scenefx (blur, shadow, corner radius, opacity)
-3. **Some disadvantages**
- - Since it uses the fully automatic layout like dwm style, it does not allow you to manually adjust the window size when the window is in tiled state. It only allows you to change the layout parameters to adjust the window ratio.
-
-
Master-Stack Layout
https://github.com/user-attachments/assets/a9d4776e-b50b-48fb-94ce-651d8a749b8a
@@ -36,30 +32,25 @@ Scroller Layout
https://github.com/user-attachments/assets/c9bf9415-fad1-4400-bcdc-3ad2d76de85a
-Layer animaiton
+Layer animation
https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
+# Our discord
+[mangowc](https://discord.gg/CPjbDxesh5)
+
# Supported layouts
-## Horizontal Layouts
- tile
- scroller
- monocle
- grid
-- dwindle
-- spiral
- deck
-
-## Vertical Layouts
+- center_tile
- vertical_tile
-- vertical_scroller
-- vertical_monocle
- vertical_grid
-- vertical_dwindle
-- vertical_spiral
-- vertical_deck
+- vertical_scroller
# Installation
@@ -80,16 +71,17 @@ https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
- 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:
@@ -107,19 +99,25 @@ Finally, install the package:
emerge --ask --verbose gui-wm/mangowc
```
-Patching wlroots is done by getting the patch with git from [the repository](https://github.com/DreamMaoMao/wlroots.git)
-and then copying it to `/etc/portage/patches/gui-libs/wlroots/`.
+## Fedora Linux
+The package is in the third-party Terra repository.
+First, add the [Terra Repository](https://terra.fyralabs.com/).
+
+Then, install the package:
+
+```bash
+dnf install mangowc
+```
## Other
```bash
-# wlroots 0.19.0 release with a fix-patch to avoid crash
-git clone https://github.com/DreamMaoMao/wlroots.git
+git clone -b 0.19.2 https://gitlab.freedesktop.org/wlroots/wlroots.git
cd wlroots
meson build -Dprefix=/usr
sudo ninja -C build install
-git clone https://github.com/wlrfx/scenefx.git
+git clone -b 0.4.1 https://github.com/wlrfx/scenefx.git
cd scenefx
meson build -Dprefix=/usr
sudo ninja -C build install
@@ -132,7 +130,11 @@ sudo ninja -C build install
## Suggested Tools
-- Application launcher (rofi-wayland, bemenu, wmenu, fuzzel)
+### Hybrid component
+- [dms-shell](https://github.com/AvengeMedia/DankMaterialShell)
+
+### Independent component
+- Application launcher (rofi, bemenu, wmenu, fuzzel)
- Terminal emulator (foot, wezterm, alacritty, kitty, ghostty)
- Status bar (waybar, eww, quickshell, ags), waybar is preferred
- Wallpaper setup (swww, swaybg)
@@ -152,13 +154,25 @@ sudo ninja -C build install
## My Dotfiles
+### Daily
- Dependencies
```bash
-yay -S rofi-wayland foot xdg-desktop-portal-wlr swaybg waybar wl-clip-persist cliphist wl-clipboard wlsunset xfce-polkit swaync pamixer wlr-dpms sway-audio-idle-inhibit-git swayidle dimland-git brightnessctl swayosd wlr-randr grim slurp satty swaylock-effects-git wlogout sox
+yay -S rofi foot xdg-desktop-portal-wlr swaybg waybar wl-clip-persist cliphist wl-clipboard wlsunset xfce-polkit swaync pamixer wlr-dpms sway-audio-idle-inhibit-git swayidle dimland-git brightnessctl swayosd wlr-randr grim slurp satty swaylock-effects-git wlogout sox
```
-- use my config
+### Dms
+- Dependencies
+```bash
+yay -S foot xdg-desktop-portal-wlr swaybg wl-clip-persist cliphist wl-clipboard sway-audio-idle-inhibit-git brightnessctl grim slurp satty matugen-bin dms-shell-git
+
+```
+- use my dms config
+
+```bash
+git clone -b dms https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
+```
+- use my daily config
```bash
git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
@@ -167,7 +181,9 @@ git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
## Config Documentation
-Refer to the [wiki](https://github.com/DreamMaoMao/mango/wiki/)
+Refer to the repo wiki [wiki](https://github.com/DreamMaoMao/mango/wiki/)
+
+or the website docs [docs](https://mangowc.vercel.app/docs)
# NixOS + Home-manager
@@ -186,7 +202,10 @@ Here's an example of using the modules in a flake:
inputs.nixpkgs.follows = "nixpkgs";
};
flake-parts.url = "github:hercules-ci/flake-parts";
- mango.url = "github:DreamMaoMao/mango";
+ mango = {
+ url = "github:DreamMaoMao/mango";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
};
outputs =
inputs@{ self, flake-parts, ... }:
@@ -246,13 +265,11 @@ Here's an example of using the modules in a flake:
To package mango for other distributions, you can check the reference setup for:
-- [nix](https://github.com/DreamMaoMao/mango/blob/main/nix/default.nix)
-- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mango-git).
+- [nix](https://github.com/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)
-Currently building mango requires a patched version of `wlroots-0.19`. If possible, the patch can be extracted from the [latest commit](https://github.com/DreamMaoMao/wlroots.git)
-and applied on `prepare` step. If it is not possible, you will need to create a separate `wlroots` package and make it a build dependency.
-You might also need to package `scenefx` for your distribution, check availability [here](https://github.com/wlrfx/scenefx.git).
+You might need to package `scenefx` for your distribution, check availability [here](https://github.com/wlrfx/scenefx.git).
If you encounter build errors when packaging `mango`, feel free to create an issue and ask a question, but
Read The Friendly Manual on packaging software in your distribution first.
diff --git a/assets/mango-black.png b/assets/mango-black.png
deleted file mode 100644
index fabfe017..00000000
Binary files a/assets/mango-black.png and /dev/null differ
diff --git a/assets/mango-transparency-128.png b/assets/mango-transparency-128.png
index 7b9b4c64..a995f573 100644
Binary files a/assets/mango-transparency-128.png and b/assets/mango-transparency-128.png differ
diff --git a/assets/mango-transparency-256.png b/assets/mango-transparency-256.png
index 3bb4d653..974ab3f6 100644
Binary files a/assets/mango-transparency-256.png and b/assets/mango-transparency-256.png differ
diff --git a/assets/mango-transparency.png b/assets/mango-transparency.png
index 25e53856..488a31ec 100644
Binary files a/assets/mango-transparency.png and b/assets/mango-transparency.png differ
diff --git a/assets/mango.png b/assets/mango.png
index eb147404..5a8136f7 100644
Binary files a/assets/mango.png and b/assets/mango.png differ
diff --git a/config.conf b/config.conf
index d15cfdd8..5483a141 100644
--- a/config.conf
+++ b/config.conf
@@ -28,7 +28,7 @@ unfocused_opacity=1.0
# Animation Configuration(support type:zoom,slide)
# tag_animation_direction: 0-horizontal,1-vertical
animations=1
-layer_animations=0
+layer_animations=1
animation_type_open=slide
animation_type_close=slide
animation_fade_in=1
@@ -42,20 +42,25 @@ animation_duration_move=500
animation_duration_open=400
animation_duration_tag=350
animation_duration_close=800
+animation_duration_focus=0
animation_curve_open=0.46,1.0,0.29,1
animation_curve_move=0.46,1.0,0.29,1
animation_curve_tag=0.46,1.0,0.29,1
animation_curve_close=0.08,0.92,0,1
+animation_curve_focus=0.46,1.0,0.29,1
+animation_curve_opafadeout=0.5,0.5,0.5,0.5
+animation_curve_opafadein=0.46,1.0,0.29,1
# Scroller Layout Setting
scroller_structs=20
scroller_default_proportion=0.8
scroller_focus_center=0
scroller_prefer_center=0
+edge_scroller_pointer_focus=1
scroller_default_proportion_single=1.0
scroller_proportion_preset=0.5,0.8,1.0
-# Master-Stack Layout Setting (tile,spiral,dwindle)
+# Master-Stack Layout Setting
new_is_master=1
default_mfact=0.55
default_nmaster=1
@@ -72,7 +77,7 @@ overviewgappo=30
no_border_when_single=0
axis_bind_apply_timeout=100
focus_on_activate=1
-inhibit_regardless_of_visibility=0
+idleinhibit_ignore_visible=0
sloppyfocus=1
warpcursor=1
focus_cross_monitor=0
@@ -85,7 +90,7 @@ drag_tile_to_tile=1
# keyboard
repeat_rate=25
repeat_delay=600
-numlockon=1
+numlockon=0
xkb_rules_layout=us
# Trackpad
@@ -115,15 +120,14 @@ borderpx=4
rootcolor=0x201b14ff
bordercolor=0x444444ff
focuscolor=0xc9b890ff
-maxmizescreencolor=0x89aa61ff
+maximizescreencolor=0x89aa61ff
urgentcolor=0xad401fff
scratchpadcolor=0x516c93ff
globalcolor=0xb153a7ff
overlaycolor=0x14a57cff
# layout support:
-# horizontal:tile,scroller,grid,monocle,spiral,dwindle
-# vertical:vertical_tile,vertical_scroller,vertical_grid,vertical_monocle,vertical_spiral,vertical_dwindle
+# tile,scroller,grid,deck,monocle,center_tile,vertical_tile,vertical_scroller
tagrule=id:1,layout_name:tile
tagrule=id:2,layout_name:tile
tagrule=id:3,layout_name:tile
@@ -166,12 +170,12 @@ bind=SUPER+SHIFT,Right,exchange_client,right
bind=SUPER,g,toggleglobal,
bind=ALT,Tab,toggleoverview,
bind=ALT,backslash,togglefloating,
-bind=ALT,a,togglemaxmizescreen,
+bind=ALT,a,togglemaximizescreen,
bind=ALT,f,togglefullscreen,
bind=ALT+SHIFT,f,togglefakefullscreen,
-bind=SUPER,i,minized,
+bind=SUPER,i,minimized,
bind=SUPER,o,toggleoverlay,
-bind=SUPER+SHIFT,I,restore_minized
+bind=SUPER+SHIFT,I,restore_minimized
bind=ALT,z,toggle_scratchpad
# scroller layout
@@ -182,35 +186,35 @@ bind=ALT,x,switch_proportion_preset,
bind=SUPER,n,switch_layout
# tag switch
-bind=SUPER,Left,viewtoleft,
-bind=CTRL,Left,viewtoleft_have_client,
-bind=SUPER,Right,viewtoright,
-bind=CTRL,Right,viewtoright_have_client,
-bind=CTRL+SUPER,Left,tagtoleft,
-bind=CTRL+SUPER,Right,tagtoright,
+bind=SUPER,Left,viewtoleft,0
+bind=CTRL,Left,viewtoleft_have_client,0
+bind=SUPER,Right,viewtoright,0
+bind=CTRL,Right,viewtoright_have_client,0
+bind=CTRL+SUPER,Left,tagtoleft,0
+bind=CTRL+SUPER,Right,tagtoright,0
-bind=Ctrl,1,view,1
-bind=Ctrl,2,view,2
-bind=Ctrl,3,view,3
-bind=Ctrl,4,view,4
-bind=Ctrl,5,view,5
-bind=Ctrl,6,view,6
-bind=Ctrl,7,view,7
-bind=Ctrl,8,view,8
-bind=Ctrl,9,view,9
+bind=Ctrl,1,view,1,0
+bind=Ctrl,2,view,2,0
+bind=Ctrl,3,view,3,0
+bind=Ctrl,4,view,4,0
+bind=Ctrl,5,view,5,0
+bind=Ctrl,6,view,6,0
+bind=Ctrl,7,view,7,0
+bind=Ctrl,8,view,8,0
+bind=Ctrl,9,view,9,0
# tag: move client to the tag and focus it
# tagsilent: move client to the tag and not focus it
# bind=Alt,1,tagsilent,1
-bind=Alt,1,tag,1
-bind=Alt,2,tag,2
-bind=Alt,3,tag,3
-bind=Alt,4,tag,4
-bind=Alt,5,tag,5
-bind=Alt,6,tag,6
-bind=Alt,7,tag,7
-bind=Alt,8,tag,8
-bind=Alt,9,tag,9
+bind=Alt,1,tag,1,0
+bind=Alt,2,tag,2,0
+bind=Alt,3,tag,3,0
+bind=Alt,4,tag,4,0
+bind=Alt,5,tag,5,0
+bind=Alt,6,tag,6,0
+bind=Alt,7,tag,7,0
+bind=Alt,8,tag,8,0
+bind=Alt,9,tag,9,0
# monitor switch
bind=alt+shift,Left,focusmon,left
@@ -238,11 +242,16 @@ bind=CTRL+ALT,Right,resizewin,+50,+0
# Mouse Button Bindings
# NONE mode key only work in ov mode
mousebind=SUPER,btn_left,moveresize,curmove
-mousebind=NONE,btn_middle,togglemaxmizescreen,0
+mousebind=NONE,btn_middle,togglemaximizescreen,0
mousebind=SUPER,btn_right,moveresize,curresize
-mousebind=NONE,btn_left,toggleoverview,-1
+mousebind=NONE,btn_left,toggleoverview,1
mousebind=NONE,btn_right,killclient,0
# Axis Bindings
axisbind=SUPER,UP,viewtoleft_have_client
axisbind=SUPER,DOWN,viewtoright_have_client
+
+
+# layer rule
+layerrule=animation_type_open:zoom,layer_name:rofi
+layerrule=animation_type_close:zoom,layer_name:rofi
diff --git a/flake.lock b/flake.lock
index aa3c3191..2917a6fd 100644
--- a/flake.lock
+++ b/flake.lock
@@ -18,45 +18,6 @@
"type": "github"
}
},
- "flake-parts_2": {
- "inputs": {
- "nixpkgs-lib": "nixpkgs-lib_2"
- },
- "locked": {
- "lastModified": 1741352980,
- "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
- "owner": "hercules-ci",
- "repo": "flake-parts",
- "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
- "type": "github"
- },
- "original": {
- "owner": "hercules-ci",
- "repo": "flake-parts",
- "type": "github"
- }
- },
- "mmsg": {
- "inputs": {
- "flake-parts": "flake-parts_2",
- "nixpkgs": [
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1753859411,
- "narHash": "sha256-xiQGpk987dCmeF29mClveaGJNIvljmJJ9FRHVPp92HU=",
- "owner": "DreamMaoMao",
- "repo": "mmsg",
- "rev": "6066d37d810bb16575c0b60e25852d1f6d50de60",
- "type": "github"
- },
- "original": {
- "owner": "DreamMaoMao",
- "repo": "mmsg",
- "type": "github"
- }
- },
"nixpkgs": {
"locked": {
"lastModified": 1750386251,
@@ -88,25 +49,9 @@
"type": "github"
}
},
- "nixpkgs-lib_2": {
- "locked": {
- "lastModified": 1740877520,
- "narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=",
- "owner": "nix-community",
- "repo": "nixpkgs.lib",
- "rev": "147dee35aab2193b174e4c0868bd80ead5ce755c",
- "type": "github"
- },
- "original": {
- "owner": "nix-community",
- "repo": "nixpkgs.lib",
- "type": "github"
- }
- },
"root": {
"inputs": {
"flake-parts": "flake-parts",
- "mmsg": "mmsg",
"nixpkgs": "nixpkgs",
"scenefx": "scenefx"
}
diff --git a/flake.nix b/flake.nix
index a3bd8e6d..b7158bbd 100644
--- a/flake.nix
+++ b/flake.nix
@@ -2,10 +2,6 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
- mmsg = {
- url = "github:DreamMaoMao/mmsg";
- inputs.nixpkgs.follows = "nixpkgs";
- };
scenefx = {
url = "github:wlrfx/scenefx";
inputs.nixpkgs.follows = "nixpkgs";
@@ -33,10 +29,8 @@
...
}: let
inherit (pkgs) callPackage ;
- inherit (inputs.mmsg.packages.${pkgs.system}) mmsg;
mango = callPackage ./nix {
- inherit mmsg;
- inherit (inputs.scenefx.packages.${pkgs.system}) scenefx;
+ inherit (inputs.scenefx.packages.${pkgs.stdenv.hostPlatform.system}) scenefx;
};
shellOverride = old: {
nativeBuildInputs = old.nativeBuildInputs ++ [];
@@ -49,7 +43,6 @@
};
packages = {
inherit mango;
- inherit mmsg;
};
devShells.default = mango.overrideAttrs shellOverride;
formatter = pkgs.alejandra;
diff --git a/format.sh b/format.sh
new file mode 100644
index 00000000..1291ff8f
--- /dev/null
+++ b/format.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/bash
+
+clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/mmsg.c -i mmsg/arg.h -i mmsg/dynarr.h
diff --git a/mangowc.scm b/mangowc.scm
new file mode 100644
index 00000000..9c55d43e
--- /dev/null
+++ b/mangowc.scm
@@ -0,0 +1,64 @@
+(define-module (mangowc)
+ #:use-module (guix download)
+ #:use-module (guix git-download)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (guix utils)
+ #:use-module (gnu packages wm)
+ #:use-module (gnu packages freedesktop)
+ #:use-module (gnu packages xdisorg)
+ #:use-module (gnu packages pciutils)
+ #:use-module (gnu packages admin)
+ #:use-module (gnu packages pcre)
+ #:use-module (gnu packages xorg)
+ #:use-module (gnu packages build-tools)
+ #:use-module (gnu packages ninja)
+ #:use-module (gnu packages pkg-config)
+ #:use-module (guix build-system meson)
+ #:use-module (guix licenses))
+
+
+(define-public mangowc-git
+ (package
+ (name "mangowc")
+ (version "git")
+ (source (local-file "." "mangowc-checkout"
+ #:recursive? #t
+ #:select? (or (git-predicate (current-source-directory))
+ (const #t))))
+ (build-system meson-build-system)
+ (arguments
+ (list
+ #:configure-flags
+ #~(list (string-append "-Dsysconfdir=" #$output "/etc"))
+ #:phases
+ #~(modify-phases %standard-phases
+ (add-before 'configure 'patch-meson
+ (lambda _
+ (substitute* "meson.build"
+ (("'-DSYSCONFDIR=\\\"@0@\\\"'.format\\('/etc'\\)")
+ "'-DSYSCONFDIR=\"@0@\"'.format(sysconfdir)")
+ (("sysconfdir = sysconfdir.substring\\(prefix.length\\(\\)\\)")
+ "")))))))
+ (inputs (list wayland
+ libinput
+ libdrm
+ libxkbcommon
+ pixman
+ libdisplay-info
+ libliftoff
+ hwdata
+ seatd
+ pcre2
+ libxcb
+ xcb-util-wm
+ wlroots
+ scenefx))
+ (native-inputs (list pkg-config wayland-protocols))
+ (home-page "https://github.com/DreamMaoMao/mangowc")
+ (synopsis "Wayland compositor based on wlroots and scenefx")
+ (description "A Wayland compositor based on wlroots and scenefx,
+inspired by dwl but aiming to be more feature-rich.")
+ (license gpl3)))
+
+mangowc-git
diff --git a/meson.build b/meson.build
index 80d6cf2c..83b95135 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'],
- version : '0.8.2',
+ version : '0.10.10',
)
subdir('protocols')
@@ -32,27 +32,38 @@ cc = meson.get_compiler('c')
libm = cc.find_library('m')
xcb = dependency('xcb', required : get_option('xwayland'))
xlibs = dependency('xcb-icccm', required : get_option('xwayland'))
-wayland_server_dep = dependency('wayland-server')
+wayland_server_dep = dependency('wayland-server',version: '>=1.23.1')
wlroots_dep = dependency('wlroots-0.19',version: '>=0.19.0')
xkbcommon_dep = dependency('xkbcommon')
-libinput_dep = dependency('libinput')
+libinput_dep = dependency('libinput',version: '>=1.27.1')
libwayland_client_dep = dependency('wayland-client')
pcre2_dep = dependency('libpcre2-8')
libscenefx_dep = dependency('scenefx-0.4',version: '>=0.4.1')
-# 获取 Git Commit Hash 和最新的 tag
+# 获取版本信息
git = find_program('git', required : false)
+is_git_repo = false
+
+# 检查当前目录是否是 Git 仓库
if git.found()
- commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip()
- latest_tag = run_command(git, 'describe', '--tags', '--abbrev=0', check : false).stdout().strip()
-else
- commit_hash = 'unknown'
- latest_tag = meson.project_version() # 如果 Git 不可用,使用默认版本号
+ git_status = run_command(git, 'rev-parse', '--is-inside-work-tree', check : false)
+ if git_status.returncode() == 0 and git_status.stdout().strip() == 'true'
+ is_git_repo = true
+ endif
endif
-# 将 Commit Hash 和最新的 tag 添加到版本信息中
-version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
+if is_git_repo
+ # 如果是 Git 目录,获取 Commit Hash 和最新的 tag
+ commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip()
+ latest_tag = run_command(git, 'describe', '--tags', '--abbrev=0', check : false).stdout().strip()
+ version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
+else
+ # 如果不是 Git 目录,使用项目版本号和 "release" 字符串
+ commit_hash = 'release'
+ latest_tag = meson.project_version()
+ version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
+endif
# 定义编译参数
c_args = [
@@ -105,8 +116,36 @@ executable('mango',
link_args : link_args,
)
+# build mmsg
+dwl_ipc_protocol = 'protocols/dwl-ipc-unstable-v2.xml'
+
+wayland_scanner_client_header = generator(
+ wayland_scanner,
+ output: '@BASENAME@-protocol.h',
+ arguments: ['client-header', '@INPUT@', '@OUTPUT@']
+)
+
+wayland_scanner_private_code = generator(
+ wayland_scanner,
+ output: '@BASENAME@-protocol.c',
+ arguments: ['private-code', '@INPUT@', '@OUTPUT@']
+)
+
+# 在 mmsg 目标中使用生成器
+executable('mmsg',
+ 'mmsg/mmsg.c',
+ wayland_scanner_private_code.process(dwl_ipc_protocol),
+ wayland_scanner_client_header.process(dwl_ipc_protocol),
+ dependencies: [
+ libwayland_client_dep
+ ],
+ install: true,
+ c_args: [
+ '-g',
+ '-Wno-unused-function',
+ ],
+)
+
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
install_data('mango.desktop', install_dir : desktop_install_dir)
-
-# 安装 config.conf
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
diff --git a/mmsg/arg.h b/mmsg/arg.h
new file mode 100644
index 00000000..c3b0d7b9
--- /dev/null
+++ b/mmsg/arg.h
@@ -0,0 +1,65 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int32_t argc, char *argv[]) */
+#define ARGBEGIN \
+ for (argv0 = *argv, argv++, argc--; \
+ argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \
+ char argc_; \
+ char **argv_; \
+ int32_t brk_; \
+ if (argv[0][1] == '-' && argv[0][2] == '\0') { \
+ argv++; \
+ argc--; \
+ break; \
+ } \
+ for (brk_ = 0, argv[0]++, argv_ = argv; argv[0][0] && !brk_; \
+ argv[0]++) { \
+ if (argv_ != argv) \
+ break; \
+ argc_ = argv[0][0]; \
+ switch (argc_)
+
+/* Handles obsolete -NUM syntax */
+#define ARGNUM \
+ case '0': \
+ case '1': \
+ case '2': \
+ case '3': \
+ case '4': \
+ case '5': \
+ case '6': \
+ case '7': \
+ case '8': \
+ case '9'
+
+#define ARGEND \
+ } \
+ }
+
+#define ARGC() argc_
+
+#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX))
+
+#define EARGF(x) \
+ ((argv[0][1] == '\0' && argv[1] == NULL) \
+ ? ((x), abort(), (char *)0) \
+ : (brk_ = 1, \
+ (argv[0][1] != '\0') ? (&argv[0][1]) : (argc--, argv++, argv[0])))
+
+#define ARGF() \
+ ((argv[0][1] == '\0' && argv[1] == NULL) \
+ ? (char *)0 \
+ : (brk_ = 1, \
+ (argv[0][1] != '\0') ? (&argv[0][1]) : (argc--, argv++, argv[0])))
+
+#define LNGARG() &argv[0][0]
+
+#endif
diff --git a/mmsg/dynarr.h b/mmsg/dynarr.h
new file mode 100644
index 00000000..45cd356a
--- /dev/null
+++ b/mmsg/dynarr.h
@@ -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
diff --git a/mmsg/mmsg.c b/mmsg/mmsg.c
new file mode 100644
index 00000000..ba073aad
--- /dev/null
+++ b/mmsg/mmsg.c
@@ -0,0 +1,789 @@
+#include "arg.h"
+#include "dwl-ipc-unstable-v2-protocol.h"
+#include "dynarr.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define die(fmt, ...) \
+ do { \
+ fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+char *argv0;
+
+static enum {
+ NONE = 0,
+ SET = 1 << 0,
+ GET = 1 << 1,
+ WATCH = 1 << 2 | GET,
+} mode = NONE;
+
+static int32_t Oflag;
+static int32_t Tflag;
+static int32_t Lflag;
+static int32_t oflag;
+static int32_t tflag;
+static int32_t lflag;
+static int32_t cflag;
+static int32_t vflag;
+static int32_t mflag;
+static int32_t fflag;
+static int32_t qflag;
+static int32_t dflag;
+static int32_t xflag;
+static int32_t eflag;
+static int32_t kflag;
+static int32_t bflag;
+static int32_t Aflag;
+
+static uint32_t occ, seltags, total_clients, urg;
+
+static char *output_name;
+static int32_t tagcount;
+static char *tagset;
+static char *layout_name;
+static int32_t layoutcount, layout_idx;
+static char *client_tags;
+static char *dispatch_cmd;
+static char *dispatch_arg1;
+static char *dispatch_arg2;
+static char *dispatch_arg3;
+static char *dispatch_arg4;
+static char *dispatch_arg5;
+
+struct output {
+ char *output_name;
+ uint32_t name;
+};
+static DYNARR_DEF(struct output) outputs;
+
+static struct wl_display *display;
+static struct zdwl_ipc_manager_v2 *dwl_ipc_manager;
+
+// 为每个回调定义专用的空函数
+static void noop_geometry(void *data, struct wl_output *wl_output, int32_t x,
+ int32_t y, int32_t physical_width,
+ int32_t physical_height, int32_t subpixel,
+ const char *make, const char *model,
+ int32_t transform) {}
+
+static void noop_mode(void *data, struct wl_output *wl_output, uint32_t flags,
+ int32_t width, int32_t height, int32_t refresh) {}
+
+static void noop_done(void *data, struct wl_output *wl_output) {}
+
+static void noop_scale(void *data, struct wl_output *wl_output,
+ int32_t factor) {}
+
+static void noop_description(void *data, struct wl_output *wl_output,
+ const char *description) {}
+
+// 将 n 转换为 9 位二进制字符串,结果存入 buf(至少长度 10)
+void bin_str_9bits(char *buf, uint32_t n) {
+ for (int32_t i = 8; i >= 0; i--) {
+ *buf++ = ((n >> i) & 1) ? '1' : '0';
+ }
+ *buf = '\0'; // 字符串结尾
+}
+
+static void dwl_ipc_tags(void *data,
+ struct zdwl_ipc_manager_v2 *dwl_ipc_manager,
+ uint32_t count) {
+ tagcount = count;
+ if (Tflag && mode & GET)
+ printf("%d\n", tagcount);
+}
+
+static void dwl_ipc_layout(void *data,
+ struct zdwl_ipc_manager_v2 *dwl_ipc_manager,
+ const char *name) {
+ if (lflag && mode & SET && strcmp(layout_name, name) == 0)
+ layout_idx = layoutcount;
+ if (Lflag && mode & GET)
+ printf("%s\n", name);
+ layoutcount++;
+}
+
+static const struct zdwl_ipc_manager_v2_listener dwl_ipc_listener = {
+ .tags = dwl_ipc_tags, .layout = dwl_ipc_layout};
+
+static void
+dwl_ipc_output_toggle_visibility(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output) {
+ if (!vflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("toggle\n");
+}
+
+static void dwl_ipc_output_active(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t active) {
+ if (!oflag) {
+ if (mode & SET && !output_name && active)
+ output_name = strdup(data);
+ return;
+ }
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("selmon %u\n", active ? 1 : 0);
+}
+
+static void dwl_ipc_output_tag(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t tag, uint32_t state, uint32_t clients,
+ uint32_t focused) {
+ if (!tflag)
+ return;
+ if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE)
+ seltags |= 1 << tag;
+ if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT)
+ urg |= 1 << tag;
+ if (clients > 0)
+ occ |= 1 << tag;
+
+ // 累计所有 tag 的 clients 总数
+ total_clients += clients;
+
+ if (!(mode & GET))
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("tag %u %u %u %u\n", tag + 1, state, clients, focused);
+}
+
+static void dwl_ipc_output_layout(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t layout) {}
+
+static void dwl_ipc_output_layout_symbol(
+ void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *layout) {
+ if (!(lflag && mode & GET))
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("layout %s\n", layout);
+}
+
+static void dwl_ipc_output_title(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const char *title) {
+ if (!(cflag && mode & GET))
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("title %s\n", title);
+}
+
+static void dwl_ipc_output_appid(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const char *appid) {
+ if (!(cflag && mode & GET))
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("appid %s\n", appid);
+}
+
+static void dwl_ipc_output_x(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ int32_t x) {
+ if (!xflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("x %d\n", x);
+}
+
+static void dwl_ipc_output_y(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ int32_t y) {
+ if (!xflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("y %d\n", y);
+}
+
+static void dwl_ipc_output_width(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ int32_t width) {
+ if (!xflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("width %d\n", width);
+}
+
+static void dwl_ipc_output_height(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ int32_t height) {
+ if (!xflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("height %d\n", height);
+}
+
+static void dwl_ipc_output_last_layer(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const char *last_layer) {
+ if (!eflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("last_layer %s\n", last_layer);
+}
+
+static void dwl_ipc_output_kb_layout(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const char *kb_layout) {
+ if (!kflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("kb_layout %s\n", kb_layout);
+}
+
+static void
+dwl_ipc_output_scalefactor(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const uint32_t scalefactor) {
+ if (!Aflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("scale_factor %f\n", scalefactor / 100.0f);
+}
+
+static void dwl_ipc_output_keymode(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const char *keymode) {
+ if (!bflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("keymode %s\n", keymode);
+}
+
+static void dwl_ipc_output_fullscreen(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t is_fullscreen) {
+ if (!mflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("fullscreen %u\n", is_fullscreen);
+}
+
+static void dwl_ipc_output_floating(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t is_floating) {
+ if (!fflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("floating %u\n", is_floating);
+}
+
+static void dwl_ipc_output_frame(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output) {
+ if (mode & SET) {
+ if (data && (!output_name || strcmp(output_name, (char *)data)))
+ return;
+ if (qflag) {
+ zdwl_ipc_output_v2_quit(dwl_ipc_output);
+ }
+ if (lflag) {
+ zdwl_ipc_output_v2_set_layout(dwl_ipc_output, layout_idx);
+ }
+ if (tflag) {
+ uint32_t mask = seltags;
+ char *t = tagset;
+ int32_t i = 0;
+
+ for (; *t && *t >= '0' && *t <= '9'; t++)
+ i = *t - '0' + i * 10;
+
+ if (!*t)
+ mask = 1 << (i - 1);
+
+ for (; *t; t++, i++) {
+ switch (*t) {
+ case '-':
+ mask &= ~(1 << (i - 1));
+ break;
+ case '+':
+ mask |= 1 << (i - 1);
+ break;
+ case '^':
+ mask ^= 1 << (i - 1);
+ break;
+ }
+ }
+
+ if ((i - 1) > tagcount)
+ die("bad tagset %s", tagset);
+
+ zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, 0);
+ }
+ if (cflag) {
+ uint32_t and = ~0, xor = 0;
+ char *t = client_tags;
+ int32_t i = 0;
+
+ for (; *t && *t >= '0' && *t <= '9'; t++)
+ i = *t - '0' + i * 10;
+
+ if (!*t)
+ t = "+";
+
+ for (; *t; t++, i++) {
+ switch (*t) {
+ case '-':
+ and &= ~(1 << (i - 1));
+ break;
+ case '+':
+ and &= ~(1 << (i - 1));
+ xor |= 1 << (i - 1);
+ break;
+ case '^':
+ xor |= 1 << (i - 1);
+ break;
+ }
+ }
+ if ((i - 1) > tagcount)
+ die("bad client tagset %s", client_tags);
+
+ zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor);
+ }
+ if (dflag) {
+ zdwl_ipc_output_v2_dispatch(
+ dwl_ipc_output, dispatch_cmd, dispatch_arg1, dispatch_arg2,
+ dispatch_arg3, dispatch_arg4, dispatch_arg5);
+ }
+ wl_display_flush(display);
+ usleep(1000);
+ exit(0);
+ } else {
+ if (tflag) {
+ char *output_name = data;
+
+ printf("%s clients %u\n", output_name, total_clients);
+
+ char occ_str[10], seltags_str[10], urg_str[10];
+
+ bin_str_9bits(occ_str, occ);
+ bin_str_9bits(seltags_str, seltags);
+ bin_str_9bits(urg_str, urg);
+ printf("%s tags %u %u %u\n", output_name, occ, seltags, urg);
+ printf("%s tags %s %s %s\n", output_name, occ_str, seltags_str,
+ urg_str);
+ occ = seltags = total_clients = urg = 0;
+ }
+ }
+ fflush(stdout);
+}
+
+static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = {
+ .toggle_visibility = dwl_ipc_output_toggle_visibility,
+ .active = dwl_ipc_output_active,
+ .tag = dwl_ipc_output_tag,
+ .layout = dwl_ipc_output_layout,
+ .title = dwl_ipc_output_title,
+ .appid = dwl_ipc_output_appid,
+ .layout_symbol = dwl_ipc_output_layout_symbol,
+ .fullscreen = dwl_ipc_output_fullscreen,
+ .floating = dwl_ipc_output_floating,
+ .x = dwl_ipc_output_x,
+ .y = dwl_ipc_output_y,
+ .width = dwl_ipc_output_width,
+ .height = dwl_ipc_output_height,
+ .last_layer = dwl_ipc_output_last_layer,
+ .kb_layout = dwl_ipc_output_kb_layout,
+ .keymode = dwl_ipc_output_keymode,
+ .scalefactor = dwl_ipc_output_scalefactor,
+ .frame = dwl_ipc_output_frame,
+};
+
+static void wl_output_name(void *data, struct wl_output *output,
+ const char *name) {
+ if (outputs.arr) {
+ struct output *o = (struct output *)data;
+ o->output_name = strdup(name);
+ printf("+ ");
+ }
+ if (Oflag)
+ printf("%s\n", name);
+ if (output_name && strcmp(output_name, name) != 0) {
+ wl_output_release(output);
+ return;
+ }
+ struct zdwl_ipc_output_v2 *dwl_ipc_output =
+ zdwl_ipc_manager_v2_get_output(dwl_ipc_manager, output);
+ zdwl_ipc_output_v2_add_listener(dwl_ipc_output, &dwl_ipc_output_listener,
+ output_name ? NULL : strdup(name));
+}
+
+static const struct wl_output_listener output_listener = {
+ .geometry = noop_geometry,
+ .mode = noop_mode,
+ .done = noop_done,
+ .scale = noop_scale,
+ .name = wl_output_name,
+ .description = noop_description,
+};
+
+static void global_add(void *data, struct wl_registry *wl_registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ if (strcmp(interface, wl_output_interface.name) == 0) {
+ struct wl_output *o =
+ wl_registry_bind(wl_registry, name, &wl_output_interface,
+ WL_OUTPUT_NAME_SINCE_VERSION);
+ if (!outputs.arr) {
+ wl_output_add_listener(o, &output_listener, NULL);
+ } else {
+ DYNARR_PUSH(&outputs, (struct output){.name = name});
+ wl_output_add_listener(o, &output_listener,
+ &outputs.arr[outputs.len - 1]);
+ }
+ } else if (strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) {
+ dwl_ipc_manager = wl_registry_bind(wl_registry, name,
+ &zdwl_ipc_manager_v2_interface, 2);
+ zdwl_ipc_manager_v2_add_listener(dwl_ipc_manager, &dwl_ipc_listener,
+ NULL);
+ }
+}
+
+static void global_remove(void *data, struct wl_registry *wl_registry,
+ uint32_t name) {
+ if (!outputs.arr)
+ return;
+ struct output *o = outputs.arr;
+ for (size_t i = 0; i < outputs.len; i++, o++) {
+ if (o->name == name) {
+ printf("- %s\n", o->output_name);
+ free(o->output_name);
+ *o = DYNARR_POP(&outputs);
+ }
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = global_add,
+ .global_remove = global_remove,
+};
+
+static void usage(void) {
+ fprintf(stderr,
+ "usage:"
+ "\t%s [-OTLq]\n"
+ "\t%s [-o