diff --git a/README.md b/README.md index 20ac1c78..087bf13b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# MangoWC - -mango-transparency-256 - +# Mango Wayland Compositor +
+ MangoWC Logo +
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 ] -s [-t ] [-l ] [-c ] [-d " + ",,,,,]\n" + "\t%s [-o ] (-g | -w) [-OotlcvmfxekbA]\n", + argv0, argv0, argv0); + exit(2); +} + +int32_t main(int32_t argc, char *argv[]) { + ARGBEGIN { + case 'q': + qflag = 1; + if (!(mode & GET)) { + mode = SET; + } + break; + case 's': + if (mode != NONE) + usage(); + mode = SET; + break; + case 'g': + if (mode != NONE) + usage(); + mode = GET; + break; + case 'w': + if (mode != NONE) + usage(); + mode = WATCH; + break; + case 'o': + if (mode == SET) + output_name = EARGF(usage()); + else + output_name = ARGF(); + if (!output_name) + oflag = 1; + break; + case 't': + tflag = 1; + if (!(mode & GET)) { + mode = SET; + tagset = EARGF(usage()); + } + break; + case 'l': + lflag = 1; + if (!(mode & GET)) { + mode = SET; + layout_name = EARGF(usage()); + } + break; + case 'c': + cflag = 1; + if (!(mode & GET)) { + mode = SET; + client_tags = EARGF(usage()); + } + break; + case 'd': + dflag = 1; + if (!(mode & GET)) { + mode = SET; + char *arg = EARGF(usage()); + + // Trim leading and trailing whitespace from entire argument first + while (isspace(*arg)) + arg++; + char *end = arg + strlen(arg) - 1; + while (end > arg && isspace(*end)) + end--; + *(end + 1) = '\0'; + + dispatch_cmd = arg; + char *comma1 = strchr(arg, ','); + if (comma1) { + *comma1 = '\0'; + + // Trim trailing whitespace from command + end = dispatch_cmd + strlen(dispatch_cmd) - 1; + while (end > dispatch_cmd && isspace(*end)) + end--; + *(end + 1) = '\0'; + + dispatch_arg1 = comma1 + 1; + // Trim leading whitespace from arg1 + while (isspace(*dispatch_arg1)) + dispatch_arg1++; + + // Trim trailing whitespace from arg1 before looking for next + // comma + end = dispatch_arg1 + strlen(dispatch_arg1) - 1; + while (end > dispatch_arg1 && isspace(*end)) + end--; + *(end + 1) = '\0'; + + char *comma2 = strchr(dispatch_arg1, ','); + if (comma2) { + *comma2 = '\0'; + dispatch_arg2 = comma2 + 1; + // Trim leading whitespace from arg2 + while (isspace(*dispatch_arg2)) + dispatch_arg2++; + + // Trim trailing whitespace from arg2 before looking for + // next comma + end = dispatch_arg2 + strlen(dispatch_arg2) - 1; + while (end > dispatch_arg2 && isspace(*end)) + end--; + *(end + 1) = '\0'; + + char *comma3 = strchr(dispatch_arg2, ','); + if (comma3) { + *comma3 = '\0'; + dispatch_arg3 = comma3 + 1; + // Trim leading whitespace from arg3 + while (isspace(*dispatch_arg3)) + dispatch_arg3++; + + // Trim trailing whitespace from arg3 before looking for + // next comma + end = dispatch_arg3 + strlen(dispatch_arg3) - 1; + while (end > dispatch_arg3 && isspace(*end)) + end--; + *(end + 1) = '\0'; + + char *comma4 = strchr(dispatch_arg3, ','); + if (comma4) { + *comma4 = '\0'; + dispatch_arg4 = comma4 + 1; + // Trim leading whitespace from arg4 + while (isspace(*dispatch_arg4)) + dispatch_arg4++; + + // Trim trailing whitespace from arg4 before looking + // for next comma + end = dispatch_arg4 + strlen(dispatch_arg4) - 1; + while (end > dispatch_arg4 && isspace(*end)) + end--; + *(end + 1) = '\0'; + + char *comma5 = strchr(dispatch_arg4, ','); + if (comma5) { + *comma5 = '\0'; + dispatch_arg5 = comma5 + 1; + // Trim leading whitespace from arg5 + while (isspace(*dispatch_arg5)) + dispatch_arg5++; + + // Trim trailing whitespace from arg5 + end = dispatch_arg5 + strlen(dispatch_arg5) - 1; + while (end > dispatch_arg5 && isspace(*end)) + end--; + *(end + 1) = '\0'; + } else { + dispatch_arg5 = ""; + } + } else { + dispatch_arg4 = ""; + dispatch_arg5 = ""; + } + } else { + dispatch_arg3 = ""; + dispatch_arg4 = ""; + dispatch_arg5 = ""; + } + } else { + dispatch_arg2 = ""; + dispatch_arg3 = ""; + dispatch_arg4 = ""; + dispatch_arg5 = ""; + } + } else { + dispatch_arg1 = ""; + dispatch_arg2 = ""; + dispatch_arg3 = ""; + dispatch_arg4 = ""; + dispatch_arg5 = ""; + } + } + break; + case 'O': + Oflag = 1; + if (mode && !(mode & GET)) + usage(); + if (mode & WATCH) + DYNARR_INIT(&outputs); + else + mode = GET; + break; + case 'T': + Tflag = 1; + if (mode && mode != GET) + usage(); + mode = GET; + break; + case 'L': + Lflag = 1; + if (mode && mode != GET) + usage(); + mode = GET; + break; + case 'v': + vflag = 1; + if (mode == SET) + usage(); + mode |= GET; + break; + case 'm': + mflag = 1; + if (mode == SET) + usage(); + mode |= GET; + break; + case 'f': + fflag = 1; + if (mode == SET) + usage(); + mode |= GET; + break; + case 'x': + xflag = 1; + if (mode == SET) + usage(); + mode |= GET; + break; + case 'e': + eflag = 1; + if (mode == SET) + usage(); + mode |= GET; + break; + case 'k': + kflag = 1; + if (mode == SET) + usage(); + mode |= GET; + break; + case 'b': + bflag = 1; + if (mode == SET) + usage(); + mode |= GET; + break; + case 'A': + Aflag = 1; + if (mode == SET) + usage(); + mode |= GET; + break; + default: + fprintf(stderr, "bad option %c\n", ARGC()); + usage(); + } + ARGEND + if (mode == NONE) + usage(); + if (mode & GET && !output_name && + !(oflag || tflag || lflag || Oflag || Tflag || Lflag || cflag || + vflag || mflag || fflag || xflag || eflag || kflag || bflag || + Aflag || dflag)) + oflag = tflag = lflag = cflag = vflag = mflag = fflag = xflag = eflag = + kflag = bflag = Aflag = 1; + + display = wl_display_connect(NULL); + if (!display) + die("bad display"); + + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®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; +} diff --git a/nix/default.nix b/nix/default.nix index bcbca201..6085565e 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,6 +1,5 @@ { lib, - fetchFromGitHub, libX11, libinput, libxcb, @@ -14,73 +13,64 @@ wayland-scanner, xcbutilwm, xwayland, - enableXWayland ? true, meson, ninja, - mmsg, scenefx, wlroots_0_19, libGL, -}: let + enableXWayland ? true, + debug ? false, +}: +stdenv.mkDerivation { pname = "mango"; - # Use patched wlroots from github.com/DreamMaoMao/wlroots - wlroots-git = wlroots_0_19.overrideAttrs ( - final: prev: { - src = fetchFromGitHub { - owner = "DreamMaoMao"; - repo = "wlroots"; - rev = "afbb5b7c2b14152730b57aa11119b1b16a299d5b"; - sha256 = "sha256-pVU+CuiqvduMTpsnDHX/+EWY2qxHX2lXKiVzdGtcnYY="; - }; - } - ); -in - stdenv.mkDerivation { - inherit pname; - version = "nightly"; + version = "nightly"; - src = builtins.path { - path = ../.; - name = "source"; - }; + src = builtins.path { + path = ../.; + name = "source"; + }; - nativeBuildInputs = [ - meson - ninja - pkg-config - wayland-scanner + mesonFlags = [ + (lib.mesonEnable "xwayland" enableXWayland) + (lib.mesonBool "asan" debug) + ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + wayland-scanner + ]; + + buildInputs = + [ + libinput + libxcb + libxkbcommon + pcre2 + pixman + wayland + wayland-protocols + wlroots_0_19 + scenefx + libGL + ] + ++ lib.optionals enableXWayland [ + libX11 + xcbutilwm + xwayland ]; - buildInputs = - [ - libinput - libxcb - libxkbcommon - pcre2 - pixman - wayland - wayland-protocols - wlroots-git - scenefx - libGL - ] - ++ lib.optionals enableXWayland [ - libX11 - xcbutilwm - xwayland - ]; + passthru = { + providedSessions = ["mango"]; + }; - passthru = { - providedSessions = ["mango"]; - inherit mmsg; - }; - - meta = { - mainProgram = "mango"; - description = "A streamlined but feature-rich Wayland compositor"; - homepage = "https://github.com/DreamMaoMao/mango"; - license = lib.licenses.gpl3Plus; - maintainers = []; - platforms = lib.platforms.unix; - }; - } + meta = { + mainProgram = "mango"; + description = "A streamlined but feature-rich Wayland compositor"; + homepage = "https://github.com/DreamMaoMao/mango"; + license = lib.licenses.gpl3Plus; + maintainers = []; + platforms = lib.platforms.unix; + }; +} diff --git a/nix/hm-modules.nix b/nix/hm-modules.nix index 72628b1d..85d57908 100644 --- a/nix/hm-modules.nix +++ b/nix/hm-modules.nix @@ -21,7 +21,7 @@ in { }; package = lib.mkOption { type = lib.types.package; - default = self.packages.${pkgs.system}.mango; + default = self.packages.${pkgs.stdenv.hostPlatform.system}.mango; description = "The mango package to use"; }; systemd = { @@ -96,27 +96,16 @@ in { }; config = lib.mkIf cfg.enable { - home.packages = [ cfg.package ]; - home.activation = - lib.optionalAttrs (cfg.autostart_sh != "") { - createMangoScript = lib.hm.dag.entryAfter ["clearMangoConfig"] '' - cat ${autostart_sh} > $HOME/.config/mango/autostart.sh - chmod +x $HOME/.config/mango/autostart.sh - ''; - } - // lib.optionalAttrs (cfg.settings != "") { - createMangoConfig = lib.hm.dag.entryAfter ["clearMangoConfig"] '' - cat > $HOME/.config/mango/config.conf < + + + current keybind mode. + + + + + + + scale factor of monitor. + + + + diff --git a/protocols/meson.build b/protocols/meson.build index 1069157c..cafab64a 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -19,6 +19,7 @@ wayland_xmls = [ wl_protocol_dir + '/staging/ext-image-capture-source/ext-image-capture-source-v1.xml', wl_protocol_dir + '/staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', wl_protocol_dir + '/staging/ext-workspace/ext-workspace-v1.xml', + wl_protocol_dir + '/staging/tearing-control/tearing-control-v1.xml', 'wlr-foreign-toplevel-management-unstable-v1.xml', 'dwl-ipc-unstable-v2.xml', 'wlr-layer-shell-unstable-v1.xml', diff --git a/src/animation/client.h b/src/animation/client.h index 229ff22b..849bc10e 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -1,10 +1,10 @@ -void client_actual_size(Client *c, unsigned int *width, unsigned int *height) { - *width = c->animation.current.width - c->bw; +void client_actual_size(Client *c, int32_t *width, int32_t *height) { + *width = c->animation.current.width - 2 * (int32_t)c->bw; - *height = c->animation.current.height - c->bw; + *height = c->animation.current.height - 2 * (int32_t)c->bw; } -void set_rect_size(struct wlr_scene_rect *rect, int width, int height) { +void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) { wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height)); } @@ -28,35 +28,54 @@ enum corner_location set_client_corner_location(Client *c) { return current_corner_location; } -int is_special_animaiton_rule(Client *c) { - int visible_client_number = 0; - Client *count_c; - wl_list_for_each(count_c, &clients, link) { - if (count_c && VISIBLEON(count_c, selmon) && !count_c->isminied && - !count_c->iskilling && !count_c->isfloating) { - visible_client_number++; - } - } +bool is_horizontal_stack_layout(Monitor *m) { - if (is_scroller_layout(selmon) && !c->isfloating) { + if (m->pertag->curtag && + (m->pertag->ltidxs[m->pertag->curtag]->id == TILE || + m->pertag->ltidxs[m->pertag->curtag]->id == DECK)) + return true; + + return false; +} + +bool is_horizontal_right_stack_layout(Monitor *m) { + + if (m->pertag->curtag && + (m->pertag->ltidxs[m->pertag->curtag]->id == RIGHT_TILE)) + return true; + + return false; +} + +int32_t is_special_animaiton_rule(Client *c) { + + if (is_scroller_layout(c->mon) && !c->isfloating) { return DOWN; - } else if (visible_client_number < 2 && !c->isfloating) { + } else if (c->mon->visible_tiling_clients == 1 && !c->isfloating) { return DOWN; - } else if (visible_client_number == 2 && !c->isfloating && !new_is_master) { + } else if (c->mon->visible_tiling_clients == 2 && !c->isfloating && + !new_is_master && is_horizontal_stack_layout(c->mon)) { return RIGHT; - } else if (!c->isfloating && new_is_master) { + } else if (!c->isfloating && new_is_master && + is_horizontal_stack_layout(c->mon)) { return LEFT; + } else if (c->mon->visible_tiling_clients == 2 && !c->isfloating && + !new_is_master && is_horizontal_right_stack_layout(c->mon)) { + return LEFT; + } else if (!c->isfloating && new_is_master && + is_horizontal_right_stack_layout(c->mon)) { + return RIGHT; } else { return UNDIR; } } void set_client_open_animaiton(Client *c, struct wlr_box geo) { - int slide_direction; - int horizontal, horizontal_value; - int vertical, vertical_value; - int special_direction; - int center_x, center_y; + int32_t slide_direction; + int32_t horizontal, horizontal_value; + int32_t vertical, vertical_value; + int32_t special_direction; + int32_t center_x, center_y; if ((!c->animation_type_open && strcmp(animation_type_open, "fade") == 0) || (c->animation_type_open && @@ -128,15 +147,15 @@ void set_client_open_animaiton(Client *c, struct wlr_box geo) { } } -void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, - int sy, void *data) { +void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, + int32_t sy, void *data) { BufferData *buffer_data = (BufferData *)data; wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, buffer_data->height); } -void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy, - void *data) { +void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, + int32_t sy, void *data) { BufferData *buffer_data = (BufferData *)data; if (buffer_data->should_scale && buffer_data->height_scale < 1 && @@ -164,8 +183,8 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy, if (buffer_data->should_scale) { - unsigned int surface_width = surface->current.width; - unsigned int surface_height = surface->current.height; + int32_t surface_width = surface->current.width; + int32_t surface_height = surface->current.height; surface_width = buffer_data->width_scale < 1 ? surface_width @@ -206,12 +225,6 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy, wlr_scene_buffer_set_corner_radius(buffer, border_radius, buffer_data->corner_location); - - float target_opacity = buffer_data->percent + fadein_begin_opacity; - if (target_opacity > buffer_data->opacity) { - target_opacity = buffer_data->opacity; - } - wlr_scene_buffer_set_opacity(buffer, target_opacity); } void buffer_set_effect(Client *c, BufferData data) { @@ -227,8 +240,9 @@ void buffer_set_effect(Client *c, BufferData data) { if (c == grabc) data.should_scale = false; - if (c->isfullscreen || (no_radius_when_single && c->mon && - c->mon->visible_tiling_clients == 1)) { + if (c->isnoradius || c->isfullscreen || + (no_radius_when_single && c->mon && + c->mon->visible_tiling_clients == 1)) { data.corner_location = CORNER_LOCATION_NONE; } @@ -238,12 +252,16 @@ void buffer_set_effect(Client *c, BufferData data) { void client_draw_shadow(Client *c) { - if (c->iskilling || !client_surface(c)->mapped) + if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow) return; if (!shadows || (!c->isfloating && shadow_only_floating)) { - wlr_scene_shadow_set_size(c->shadow, 0, 0); + if (c->shadow->node.enabled) + wlr_scene_node_set_enabled(&c->shadow->node, false); return; + } else { + if (c->scene_surface->node.enabled && !c->shadow->node.enabled) + wlr_scene_node_set_enabled(&c->shadow->node, true); } bool hit_no_border = check_hit_no_border(c); @@ -253,24 +271,24 @@ void client_draw_shadow(Client *c) { ? CORNER_LOCATION_NONE : CORNER_LOCATION_ALL; - unsigned int bwoffset = c->bw != 0 && hit_no_border ? c->bw : 0; + int32_t bwoffset = c->bw != 0 && hit_no_border ? (int32_t)c->bw : 0; - uint32_t width, height; + int32_t width, height; client_actual_size(c, &width, &height); - uint32_t delta = shadows_size + c->bw - bwoffset; + int32_t delta = shadows_size + (int32_t)c->bw - bwoffset; /* we calculate where to clip the shadow */ struct wlr_box client_box = { .x = bwoffset, .y = bwoffset, - .width = width - 2 * bwoffset, - .height = height - 2 * bwoffset, + .width = width + (int32_t)c->bw - bwoffset, + .height = height + (int32_t)c->bw - bwoffset, }; struct wlr_box shadow_box = { - .x = shadows_position_x, - .y = shadows_position_y, + .x = shadows_position_x + bwoffset, + .y = shadows_position_y + bwoffset, .width = width + 2 * delta, .height = height + 2 * delta, }; @@ -279,8 +297,8 @@ void client_draw_shadow(Client *c) { wlr_box_intersection(&intersection_box, &client_box, &shadow_box); /* clipped region takes shadow relative coords, so we translate everything * by its position */ - intersection_box.x -= shadows_position_x; - intersection_box.y -= shadows_position_y; + intersection_box.x -= shadows_position_x + bwoffset; + intersection_box.y -= shadows_position_y + bwoffset; struct clipped_region clipped_region = { .area = intersection_box, @@ -295,7 +313,7 @@ void client_draw_shadow(Client *c) { .height = shadow_box.height, }; - int right_offset, bottom_offset, left_offset, top_offset; + int32_t right_offset, bottom_offset, left_offset, top_offset; if (c == grabc) { right_offset = 0; @@ -337,11 +355,13 @@ void apply_border(Client *c) { return; bool hit_no_border = check_hit_no_border(c); - enum corner_location current_corner_location = - c->isfullscreen || (no_radius_when_single && c->mon && - c->mon->visible_tiling_clients == 1) - ? CORNER_LOCATION_NONE - : CORNER_LOCATION_ALL; + enum corner_location current_corner_location; + if (c->isfullscreen || (no_radius_when_single && c->mon && + c->mon->visible_tiling_clients == 1)) { + current_corner_location = CORNER_LOCATION_NONE; + } else { + current_corner_location = set_client_corner_location(c); + } // Handle no-border cases if (hit_no_border && smartgaps) { @@ -359,9 +379,9 @@ void apply_border(Client *c) { struct wlr_box clip_box = c->animation.current; // 一但在GEZERO如果使用无符号,那么其他数据也会转换为无符号导致没有负数出错 - int bw = (int)c->bw; + int32_t bw = (int32_t)c->bw; - int right_offset, bottom_offset, left_offset, top_offset; + int32_t right_offset, bottom_offset, left_offset, top_offset; if (c == grabc) { right_offset = 0; @@ -380,25 +400,27 @@ void apply_border(Client *c) { top_offset = GEZERO(c->mon->m.y - c->animation.current.y); } - int inner_surface_width = GEZERO(clip_box.width - 2 * bw); - int inner_surface_height = GEZERO(clip_box.height - 2 * bw); + int32_t inner_surface_width = GEZERO(clip_box.width - 2 * bw); + int32_t inner_surface_height = GEZERO(clip_box.height - 2 * bw); - int inner_surface_x = GEZERO(bw - left_offset); - int inner_surface_y = GEZERO(bw - top_offset); + int32_t inner_surface_x = GEZERO(bw - left_offset); + int32_t inner_surface_y = GEZERO(bw - top_offset); - int rect_x = left_offset; - int rect_y = top_offset; + int32_t rect_x = left_offset; + int32_t rect_y = top_offset; - int rect_width = + int32_t rect_width = GEZERO(c->animation.current.width - left_offset - right_offset); - int rect_height = + int32_t rect_height = GEZERO(c->animation.current.height - top_offset - bottom_offset); if (left_offset > c->bw) - inner_surface_width = inner_surface_width - left_offset + c->bw; + inner_surface_width = + inner_surface_width - left_offset + (int32_t)c->bw; if (top_offset > c->bw) - inner_surface_height = inner_surface_height - top_offset + c->bw; + inner_surface_height = + inner_surface_height - top_offset + (int32_t)c->bw; if (right_offset > 0) { inner_surface_width = @@ -426,31 +448,31 @@ void apply_border(Client *c) { } struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { - int offsetx = 0, offsety = 0, offsetw = 0, offseth = 0; + int32_t offsetx = 0, offsety = 0, offsetw = 0, offseth = 0; struct ivec2 offset = {0, 0, 0, 0}; - if (!ISTILED(c) && !c->animation.tagining && !c->animation.tagouted && + if (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouted && !c->animation.tagouting) return offset; - int bottom_out_offset = + int32_t bottom_out_offset = GEZERO(c->animation.current.y + c->animation.current.height - c->mon->m.y - c->mon->m.height); - int right_out_offset = + int32_t right_out_offset = GEZERO(c->animation.current.x + c->animation.current.width - c->mon->m.x - c->mon->m.width); - int left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x); - int top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y); + int32_t left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x); + int32_t top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y); // 必须转换为int,否计算会没有负数导致判断错误 - int bw = (int)c->bw; + int32_t bw = (int32_t)c->bw; /* 计算窗口表面超出屏幕四个方向的偏差,避免窗口超出屏幕 需要主要border超出屏幕的时候不计算如偏差之内而是 要等窗口表面超出才开始计算偏差 */ - if (ISTILED(c) || c->animation.tagining || c->animation.tagouted || + if (ISSCROLLTILED(c) || c->animation.tagining || c->animation.tagouted || c->animation.tagouting) { if (left_out_offset > 0) { offsetx = GEZERO(left_out_offset - bw); @@ -478,7 +500,7 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { offset.height = offseth; if ((clip_box->width + bw <= 0 || clip_box->height + bw <= 0) && - (ISTILED(c) || c->animation.tagouting || c->animation.tagining)) { + (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) { c->is_clip_to_hide = true; wlr_scene_node_set_enabled(&c->scene->node, false); } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon)) { @@ -498,13 +520,10 @@ void client_apply_clip(Client *c, float factor) { bool should_render_client_surface = false; struct ivec2 offset; BufferData buffer_data; - float opacity, percent; enum corner_location current_corner_location = set_client_corner_location(c); - int bw = (int)c->bw; - if (!animations) { c->animation.running = false; c->need_output_flush = false; @@ -518,31 +537,19 @@ void client_apply_clip(Client *c, float factor) { apply_border(c); client_draw_shadow(c); - opacity = c->isfullscreen ? 1 - : c == selmon->sel ? c->focused_opacity - : c->unfocused_opacity; - if (clip_box.width <= 0 || clip_box.height <= 0) { return; } wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); buffer_set_effect(c, (BufferData){1.0f, 1.0f, clip_box.width, - clip_box.height, opacity, opacity, + clip_box.height, current_corner_location, true}); return; } - percent = - c->animation.action == OPEN && animation_fade_in && !c->nofadein - ? (double)c->animation.passed_frames / c->animation.total_frames - : 1.0; - opacity = c->isfullscreen ? 1 - : c == selmon->sel ? c->focused_opacity - : c->unfocused_opacity; - // 获取窗口动画实时位置矩形 - unsigned int width, height; + int32_t width, height; client_actual_size(c, &width, &height); // 计算出除了边框的窗口实际剪切大小 @@ -551,8 +558,8 @@ void client_apply_clip(Client *c, float factor) { clip_box = (struct wlr_box){ .x = geometry.x, .y = geometry.y, - .width = width - bw, - .height = height - bw, + .width = width, + .height = height, }; if (client_is_x11(c)) { @@ -585,8 +592,8 @@ void client_apply_clip(Client *c, float factor) { wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); // 获取剪切后的表面的实际大小用于计算缩放 - int acutal_surface_width = geometry.width - offset.x - offset.width; - int acutal_surface_height = geometry.height - offset.y - offset.height; + int32_t acutal_surface_width = geometry.width - offset.x - offset.width; + int32_t acutal_surface_height = geometry.height - offset.y - offset.height; if (acutal_surface_width <= 0 || acutal_surface_height <= 0) return; @@ -595,8 +602,6 @@ void client_apply_clip(Client *c, float factor) { buffer_data.width = clip_box.width; buffer_data.height = clip_box.height; buffer_data.corner_location = current_corner_location; - buffer_data.percent = percent; - buffer_data.opacity = opacity; if (factor == 1.0) { buffer_data.width_scale = 1.0; @@ -617,21 +622,27 @@ void fadeout_client_animation_next_tick(Client *c) { BufferData buffer_data; - double animation_passed = - (double)c->animation.passed_frames / c->animation.total_frames; - int type = c->animation.action = c->animation.action; - double factor = find_animation_curve_at(animation_passed, type); - unsigned int width = - c->animation.initial.width + - (c->current.width - c->animation.initial.width) * factor; - unsigned int height = - c->animation.initial.height + - (c->current.height - c->animation.initial.height) * factor; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); - unsigned int x = c->animation.initial.x + - (c->current.x - c->animation.initial.x) * factor; - unsigned int y = c->animation.initial.y + - (c->current.y - c->animation.initial.y) * factor; + int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; + double animation_passed = + c->animation.duration + ? (double)passed_time / (double)c->animation.duration + : 1.0; + + int32_t type = c->animation.action = c->animation.action; + double factor = find_animation_curve_at(animation_passed, type); + + int32_t width = c->animation.initial.width + + (c->current.width - c->animation.initial.width) * factor; + int32_t height = c->animation.initial.height + + (c->current.height - c->animation.initial.height) * factor; + + int32_t x = c->animation.initial.x + + (c->current.x - c->animation.initial.x) * factor; + int32_t y = c->animation.initial.y + + (c->current.y - c->animation.initial.y) * factor; wlr_scene_node_set_position(&c->scene->node, x, y); @@ -642,7 +653,13 @@ void fadeout_client_animation_next_tick(Client *c) { .height = height, }; - double opacity = MAX(fadeout_begin_opacity - animation_passed, 0); + double opacity_eased_progress = + find_animation_curve_at(animation_passed, OPAFADEOUT); + + double percent = fadeout_begin_opacity - + (opacity_eased_progress * fadeout_begin_opacity); + + double opacity = MAX(percent, 0); if (animation_fade_out && !c->nofadeout) wlr_scene_node_for_each_buffer(&c->scene->node, @@ -662,38 +679,40 @@ void fadeout_client_animation_next_tick(Client *c) { &c->scene->node, snap_scene_buffer_apply_effect, &buffer_data); } - if (animation_passed == 1.0) { + if (animation_passed >= 1.0) { wl_list_remove(&c->fadeout_link); wlr_scene_node_destroy(&c->scene->node); free(c); c = NULL; - } else { - c->animation.passed_frames++; } } void client_animation_next_tick(Client *c) { - double animation_passed = - (double)c->animation.passed_frames / c->animation.total_frames; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); - int type = c->animation.action == NONE ? MOVE : c->animation.action; + int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; + double animation_passed = + c->animation.duration + ? (double)passed_time / (double)c->animation.duration + : 1.0; + + int32_t type = c->animation.action == NONE ? MOVE : c->animation.action; double factor = find_animation_curve_at(animation_passed, type); Client *pointer_c = NULL; double sx = 0, sy = 0; struct wlr_surface *surface = NULL; - unsigned int width = - c->animation.initial.width + - (c->current.width - c->animation.initial.width) * factor; - unsigned int height = - c->animation.initial.height + - (c->current.height - c->animation.initial.height) * factor; + int32_t width = c->animation.initial.width + + (c->current.width - c->animation.initial.width) * factor; + int32_t height = c->animation.initial.height + + (c->current.height - c->animation.initial.height) * factor; - unsigned int x = c->animation.initial.x + - (c->current.x - c->animation.initial.x) * factor; - unsigned int y = c->animation.initial.y + - (c->current.y - c->animation.initial.y) * factor; + int32_t x = c->animation.initial.x + + (c->current.x - c->animation.initial.x) * factor; + int32_t y = c->animation.initial.y + + (c->current.y - c->animation.initial.y) * factor; wlr_scene_node_set_position(&c->scene->node, x, y); c->animation.current = (struct wlr_box){ @@ -705,7 +724,7 @@ void client_animation_next_tick(Client *c) { c->is_pending_open_animation = false; - if (animation_passed == 1.0) { + if (animation_passed >= 1.0) { // clear the open action state // To prevent him from being mistaken that @@ -733,8 +752,6 @@ void client_animation_next_tick(Client *c) { // end flush in next frame, not the current frame c->need_output_flush = false; - } else { - c->animation.passed_frames++; } client_apply_clip(c, factor); @@ -818,14 +835,12 @@ void init_fadeout_client(Client *c) { fadeout_cient->geom.height * zoom_end_ratio; } - fadeout_cient->animation.passed_frames = 0; - fadeout_cient->animation.total_frames = - fadeout_cient->animation.duration / output_frame_duration_ms(); + fadeout_cient->animation.time_started = get_now_in_ms(); wlr_scene_node_set_enabled(&fadeout_cient->scene->node, true); wl_list_insert(&fadeout_clients, &fadeout_cient->fadeout_link); // 请求刷新屏幕 - wlr_output_schedule_frame(c->mon->wlr_output); + request_fresh_all_monitors(); } void client_commit(Client *c) { @@ -837,30 +852,21 @@ void client_commit(Client *c) { } c->animation.initial = c->animainit_geom; - // 设置动画速度 - c->animation.passed_frames = 0; - c->animation.total_frames = - c->animation.duration / output_frame_duration_ms(); + c->animation.time_started = get_now_in_ms(); // 标记动画开始 c->animation.running = true; c->animation.should_animate = false; - } else { - // 如果动画没有开始,且被判定为不应该动画, - // 则设置总帧数为1,不然其他地方一旦获取动画 - // 进度,总帧数作为分母会造成除零 - // 比如动画类型为none的时候 - if (!c->animation.running) { - c->animation.passed_frames = 1; - c->animation.total_frames = 1; - } } // 请求刷新屏幕 - wlr_output_schedule_frame(c->mon->wlr_output); + request_fresh_all_monitors(); } void client_set_pending_state(Client *c) { + if (!c || c->iskilling) + return; + // 判断是否需要动画 if (!animations) { c->animation.should_animate = false; @@ -879,7 +885,22 @@ void client_set_pending_state(Client *c) { (!c->animation_type_open && strcmp(animation_type_open, "none") == 0)) && c->animation.action == OPEN) { + c->animation.duration = 0; + } + + if (c->istagswitching) { + c->animation.duration = 0; + c->istagswitching = 0; + } + + if (start_drag_window) { c->animation.should_animate = false; + c->animation.duration = 0; + } + + if (c->isnoanimation) { + c->animation.should_animate = false; + c->animation.duration = 0; } // 开始动画 @@ -887,7 +908,7 @@ void client_set_pending_state(Client *c) { c->dirty = true; } -void resize(Client *c, struct wlr_box geo, int interact) { +void resize(Client *c, struct wlr_box geo, int32_t interact) { // 动画设置的起始函数,这里用来计算一些动画的起始值 // 动画起始位置大小是由于c->animainit_geom确定的 @@ -904,13 +925,13 @@ void resize(Client *c, struct wlr_box geo, int interact) { c->need_output_flush = true; c->dirty = true; - // oldgeom = c->geom; + // float_geom = c->geom; bbox = (interact || c->isfloating || c->isfullscreen) ? &sgeom : &c->mon->w; if (is_scroller_layout(c->mon) && (!c->isfloating || c == grabc)) { c->geom = geo; - c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); - c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height); + c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width); + c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height); } else { // 这里会限制不允许窗口划出屏幕 c->geom = geo; applybounds( @@ -918,7 +939,7 @@ void resize(Client *c, struct wlr_box geo, int interact) { bbox); // 去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常 } - if (!c->isnosizehint && !c->ismaxmizescreen && !c->isfullscreen && + if (!c->isnosizehint && !c->ismaximizescreen && !c->isfullscreen && c->isfloating) { client_set_size_bound(c); } @@ -997,6 +1018,10 @@ void resize(Client *c, struct wlr_box geo, int interact) { c->animainit_geom = c->geom; } + if (c->scratchpad_switching_mon && c->isfloating) { + c->animainit_geom = c->geom; + } + // 开始应用动画设置 client_set_pending_state(c); @@ -1011,22 +1036,162 @@ bool client_draw_fadeout_frame(Client *c) { return true; } +void client_set_focused_opacity_animation(Client *c) { + float *border_color = get_border_color(c); + + if (!animations) { + setborder_color(c); + return; + } + + c->opacity_animation.duration = animation_duration_focus; + memcpy(c->opacity_animation.target_border_color, border_color, + sizeof(c->opacity_animation.target_border_color)); + c->opacity_animation.target_opacity = c->focused_opacity; + c->opacity_animation.time_started = get_now_in_ms(); + if (c->opacity_animation.running) { + memcpy(c->opacity_animation.initial_border_color, + c->opacity_animation.current_border_color, + sizeof(c->opacity_animation.initial_border_color)); + c->opacity_animation.initial_opacity = + c->opacity_animation.current_opacity; + } else { + memcpy(c->opacity_animation.initial_border_color, border_color, + sizeof(c->opacity_animation.initial_border_color)); + memcpy(c->opacity_animation.current_border_color, border_color, + sizeof(c->opacity_animation.current_border_color)); + c->opacity_animation.initial_opacity = c->unfocused_opacity; + c->opacity_animation.current_opacity = c->unfocused_opacity; + } + c->opacity_animation.running = true; +} + +void client_set_unfocused_opacity_animation(Client *c) { + // Start border color animation to unfocused + float *border_color = get_border_color(c); + + if (!animations) { + setborder_color(c); + return; + } + + c->opacity_animation.duration = animation_duration_focus; + memcpy(c->opacity_animation.target_border_color, border_color, + sizeof(c->opacity_animation.target_border_color)); + // Start opacity animation to unfocused + c->opacity_animation.target_opacity = c->unfocused_opacity; + c->opacity_animation.time_started = get_now_in_ms(); + + if (c->opacity_animation.running) { + memcpy(c->opacity_animation.initial_border_color, + c->opacity_animation.current_border_color, + sizeof(c->opacity_animation.initial_border_color)); + c->opacity_animation.initial_opacity = + c->opacity_animation.current_opacity; + } else { + memcpy(c->opacity_animation.initial_border_color, border_color, + sizeof(c->opacity_animation.initial_border_color)); + memcpy(c->opacity_animation.current_border_color, border_color, + sizeof(c->opacity_animation.current_border_color)); + c->opacity_animation.initial_opacity = c->focused_opacity; + c->opacity_animation.current_opacity = c->focused_opacity; + } + + c->opacity_animation.running = true; +} + +bool client_apply_focus_opacity(Client *c) { + // Animate focus transitions (opacity + border color) + float *border_color = get_border_color(c); + if (c->isfullscreen) { + c->opacity_animation.running = false; + client_set_opacity(c, 1); + } else if (c->animation.running && c->animation.action == OPEN) { + c->opacity_animation.running = false; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; + double linear_progress = + c->animation.duration + ? (double)passed_time / (double)c->animation.duration + : 1.0; + + double opacity_eased_progress = + find_animation_curve_at(linear_progress, OPAFADEIN); + + float percent = + animation_fade_in && !c->nofadein ? opacity_eased_progress : 1.0; + float opacity = + c == selmon->sel ? c->focused_opacity : c->unfocused_opacity; + + float target_opacity = + percent * (1.0 - fadein_begin_opacity) + fadein_begin_opacity; + if (target_opacity > opacity) { + target_opacity = opacity; + } + client_set_opacity(c, target_opacity); + } else if (animations && c->opacity_animation.running) { + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + int32_t passed_time = + timespec_to_ms(&now) - c->opacity_animation.time_started; + double linear_progress = + c->opacity_animation.duration + ? (double)passed_time / (double)c->opacity_animation.duration + : 1.0; + + float eased_progress = find_animation_curve_at(linear_progress, FOCUS); + + c->opacity_animation.current_opacity = + c->opacity_animation.initial_opacity + + (c->opacity_animation.target_opacity - + c->opacity_animation.initial_opacity) * + eased_progress; + client_set_opacity(c, c->opacity_animation.current_opacity); + + // Animate border color + for (int32_t i = 0; i < 4; i++) { + c->opacity_animation.current_border_color[i] = + c->opacity_animation.initial_border_color[i] + + (c->opacity_animation.target_border_color[i] - + c->opacity_animation.initial_border_color[i]) * + eased_progress; + } + client_set_border_color(c, c->opacity_animation.current_border_color); + if (linear_progress == 1.0f) { + c->opacity_animation.running = false; + } else { + return true; + } + } else if (c == selmon->sel) { + c->opacity_animation.running = false; + c->opacity_animation.current_opacity = c->focused_opacity; + memcpy(c->opacity_animation.current_border_color, border_color, + sizeof(c->opacity_animation.current_border_color)); + client_set_opacity(c, c->focused_opacity); + } else { + c->opacity_animation.running = false; + c->opacity_animation.current_opacity = c->unfocused_opacity; + memcpy(c->opacity_animation.current_border_color, border_color, + sizeof(c->opacity_animation.current_border_color)); + client_set_opacity(c, c->unfocused_opacity); + } + + return false; +} + bool client_draw_frame(Client *c) { if (!c || !client_surface(c)->mapped) return false; - if (c->isfullscreen) { - client_set_opacity(c, 1); - } else if (c == selmon->sel && !c->animation.running) { - client_set_opacity(c, c->focused_opacity); - } else if (!c->animation.running) { - client_set_opacity(c, c->unfocused_opacity); + if (!c->need_output_flush) { + return client_apply_focus_opacity(c); } - if (!c->need_output_flush) - return false; - if (animations && c->animation.running) { client_animation_next_tick(c); } else { @@ -1037,5 +1202,6 @@ bool client_draw_frame(Client *c) { client_apply_clip(c, 1.0); c->need_output_flush = false; } + client_apply_focus_opacity(c); return true; } diff --git a/src/animation/common.h b/src/animation/common.h index 02a87f1e..9f022db2 100644 --- a/src/animation/common.h +++ b/src/animation/common.h @@ -1,4 +1,4 @@ -struct dvec2 calculate_animation_curve_at(double t, int type) { +struct dvec2 calculate_animation_curve_at(double t, int32_t type) { struct dvec2 point; double *animation_curve; if (type == MOVE) { @@ -9,6 +9,12 @@ struct dvec2 calculate_animation_curve_at(double t, int type) { animation_curve = animation_curve_tag; } else if (type == CLOSE) { animation_curve = animation_curve_close; + } else if (type == FOCUS) { + animation_curve = animation_curve_focus; + } else if (type == OPAFADEIN) { + animation_curve = animation_curve_opafadein; + } else if (type == OPAFADEOUT) { + animation_curve = animation_curve_opafadeout; } else { animation_curve = animation_curve_move; } @@ -28,30 +34,48 @@ void init_baked_points(void) { baked_points_tag = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_tag)); baked_points_close = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_close)); + baked_points_focus = + calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_focus)); + baked_points_opafadein = + calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_opafadein)); + baked_points_opafadeout = + calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_opafadeout)); - for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) { + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { baked_points_move[i] = calculate_animation_curve_at( (double)i / (BAKED_POINTS_COUNT - 1), MOVE); } - for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) { + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { baked_points_open[i] = calculate_animation_curve_at( (double)i / (BAKED_POINTS_COUNT - 1), OPEN); } - for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) { + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { baked_points_tag[i] = calculate_animation_curve_at( (double)i / (BAKED_POINTS_COUNT - 1), TAG); } - for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) { + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { baked_points_close[i] = calculate_animation_curve_at( (double)i / (BAKED_POINTS_COUNT - 1), CLOSE); } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + baked_points_focus[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), FOCUS); + } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + baked_points_opafadein[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), OPAFADEIN); + } + for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) { + baked_points_opafadeout[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), OPAFADEOUT); + } } -double find_animation_curve_at(double t, int type) { - unsigned int down = 0; - unsigned int up = BAKED_POINTS_COUNT - 1; +double find_animation_curve_at(double t, int32_t type) { + int32_t down = 0; + int32_t up = BAKED_POINTS_COUNT - 1; - unsigned int middle = (up + down) / 2; + int32_t middle = (up + down) / 2; struct dvec2 *baked_points; if (type == MOVE) { baked_points = baked_points_move; @@ -61,6 +85,12 @@ double find_animation_curve_at(double t, int type) { baked_points = baked_points_tag; } else if (type == CLOSE) { baked_points = baked_points_close; + } else if (type == FOCUS) { + baked_points = baked_points_focus; + } else if (type == OPAFADEIN) { + baked_points = baked_points_opafadein; + } else if (type == OPAFADEOUT) { + baked_points = baked_points_opafadeout; } else { baked_points = baked_points_move; } @@ -76,19 +106,8 @@ double find_animation_curve_at(double t, int type) { return baked_points[up].y; } -double output_frame_duration_ms() { - int32_t refresh_total = 0; - Monitor *m; - wl_list_for_each(m, &mons, link) { - if (!m->wlr_output->enabled) { - continue; - } - refresh_total += m->wlr_output->refresh; - } - return 1000000.0 / refresh_total; -} - -static bool scene_node_snapshot(struct wlr_scene_node *node, int lx, int ly, +static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx, + int32_t ly, struct wlr_scene_tree *snapshot_tree) { if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) { return true; @@ -230,4 +249,14 @@ struct wlr_scene_tree *wlr_scene_tree_snapshot(struct wlr_scene_node *node, wlr_scene_node_set_enabled(&snapshot->node, true); return snapshot; +} + +void request_fresh_all_monitors(void) { + Monitor *m = NULL; + wl_list_for_each(m, &mons, link) { + if (!m->wlr_output->enabled) { + continue; + } + wlr_output_schedule_frame(m->wlr_output); + } } \ No newline at end of file diff --git a/src/animation/layer.h b/src/animation/layer.h index bf40da9b..568d52b3 100644 --- a/src/animation/layer.h +++ b/src/animation/layer.h @@ -1,5 +1,4 @@ -void layer_actual_size(LayerSurface *l, unsigned int *width, - unsigned int *height) { +void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height) { struct wlr_box box; if (l->animation.running) { @@ -43,7 +42,7 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { .height = state->desired_height}; // 水平方向定位 - const uint32_t both_horiz = + const int32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; if (box.width == 0) { box.x = bounds.x; @@ -58,7 +57,7 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { } // 垂直方向定位 - const uint32_t both_vert = + const int32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if (box.height == 0) { box.y = bounds.y; @@ -102,10 +101,10 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { } void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) { - int slide_direction; - int horizontal, horizontal_value; - int vertical, vertical_value; - int center_x, center_y; + int32_t slide_direction; + int32_t horizontal, horizontal_value; + int32_t vertical, vertical_value; + int32_t center_x, center_y; if (!l) return; @@ -162,10 +161,10 @@ void layer_draw_shadow(LayerSurface *l) { return; } - uint32_t width, height; + int32_t width, height; layer_actual_size(l, &width, &height); - uint32_t delta = shadows_size; + int32_t delta = shadows_size; /* we calculate where to clip the shadow */ struct wlr_box layer_box = { @@ -201,8 +200,8 @@ void layer_draw_shadow(LayerSurface *l) { wlr_scene_shadow_set_clipped_region(l->shadow, clipped_region); } -void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, - int sy, void *data) { +void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, void *data) { BufferData *buffer_data = (BufferData *)data; struct wlr_scene_surface *scene_surface = @@ -213,9 +212,8 @@ void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, struct wlr_surface *surface = scene_surface->surface; - unsigned int surface_width = - surface->current.width * buffer_data->width_scale; - unsigned int surface_height = + int32_t surface_width = surface->current.width * buffer_data->width_scale; + int32_t surface_height = surface->current.height * buffer_data->height_scale; if (surface_height > 0 && surface_width > 0) { @@ -224,7 +222,8 @@ void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, } void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, - int sx, int sy, void *data) { + int32_t sx, int32_t sy, + void *data) { BufferData *buffer_data = (BufferData *)data; wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, buffer_data->height); @@ -234,21 +233,26 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) { if (!l) return; - double animation_passed = - (double)l->animation.passed_frames / l->animation.total_frames; - int type = l->animation.action = l->animation.action; - double factor = find_animation_curve_at(animation_passed, type); - unsigned int width = - l->animation.initial.width + - (l->current.width - l->animation.initial.width) * factor; - unsigned int height = - l->animation.initial.height + - (l->current.height - l->animation.initial.height) * factor; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); - unsigned int x = l->animation.initial.x + - (l->current.x - l->animation.initial.x) * factor; - unsigned int y = l->animation.initial.y + - (l->current.y - l->animation.initial.y) * factor; + int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started; + double animation_passed = + l->animation.duration + ? (double)passed_time / (double)l->animation.duration + : 1.0; + + int32_t type = l->animation.action = l->animation.action; + double factor = find_animation_curve_at(animation_passed, type); + int32_t width = l->animation.initial.width + + (l->current.width - l->animation.initial.width) * factor; + int32_t height = l->animation.initial.height + + (l->current.height - l->animation.initial.height) * factor; + + int32_t x = l->animation.initial.x + + (l->current.x - l->animation.initial.x) * factor; + int32_t y = l->animation.initial.y + + (l->current.y - l->animation.initial.y) * factor; wlr_scene_node_set_position(&l->scene->node, x, y); @@ -272,19 +276,23 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) { .height = height, }; - double opacity = MAX(fadeout_begin_opacity - animation_passed, 0.0f); + double opacity_eased_progress = + find_animation_curve_at(animation_passed, OPAFADEOUT); + + double percent = fadeout_begin_opacity - + (opacity_eased_progress * fadeout_begin_opacity); + + double opacity = MAX(percent, 0.0f); if (animation_fade_out) wlr_scene_node_for_each_buffer(&l->scene->node, scene_buffer_apply_opacity, &opacity); - if (animation_passed == 1.0) { + if (animation_passed >= 1.0) { wl_list_remove(&l->fadeout_link); wlr_scene_node_destroy(&l->scene->node); free(l); l = NULL; - } else { - l->animation.passed_frames++; } } @@ -293,25 +301,35 @@ void layer_animation_next_tick(LayerSurface *l) { if (!l || !l->mapped) return; - double animation_passed = - (double)l->animation.passed_frames / l->animation.total_frames; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); - int type = l->animation.action == NONE ? MOVE : l->animation.action; + int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started; + double animation_passed = + l->animation.duration + ? (double)passed_time / (double)l->animation.duration + : 1.0; + + int32_t type = l->animation.action == NONE ? MOVE : l->animation.action; double factor = find_animation_curve_at(animation_passed, type); - unsigned int width = - l->animation.initial.width + - (l->current.width - l->animation.initial.width) * factor; - unsigned int height = - l->animation.initial.height + - (l->current.height - l->animation.initial.height) * factor; + int32_t width = l->animation.initial.width + + (l->current.width - l->animation.initial.width) * factor; + int32_t height = l->animation.initial.height + + (l->current.height - l->animation.initial.height) * factor; - unsigned int x = l->animation.initial.x + - (l->current.x - l->animation.initial.x) * factor; - unsigned int y = l->animation.initial.y + - (l->current.y - l->animation.initial.y) * factor; + int32_t x = l->animation.initial.x + + (l->current.x - l->animation.initial.x) * factor; + int32_t y = l->animation.initial.y + + (l->current.y - l->animation.initial.y) * factor; - double opacity = MIN(fadein_begin_opacity + animation_passed, 1.0f); + double opacity_eased_progress = + find_animation_curve_at(animation_passed, OPAFADEIN); + + double opacity = + MIN(fadein_begin_opacity + + opacity_eased_progress * (1.0 - fadein_begin_opacity), + 1.0f); if (animation_fade_in) wlr_scene_node_for_each_buffer(&l->scene->node, @@ -343,12 +361,10 @@ void layer_animation_next_tick(LayerSurface *l) { .height = height, }; - if (animation_passed == 1.0) { + if (animation_passed >= 1.0) { l->animation.running = false; l->need_output_flush = false; l->animation.action = MOVE; - } else { - l->animation.passed_frames++; } } @@ -442,17 +458,16 @@ void init_fadeout_layers(LayerSurface *l) { fadeout_layer->current.height = 0; } - // 计算动画帧数 - fadeout_layer->animation.passed_frames = 0; - fadeout_layer->animation.total_frames = - fadeout_layer->animation.duration / output_frame_duration_ms(); + // 动画开始时间 + fadeout_layer->animation.time_started = get_now_in_ms(); // 将节点插入到关闭动画链表中,屏幕刷新哪里会检查链表中是否有节点可以应用于动画 wlr_scene_node_set_enabled(&fadeout_layer->scene->node, true); wl_list_insert(&fadeout_layers, &fadeout_layer->fadeout_link); // 请求刷新屏幕 - wlr_output_schedule_frame(l->mon->wlr_output); + if (l->mon) + wlr_output_schedule_frame(l->mon->wlr_output); } void layer_set_pending_state(LayerSurface *l) { @@ -528,26 +543,15 @@ void layer_commit(LayerSurface *l) { } l->animation.initial = l->animainit_geom; - // 设置动画速度 - l->animation.passed_frames = 0; - l->animation.total_frames = - l->animation.duration / output_frame_duration_ms(); + l->animation.time_started = get_now_in_ms(); // 标记动画开始 l->animation.running = true; l->animation.should_animate = false; - } else { - // 如果动画没有开始,且被判定为不应该动画, - // 则设置总帧数为1,不然其他地方一旦获取动画 - // 进度,总帧数作为分母会造成除零 - // 比如动画类型为none的时候 - if (!l->animation.running) { - l->animation.passed_frames = 1; - l->animation.total_frames = 1; - } } // 请求刷新屏幕 - wlr_output_schedule_frame(l->mon->wlr_output); + if (l->mon) + wlr_output_schedule_frame(l->mon->wlr_output); } bool layer_draw_frame(LayerSurface *l) { diff --git a/src/animation/tag.h b/src/animation/tag.h new file mode 100644 index 00000000..8e65a93a --- /dev/null +++ b/src/animation/tag.h @@ -0,0 +1,93 @@ +void set_tagin_animation(Monitor *m, Client *c) { + if (c->animation.running) { + c->animainit_geom.x = c->animation.current.x; + c->animainit_geom.y = c->animation.current.y; + return; + } + + if (m->pertag->curtag > m->pertag->prevtag) { + + c->animainit_geom.x = tag_animation_direction == VERTICAL + ? c->animation.current.x + : MAX(c->mon->m.x + c->mon->m.width, + c->geom.x + c->mon->m.width); + c->animainit_geom.y = tag_animation_direction == VERTICAL + ? MAX(c->mon->m.y + c->mon->m.height, + c->geom.y + c->mon->m.height) + : c->animation.current.y; + + } else { + + c->animainit_geom.x = + tag_animation_direction == VERTICAL + ? c->animation.current.x + : MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width); + c->animainit_geom.y = + tag_animation_direction == VERTICAL + ? MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height) + : c->animation.current.y; + } +} + +void set_arrange_visible(Monitor *m, Client *c, bool want_animation) { + + if (!c->is_clip_to_hide || !ISTILED(c) || !is_scroller_layout(c->mon)) { + c->is_clip_to_hide = false; + wlr_scene_node_set_enabled(&c->scene->node, true); + wlr_scene_node_set_enabled(&c->scene_surface->node, true); + } + client_set_suspended(c, false); + + if (!c->animation.tag_from_rule && want_animation && + m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) { + c->animation.tagining = true; + set_tagin_animation(m, c); + } else { + c->animainit_geom.x = c->animation.current.x; + c->animainit_geom.y = c->animation.current.y; + } + + c->animation.tag_from_rule = false; + c->animation.tagouting = false; + c->animation.tagouted = false; + resize(c, c->geom, 0); +} + +void set_tagout_animation(Monitor *m, Client *c) { + if (m->pertag->curtag > m->pertag->prevtag) { + c->pending = c->geom; + c->pending.x = + tag_animation_direction == VERTICAL + ? c->animation.current.x + : MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width); + c->pending.y = tag_animation_direction == VERTICAL + ? MIN(c->mon->m.y - c->geom.height, + c->geom.y - c->mon->m.height) + : c->animation.current.y; + + resize(c, c->geom, 0); + } else { + c->pending = c->geom; + c->pending.x = tag_animation_direction == VERTICAL + ? c->animation.current.x + : MAX(c->mon->m.x + c->mon->m.width, + c->geom.x + c->mon->m.width); + c->pending.y = tag_animation_direction == VERTICAL + ? MAX(c->mon->m.y + c->mon->m.height, + c->geom.y + c->mon->m.height) + : c->animation.current.y; + resize(c, c->geom, 0); + } +} + +void set_arrange_hidden(Monitor *m, Client *c, bool want_animation) { + if ((c->tags & (1 << (m->pertag->prevtag - 1))) && + m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) { + c->animation.tagouting = true; + c->animation.tagining = false; + set_tagout_animation(m, c); + } else { + wlr_scene_node_set_enabled(&c->scene->node, false); + client_set_suspended(c, true); + } +} diff --git a/src/client/client.h b/src/client/client.h index 2436bd9e..8995a5d0 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -6,7 +6,7 @@ */ /* Leave these functions first; they're used in the others */ -static inline int client_is_x11(Client *c) { +static inline int32_t client_is_x11(Client *c) { #ifdef XWAYLAND return c->type == X11; #endif @@ -21,14 +21,15 @@ static inline struct wlr_surface *client_surface(Client *c) { return c->surface.xdg->surface; } -static inline int toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, - LayerSurface **pl) { +static inline int32_t toplevel_from_wlr_surface(struct wlr_surface *s, + Client **pc, + LayerSurface **pl) { struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; struct wlr_surface *root_surface; struct wlr_layer_surface_v1 *layer_surface; Client *c = NULL; LayerSurface *l = NULL; - int type = -1; + int32_t type = -1; #ifdef XWAYLAND struct wlr_xwayland_surface *xsurface; #endif @@ -88,7 +89,7 @@ end: /* The others */ static inline void client_activate_surface(struct wlr_surface *s, - int activated) { + int32_t activated) { struct wlr_xdg_toplevel *toplevel; #ifdef XWAYLAND struct wlr_xwayland_surface *xsurface; @@ -113,7 +114,7 @@ static inline const char *client_get_appid(Client *c) { : "broken"; } -static inline int client_get_pid(Client *c) { +static inline int32_t client_get_pid(Client *c) { pid_t pid; #ifdef XWAYLAND if (client_is_x11(c)) @@ -127,8 +128,8 @@ static inline void client_get_clip(Client *c, struct wlr_box *clip) { *clip = (struct wlr_box){ .x = 0, .y = 0, - .width = c->geom.width - c->bw, - .height = c->geom.height - c->bw, + .width = c->geom.width - 2 * c->bw, + .height = c->geom.height - 2 * c->bw, }; #ifdef XWAYLAND @@ -169,7 +170,7 @@ static inline Client *client_get_parent(Client *c) { return p; } -static inline int client_has_children(Client *c) { +static inline int32_t client_has_children(Client *c) { #ifdef XWAYLAND if (client_is_x11(c)) return !wl_list_empty(&c->surface.xwayland->children); @@ -189,7 +190,7 @@ static inline const char *client_get_title(Client *c) { : "broken"; } -static inline int client_is_float_type(Client *c) { +static inline int32_t client_is_float_type(Client *c) { struct wlr_xdg_toplevel *toplevel; struct wlr_xdg_toplevel_state state; @@ -229,12 +230,12 @@ static inline int client_is_float_type(Client *c) { state.min_height == state.max_height)); } -static inline int client_is_rendered_on_mon(Client *c, Monitor *m) { +static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) { /* This is needed for when you don't want to check formal assignment, * but rather actual displaying of the pixels. * Usually VISIBLEON suffices and is also faster. */ struct wlr_surface_output *s; - int unused_lx, unused_ly; + int32_t unused_lx, unused_ly; if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) return 0; wl_list_for_each(s, &client_surface(c)->current_outputs, @@ -242,8 +243,8 @@ static inline int client_is_rendered_on_mon(Client *c, Monitor *m) { return 0; } -static inline int client_is_stopped(Client *c) { - int pid; +static inline int32_t client_is_stopped(Client *c) { + int32_t pid; siginfo_t in = {0}; #ifdef XWAYLAND if (client_is_x11(c)) @@ -267,7 +268,7 @@ static inline int client_is_stopped(Client *c) { return 0; } -static inline int client_is_unmanaged(Client *c) { +static inline int32_t client_is_unmanaged(Client *c) { #ifdef XWAYLAND if (client_is_x11(c)) return c->surface.xwayland->override_redirect; @@ -299,7 +300,7 @@ static inline void client_set_border_color(Client *c, wlr_scene_rect_set_color(c->border, color); } -static inline void client_set_fullscreen(Client *c, int fullscreen) { +static inline void client_set_fullscreen(Client *c, int32_t fullscreen) { #ifdef XWAYLAND if (client_is_x11(c)) { wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); @@ -341,10 +342,25 @@ static inline void client_set_minimized(Client *c, bool minimized) { return; } +static inline void client_set_maximized(Client *c, bool maximized) { + struct wlr_xdg_toplevel *toplevel; + +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_set_maximized(c->surface.xwayland, maximized, + maximized); + return; + } +#endif + toplevel = c->surface.xdg->toplevel; + wlr_xdg_toplevel_set_maximized(toplevel, maximized); + return; +} + static inline void client_set_tiled(Client *c, uint32_t edges) { struct wlr_xdg_toplevel *toplevel; #ifdef XWAYLAND - if (client_is_x11(c)) { + if (client_is_x11(c) && c->force_maximize) { wlr_xwayland_surface_set_maximized(c->surface.xwayland, edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE); @@ -359,12 +375,12 @@ static inline void client_set_tiled(Client *c, uint32_t edges) { wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); } - if (!c->ignore_maximize) { + if (c->force_maximize) { wlr_xdg_toplevel_set_maximized(toplevel, edges != WLR_EDGE_NONE); } } -static inline void client_set_suspended(Client *c, int suspended) { +static inline void client_set_suspended(Client *c, int32_t suspended) { #ifdef XWAYLAND if (client_is_x11(c)) return; @@ -373,7 +389,22 @@ static inline void client_set_suspended(Client *c, int suspended) { wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); } -static inline int client_should_ignore_focus(Client *c) { +static inline int32_t client_should_ignore_focus(Client *c) { + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + + if (!surface->hints) + return 0; + + return !surface->hints->input; + } +#endif + return 0; +} + +static inline int32_t client_is_x11_popup(Client *c) { #ifdef XWAYLAND if (client_is_x11(c)) { @@ -402,7 +433,7 @@ static inline int client_should_ignore_focus(Client *c) { return 0; } -static inline int client_should_global(Client *c) { +static inline int32_t client_should_global(Client *c) { #ifdef XWAYLAND if (client_is_x11(c)) { @@ -415,7 +446,7 @@ static inline int client_should_global(Client *c) { return 0; } -static inline int client_should_overtop(Client *c) { +static inline int32_t client_should_overtop(Client *c) { #ifdef XWAYLAND if (client_is_x11(c)) { @@ -427,7 +458,7 @@ static inline int client_should_overtop(Client *c) { return 0; } -static inline int client_wants_focus(Client *c) { +static inline int32_t client_wants_focus(Client *c) { #ifdef XWAYLAND return client_is_unmanaged(c) && wlr_xwayland_surface_override_redirect_wants_focus( @@ -438,7 +469,7 @@ static inline int client_wants_focus(Client *c) { return 0; } -static inline int client_wants_fullscreen(Client *c) { +static inline int32_t client_wants_fullscreen(Client *c) { #ifdef XWAYLAND if (client_is_x11(c)) return c->surface.xwayland->fullscreen; @@ -458,6 +489,18 @@ static inline bool client_request_minimize(Client *c, void *data) { return c->surface.xdg->toplevel->requested.minimized; } +static inline bool client_request_maximize(Client *c, void *data) { + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + return surface->maximized_vert || surface->maximized_horz; + } +#endif + + return c->surface.xdg->toplevel->requested.maximized; +} + static inline void client_set_size_bound(Client *c) { struct wlr_xdg_toplevel *toplevel; struct wlr_xdg_toplevel_state state; @@ -470,16 +513,16 @@ static inline void client_set_size_bound(Client *c) { if (!size_hints) return; - if ((unsigned int)c->geom.width - 2 * c->bw < size_hints->min_width && + if ((uint32_t)c->geom.width - 2 * c->bw < size_hints->min_width && size_hints->min_width > 0) c->geom.width = size_hints->min_width + 2 * c->bw; - if ((unsigned int)c->geom.height - 2 * c->bw < size_hints->min_height && + if ((uint32_t)c->geom.height - 2 * c->bw < size_hints->min_height && size_hints->min_height > 0) c->geom.height = size_hints->min_height + 2 * c->bw; - if ((unsigned int)c->geom.width - 2 * c->bw > size_hints->max_width && + if ((uint32_t)c->geom.width - 2 * c->bw > size_hints->max_width && size_hints->max_width > 0) c->geom.width = size_hints->max_width + 2 * c->bw; - if ((unsigned int)c->geom.height - 2 * c->bw > size_hints->max_height && + if ((uint32_t)c->geom.height - 2 * c->bw > size_hints->max_height && size_hints->max_height > 0) c->geom.height = size_hints->max_height + 2 * c->bw; return; @@ -488,19 +531,19 @@ static inline void client_set_size_bound(Client *c) { toplevel = c->surface.xdg->toplevel; state = toplevel->current; - if ((unsigned int)c->geom.width - 2 * c->bw < state.min_width && + if ((uint32_t)c->geom.width - 2 * c->bw < state.min_width && state.min_width > 0) { c->geom.width = state.min_width + 2 * c->bw; } - if ((unsigned int)c->geom.height - 2 * c->bw < state.min_height && + if ((uint32_t)c->geom.height - 2 * c->bw < state.min_height && state.min_height > 0) { c->geom.height = state.min_height + 2 * c->bw; } - if ((unsigned int)c->geom.width - 2 * c->bw > state.max_width && + if ((uint32_t)c->geom.width - 2 * c->bw > state.max_width && state.max_width > 0) { c->geom.width = state.max_width + 2 * c->bw; } - if ((unsigned int)c->geom.height - 2 * c->bw > state.max_height && + if ((uint32_t)c->geom.height - 2 * c->bw > state.max_height && state.max_height > 0) { c->geom.height = state.max_height + 2 * c->bw; } diff --git a/src/common/util.c b/src/common/util.c index 272340cd..a15cca7c 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "util.h" @@ -35,8 +36,8 @@ void *ecalloc(size_t nmemb, size_t size) { return p; } -int fd_set_nonblock(int fd) { - int flags = fcntl(fd, F_GETFL); +int32_t fd_set_nonblock(int32_t fd) { + int32_t flags = fcntl(fd, F_GETFL); if (flags < 0) { perror("fcntl(F_GETFL):"); return -1; @@ -49,8 +50,8 @@ int fd_set_nonblock(int fd) { return 0; } -int regex_match(const char *pattern, const char *str) { - int errnum; +int32_t regex_match(const char *pattern, const char *str) { + int32_t errnum; PCRE2_SIZE erroffset; if (!pattern || !str) { @@ -69,7 +70,7 @@ int regex_match(const char *pattern, const char *str) { pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL); - int ret = + int32_t ret = pcre2_match(re, (PCRE2_SPTR)str, strlen(str), 0, 0, match_data, NULL); pcre2_match_data_free(match_data); @@ -80,3 +81,14 @@ int regex_match(const char *pattern, const char *str) { void wl_list_append(struct wl_list *list, struct wl_list *object) { wl_list_insert(list->prev, object); } + +uint32_t get_now_in_ms(void) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + return timespec_to_ms(&now); +} + +uint32_t timespec_to_ms(struct timespec *ts) { + return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000; +} diff --git a/src/common/util.h b/src/common/util.h index 2ebef43a..8fb60338 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -3,6 +3,8 @@ void die(const char *fmt, ...); void *ecalloc(size_t nmemb, size_t size); -int fd_set_nonblock(int fd); -int regex_match(const char *pattern_mb, const char *str_mb); +int32_t fd_set_nonblock(int32_t fd); +int32_t regex_match(const char *pattern_mb, const char *str_mb); void wl_list_append(struct wl_list *list, struct wl_list *object); +uint32_t get_now_in_ms(void); +uint32_t timespec_to_ms(struct timespec *ts); \ No newline at end of file diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 8ccb0c71..9c278724 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -9,8 +10,9 @@ // 整数版本 - 截断小数部分 #define CLAMP_INT(x, min, max) \ - ((int)(x) < (int)(min) ? (int)(min) \ - : ((int)(x) > (int)(max) ? (int)(max) : (int)(x))) + ((int32_t)(x) < (int32_t)(min) \ + ? (int32_t)(min) \ + : ((int32_t)(x) > (int32_t)(max) ? (int32_t)(max) : (int32_t)(x))) // 浮点数版本 - 保留小数部分 #define CLAMP_FLOAT(x, min, max) \ @@ -18,72 +20,96 @@ enum { NUM_TYPE_MINUS, NUM_TYPE_PLUS, NUM_TYPE_DEFAULT }; -enum { KEY_TYPE_SYM, KEY_TYPE_CODE }; +enum { KEY_TYPE_CODE, KEY_TYPE_SYM }; + +typedef struct { + uint32_t keycode1; + uint32_t keycode2; + uint32_t keycode3; +} MultiKeycode; typedef struct { xkb_keysym_t keysym; - uint32_t keycode; - int type; + MultiKeycode keycode; + int32_t type; } KeySymCode; typedef struct { uint32_t mod; KeySymCode keysymcode; - void (*func)(const Arg *); + int32_t (*func)(const Arg *); Arg arg; + char mode[28]; + bool iscommonmode; + bool isdefaultmode; + bool islockapply; + bool isreleaseapply; + bool ispassapply; } KeyBinding; +typedef struct { + char *type; + char *value; +} ConfigEnv; + typedef struct { const char *id; const char *title; - unsigned int tags; - int isfloating; - int isfullscreen; + uint32_t tags; + int32_t isfloating; + int32_t isfullscreen; float scroller_proportion; const char *animation_type_open; const char *animation_type_close; const char *layer_animation_type_open; const char *layer_animation_type_close; - int isnoborder; - int isopensilent; - int istagsilent; - int isnamedscratchpad; - int isunglobal; - int isglobal; - int isoverlay; - int ignore_maximize; - int ignore_minimize; - int isnosizehint; + int32_t isnoborder; + int32_t isnoshadow; + int32_t isnoradius; + int32_t isnoanimation; + int32_t isopensilent; + int32_t istagsilent; + int32_t isnamedscratchpad; + int32_t isunglobal; + int32_t isglobal; + int32_t isoverlay; + int32_t allow_shortcuts_inhibit; + int32_t ignore_maximize; + int32_t ignore_minimize; + int32_t isnosizehint; const char *monitor; - int offsetx; - int offsety; - int width; - int height; - int nofadein; - int nofadeout; - int no_force_center; - int isterm; - int noswallow; - int noblur; - int scratchpad_width; - int scratchpad_height; + int32_t offsetx; + int32_t offsety; + int32_t width; + int32_t height; + int32_t nofocus; + int32_t nofadein; + int32_t nofadeout; + int32_t no_force_center; + int32_t isterm; + int32_t allow_csd; + int32_t force_maximize; + int32_t force_tearing; + int32_t noswallow; + int32_t noblur; float focused_opacity; float unfocused_opacity; + float scroller_proportion_single; uint32_t passmod; xkb_keysym_t keysym; KeyBinding globalkeybinding; } ConfigWinRule; typedef struct { - const char *name; // 显示器名称 - float mfact; // 主区域比例 - int nmaster; // 主区域窗口数量 - const char *layout; // 布局名称(字符串) - int rr; // 旋转和翻转(假设为整数) - float scale; // 显示器缩放比例 - int x, y; // 显示器位置 - int width, height; // 显示器分辨率 - float refresh; // 刷新率 + const char *name; // 显示器名称 + float mfact; // 主区域比例 + int32_t nmaster; // 主区域窗口数量 + const char *layout; // 布局名称(字符串) + int32_t rr; // 旋转和翻转(假设为整数) + float scale; // 显示器缩放比例 + int32_t x, y; // 显示器位置 + int32_t width, height; // 显示器分辨率 + float refresh; // 刷新率 } ConfigMonitorRule; // 修改后的宏定义 @@ -102,53 +128,60 @@ KeyBinding default_key_bindings[] = {CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(9), CHVT(10), CHVT(11), CHVT(12)}; typedef struct { - unsigned int mod; - unsigned int button; - void (*func)(const Arg *); + uint32_t mod; + uint32_t button; + int32_t (*func)(const Arg *); Arg arg; } MouseBinding; typedef struct { - unsigned int mod; - unsigned int dir; - void (*func)(const Arg *); + uint32_t mod; + uint32_t dir; + int32_t (*func)(const Arg *); Arg arg; } AxisBinding; typedef struct { - unsigned int mod; - unsigned int motion; - unsigned int fingers_count; - void (*func)(const Arg *); + uint32_t fold; + int32_t (*func)(const Arg *); + Arg arg; +} SwitchBinding; + +typedef struct { + uint32_t mod; + uint32_t motion; + uint32_t fingers_count; + int32_t (*func)(const Arg *); Arg arg; } GestureBinding; typedef struct { - int id; // 标签ID (1-9) + int32_t id; // 标签ID (1-9) char *layout_name; // 布局名称 char *monitor_name; - int no_render_border; + int32_t no_render_border; + int32_t no_hide; } ConfigTagRule; typedef struct { char *layer_name; // 布局名称 char *animation_type_open; char *animation_type_close; - int noblur; - int noanim; - int noshadow; + int32_t noblur; + int32_t noanim; + int32_t noshadow; } ConfigLayerRule; typedef struct { - int animations; - int layer_animations; + int32_t animations; + int32_t layer_animations; char animation_type_open[10]; char animation_type_close[10]; char layer_animation_type_open[10]; char layer_animation_type_close[10]; - int animation_fade_in; - int animation_fade_out; - int tag_animation_direction; + int32_t animation_fade_in; + int32_t animation_fade_out; + int32_t tag_animation_direction; float zoom_initial_ratio; float zoom_end_ratio; float fadein_begin_opacity; @@ -157,99 +190,111 @@ typedef struct { uint32_t animation_duration_open; uint32_t animation_duration_tag; uint32_t animation_duration_close; + uint32_t animation_duration_focus; double animation_curve_move[4]; double animation_curve_open[4]; double animation_curve_tag[4]; double animation_curve_close[4]; + double animation_curve_focus[4]; + double animation_curve_opafadein[4]; + double animation_curve_opafadeout[4]; - int scroller_structs; + int32_t scroller_structs; float scroller_default_proportion; float scroller_default_proportion_single; - int scroller_focus_center; - int scroller_prefer_center; - int focus_cross_monitor; - int focus_cross_tag; - int no_border_when_single; - int no_radius_when_single; - int snap_distance; - int enable_floating_snap; - int drag_tile_to_tile; - unsigned int swipe_min_threshold; + int32_t scroller_ignore_proportion_single; + int32_t scroller_focus_center; + int32_t scroller_prefer_center; + int32_t edge_scroller_pointer_focus; + int32_t focus_cross_monitor; + int32_t exchange_cross_monitor; + int32_t scratchpad_cross_monitor; + int32_t focus_cross_tag; + int32_t view_current_to_back; + int32_t no_border_when_single; + int32_t no_radius_when_single; + int32_t snap_distance; + int32_t enable_floating_snap; + int32_t drag_tile_to_tile; + uint32_t swipe_min_threshold; float focused_opacity; float unfocused_opacity; float *scroller_proportion_preset; - int scroller_proportion_preset_count; + int32_t scroller_proportion_preset_count; char **circle_layout; - int circle_layout_count; + int32_t circle_layout_count; - unsigned int new_is_master; + uint32_t new_is_master; float default_mfact; - float default_smfact; - unsigned int default_nmaster; + uint32_t default_nmaster; + int32_t center_master_overspread; + int32_t center_when_single_stack; - unsigned int hotarea_size; - unsigned int enable_hotarea; - unsigned int ov_tab_mode; - int overviewgappi; - int overviewgappo; - unsigned int cursor_hide_timeout; + uint32_t hotarea_size; + uint32_t enable_hotarea; + uint32_t ov_tab_mode; + int32_t overviewgappi; + int32_t overviewgappo; + uint32_t cursor_hide_timeout; - unsigned int axis_bind_apply_timeout; - unsigned int focus_on_activate; - int inhibit_regardless_of_visibility; - int sloppyfocus; - int warpcursor; + uint32_t axis_bind_apply_timeout; + uint32_t focus_on_activate; + int32_t idleinhibit_ignore_visible; + int32_t sloppyfocus; + int32_t warpcursor; /* keyboard */ - int repeat_rate; - int repeat_delay; - unsigned int numlockon; + int32_t repeat_rate; + int32_t repeat_delay; + uint32_t numlockon; /* Trackpad */ - int disable_trackpad; - int tap_to_click; - int tap_and_drag; - int drag_lock; - int mouse_natural_scrolling; - int trackpad_natural_scrolling; - int disable_while_typing; - int left_handed; - int middle_button_emulation; - unsigned int accel_profile; + int32_t disable_trackpad; + int32_t tap_to_click; + int32_t tap_and_drag; + int32_t drag_lock; + int32_t mouse_natural_scrolling; + int32_t trackpad_natural_scrolling; + int32_t disable_while_typing; + int32_t left_handed; + int32_t middle_button_emulation; + uint32_t accel_profile; double accel_speed; - unsigned int scroll_method; - unsigned int scroll_button; - unsigned int click_method; - unsigned int send_events_mode; - unsigned int button_map; + uint32_t scroll_method; + uint32_t scroll_button; + uint32_t click_method; + uint32_t send_events_mode; + uint32_t button_map; - int blur; - int blur_layer; - int blur_optimized; - int border_radius; + double axis_scroll_factor; + + int32_t blur; + int32_t blur_layer; + int32_t blur_optimized; + int32_t border_radius; struct blur_data blur_params; - int shadows; - int shadow_only_floating; - int layer_shadows; - unsigned int shadows_size; + int32_t shadows; + int32_t shadow_only_floating; + int32_t layer_shadows; + uint32_t shadows_size; float shadows_blur; - int shadows_position_x; - int shadows_position_y; + int32_t shadows_position_x; + int32_t shadows_position_y; float shadowscolor[4]; - int smartgaps; - unsigned int gappih; - unsigned int gappiv; - unsigned int gappoh; - unsigned int gappov; - unsigned int borderpx; + int32_t smartgaps; + uint32_t gappih; + uint32_t gappiv; + uint32_t gappoh; + uint32_t gappov; + uint32_t borderpx; float scratchpad_width_ratio; float scratchpad_height_ratio; float rootcolor[4]; float bordercolor[4]; float focuscolor[4]; - float maxmizescreencolor[4]; + float maximizescreencolor[4]; float urgentcolor[4]; float scratchpadcolor[4]; float globalcolor[4]; @@ -258,46 +303,61 @@ typedef struct { char autostart[3][256]; ConfigTagRule *tag_rules; // 动态数组 - int tag_rules_count; // 数量 + int32_t tag_rules_count; // 数量 ConfigLayerRule *layer_rules; // 动态数组 - int layer_rules_count; // 数量 + int32_t layer_rules_count; // 数量 ConfigWinRule *window_rules; - int window_rules_count; + int32_t window_rules_count; ConfigMonitorRule *monitor_rules; // 动态数组 - int monitor_rules_count; // 条数 + int32_t monitor_rules_count; // 条数 KeyBinding *key_bindings; - int key_bindings_count; + int32_t key_bindings_count; MouseBinding *mouse_bindings; - int mouse_bindings_count; + int32_t mouse_bindings_count; AxisBinding *axis_bindings; - int axis_bindings_count; + int32_t axis_bindings_count; + + SwitchBinding *switch_bindings; + int32_t switch_bindings_count; GestureBinding *gesture_bindings; - int gesture_bindings_count; + int32_t gesture_bindings_count; + + ConfigEnv **env; + int32_t env_count; char **exec; - int exec_count; + int32_t exec_count; char **exec_once; - int exec_once_count; + int32_t exec_once_count; char *cursor_theme; - unsigned int cursor_size; + uint32_t cursor_size; - int single_scratchpad; - int xwayland_persistence; - int syncobj_enable; + int32_t single_scratchpad; + int32_t xwayland_persistence; + int32_t syncobj_enable; + int32_t adaptive_sync; + int32_t allow_tearing; + int32_t allow_shortcuts_inhibit; + int32_t allow_lock_transparent; struct xkb_rule_names xkb_rules; + + char keymode[28]; + + struct xkb_context *ctx; + struct xkb_keymap *keymap; } Config; -typedef void (*FuncType)(const Arg *); +typedef int32_t (*FuncType)(const Arg *); Config config; void parse_config_file(Config *config, const char *file_path); @@ -328,22 +388,27 @@ void trim_whitespace(char *str) { } } -int parse_double_array(const char *input, double *output, int max_count) { - char *dup = strdup(input); // 复制一份用于修改 +int32_t parse_double_array(const char *input, double *output, + int32_t max_count) { + char *dup = strdup(input); char *token; - int count = 0; + int32_t count = 0; + + // 先清空整个数组 + memset(output, 0, max_count * sizeof(double)); token = strtok(dup, ","); while (token != NULL && count < max_count) { - trim_whitespace(token); // 对每一个分割后的 token 去除前后空格 + trim_whitespace(token); char *endptr; double val = strtod(token, &endptr); if (endptr == token || *endptr != '\0') { fprintf(stderr, "Error: Invalid number in array: %s\n", token); free(dup); - return -1; // 解析失败 + return -1; } - output[count++] = val; + output[count] = val; // 赋值到当前count位置 + count++; // 然后才自增 token = strtok(NULL, ","); } @@ -364,10 +429,42 @@ char *sanitize_string(char *str) { return str; } -int parse_circle_direction(const char *str) { +// 解析bind组合字符串 +void parse_bind_flags(const char *str, KeyBinding *kb) { + + // 检查是否以"bind"开头 + if (strncmp(str, "bind", 4) != 0) { + return; + } + + const char *suffix = str + 4; // 跳过"bind" + + // 遍历后缀字符 + for (int32_t i = 0; suffix[i] != '\0'; i++) { + switch (suffix[i]) { + case 's': + kb->keysymcode.type = KEY_TYPE_SYM; + break; + case 'l': + kb->islockapply = true; + break; + case 'r': + kb->isreleaseapply = true; + break; + case 'p': + kb->ispassapply = true; + break; + default: + // 忽略其他字符或可根据需要处理错误 + break; + } + } +} + +int32_t parse_circle_direction(const char *str) { // 将输入字符串转换为小写 char lowerStr[10]; - int i = 0; + int32_t i = 0; while (str[i] && i < 9) { lowerStr[i] = tolower(str[i]); i++; @@ -376,16 +473,16 @@ int parse_circle_direction(const char *str) { // 根据转换后的小写字符串返回对应的枚举值 if (strcmp(lowerStr, "next") == 0) { - return 1; + return NEXT; } else { - return -1; + return PREV; } } -int parse_direction(const char *str) { +int32_t parse_direction(const char *str) { // 将输入字符串转换为小写 char lowerStr[10]; - int i = 0; + int32_t i = 0; while (str[i] && i < 9) { lowerStr[i] = tolower(str[i]); i++; @@ -406,70 +503,34 @@ int parse_direction(const char *str) { } } -long int parse_color(const char *hex_str) { +int32_t parse_fold_state(const char *str) { + // 将输入字符串转换为小写 + char lowerStr[10]; + int32_t i = 0; + while (str[i] && i < 9) { + lowerStr[i] = tolower(str[i]); + i++; + } + lowerStr[i] = '\0'; + + // 根据转换后的小写字符串返回对应的枚举值 + if (strcmp(lowerStr, "fold") == 0) { + return FOLD; + } else if (strcmp(lowerStr, "unfold") == 0) { + return UNFOLD; + } else { + return INVALIDFOLD; + } +} +int64_t parse_color(const char *hex_str) { char *endptr; - long int hex_num = strtol(hex_str, &endptr, 16); + int64_t hex_num = strtol(hex_str, &endptr, 16); if (*endptr != '\0') { return -1; } return hex_num; } -xkb_keysym_t normalize_keysym(xkb_keysym_t sym) { - // 首先转换为小写(主要影响字母键) - sym = xkb_keysym_to_lower(sym); - - // 将数字小键盘键转换为普通数字键 - switch (sym) { - // 小键盘数字转换 - case XKB_KEY_KP_0: - return XKB_KEY_0; - case XKB_KEY_KP_1: - return XKB_KEY_1; - case XKB_KEY_KP_2: - return XKB_KEY_2; - case XKB_KEY_KP_3: - return XKB_KEY_3; - case XKB_KEY_KP_4: - return XKB_KEY_4; - case XKB_KEY_KP_5: - return XKB_KEY_5; - case XKB_KEY_KP_6: - return XKB_KEY_6; - case XKB_KEY_KP_7: - return XKB_KEY_7; - case XKB_KEY_KP_8: - return XKB_KEY_8; - case XKB_KEY_KP_9: - return XKB_KEY_9; - - // 将 Shift+数字 的符号转换回基础数字 - case XKB_KEY_exclam: - return XKB_KEY_1; // ! - case XKB_KEY_at: - return XKB_KEY_2; // @ - case XKB_KEY_numbersign: - return XKB_KEY_3; // # - case XKB_KEY_dollar: - return XKB_KEY_4; // $ - case XKB_KEY_percent: - return XKB_KEY_5; // % - case XKB_KEY_asciicircum: - return XKB_KEY_6; // ^ - case XKB_KEY_ampersand: - return XKB_KEY_7; // & - case XKB_KEY_asterisk: - return XKB_KEY_8; // * - case XKB_KEY_parenleft: - return XKB_KEY_9; // ( - case XKB_KEY_parenright: - return XKB_KEY_0; // ) - - default: - return sym; - } -} - // 辅助函数:检查字符串是否以指定的前缀开头(忽略大小写) static bool starts_with_ignore_case(const char *str, const char *prefix) { while (*prefix) { @@ -560,30 +621,128 @@ uint32_t parse_mod(const char *mod_str) { return mod; } -KeySymCode parse_key(const char *key_str) { - KeySymCode kc; +// 定义辅助函数:在 keymap 中查找 keysym 对应的多个 keycode +static int32_t find_keycodes_for_keysym(struct xkb_keymap *keymap, + xkb_keysym_t sym, + MultiKeycode *multi_kc) { + xkb_keycode_t min_keycode = xkb_keymap_min_keycode(keymap); + xkb_keycode_t max_keycode = xkb_keymap_max_keycode(keymap); + + multi_kc->keycode1 = 0; + multi_kc->keycode2 = 0; + multi_kc->keycode3 = 0; + + int32_t found_count = 0; + + for (xkb_keycode_t keycode = min_keycode; + keycode <= max_keycode && found_count < 3; keycode++) { + // 使用布局0和层级0 + const xkb_keysym_t *syms; + int32_t num_syms = + xkb_keymap_key_get_syms_by_level(keymap, keycode, 0, 0, &syms); + + for (int32_t i = 0; i < num_syms; i++) { + if (syms[i] == sym) { + switch (found_count) { + case 0: + multi_kc->keycode1 = keycode; + break; + case 1: + multi_kc->keycode2 = keycode; + break; + case 2: + multi_kc->keycode3 = keycode; + break; + } + found_count++; + break; + } + } + } + + return found_count; +} + +void cleanup_config_keymap(void) { + if (config.keymap != NULL) { + xkb_keymap_unref(config.keymap); + config.keymap = NULL; + } + if (config.ctx != NULL) { + xkb_context_unref(config.ctx); + config.ctx = NULL; + } +} + +void create_config_keymap(void) { + // 初始化 xkb 上下文和 keymap + + if (config.ctx == NULL) { + config.ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + } + + if (config.keymap == NULL) { + config.keymap = xkb_keymap_new_from_names( + config.ctx, &xkb_fallback_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + } +} + +KeySymCode parse_key(const char *key_str, bool isbindsym) { + KeySymCode kc = {0}; // 初始化为0 + + if (config.keymap == NULL || config.ctx == NULL) { + // 处理错误 + kc.type = KEY_TYPE_SYM; + kc.keysym = XKB_KEY_NoSymbol; + return kc; + } // 处理 code: 前缀的情况 if (strncmp(key_str, "code:", 5) == 0) { char *endptr; xkb_keycode_t keycode = (xkb_keycode_t)strtol(key_str + 5, &endptr, 10); kc.type = KEY_TYPE_CODE; - kc.keycode = keycode; + kc.keycode.keycode1 = keycode; // 只设置第一个 + kc.keycode.keycode2 = 0; + kc.keycode.keycode3 = 0; return kc; } // 普通键名直接转换 xkb_keysym_t sym = xkb_keysym_from_name(key_str, XKB_KEYSYM_NO_FLAGS); - kc.type = KEY_TYPE_SYM; - kc.keycode = 0; - kc.keysym = sym; + + if (isbindsym) { + kc.type = KEY_TYPE_SYM; + kc.keysym = sym; + return kc; + } + + if (sym != XKB_KEY_NoSymbol) { + // 尝试找到对应的多个 keycode + int32_t found_count = + find_keycodes_for_keysym(config.keymap, sym, &kc.keycode); + if (found_count > 0) { + kc.type = KEY_TYPE_CODE; + kc.keysym = sym; // 仍然保存 keysym 供参考 + } else { + kc.type = KEY_TYPE_SYM; + kc.keysym = sym; + // keycode 字段保持为0 + } + } else { + // 无法解析的键名 + kc.type = KEY_TYPE_SYM; + kc.keysym = XKB_KEY_NoSymbol; + // keycode 字段保持为0 + } + return kc; } -int parse_button(const char *str) { +int32_t parse_button(const char *str) { // 将输入字符串转换为小写 char lowerStr[20]; - int i = 0; + int32_t i = 0; while (str[i] && i < 19) { lowerStr[i] = tolower(str[i]); i++; @@ -612,10 +771,10 @@ int parse_button(const char *str) { } } -int parse_mouse_action(const char *str) { +int32_t parse_mouse_action(const char *str) { // 将输入字符串转换为小写 char lowerStr[20]; - int i = 0; + int32_t i = 0; while (str[i] && i < 19) { lowerStr[i] = tolower(str[i]); i++; @@ -636,14 +795,14 @@ int parse_mouse_action(const char *str) { } } -void convert_hex_to_rgba(float *color, unsigned long int hex) { +void convert_hex_to_rgba(float *color, uint32_t hex) { color[0] = ((hex >> 24) & 0xFF) / 255.0f; color[1] = ((hex >> 16) & 0xFF) / 255.0f; color[2] = ((hex >> 8) & 0xFF) / 255.0f; color[3] = (hex & 0xFF) / 255.0f; } -unsigned int parse_num_type(char *str) { +uint32_t parse_num_type(char *str) { switch (str[0]) { case '-': return NUM_TYPE_MINUS; @@ -675,48 +834,101 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "setmfact") == 0) { func = setmfact; (*arg).f = atof(arg_value); - } else if (strcmp(func_name, "setsmfact") == 0) { - func = setsmfact; - (*arg).f = atof(arg_value); } else if (strcmp(func_name, "zoom") == 0) { func = zoom; } else if (strcmp(func_name, "exchange_client") == 0) { func = exchange_client; (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "exchange_stack_client") == 0) { + func = exchange_stack_client; + (*arg).i = parse_circle_direction(arg_value); } else if (strcmp(func_name, "toggleglobal") == 0) { func = toggleglobal; } else if (strcmp(func_name, "toggleoverview") == 0) { func = toggleoverview; + (*arg).i = atoi(arg_value); } else if (strcmp(func_name, "set_proportion") == 0) { func = set_proportion; (*arg).f = atof(arg_value); - } else if (strcmp(func_name, "increase_proportion") == 0) { - func = increase_proportion; - (*arg).f = atof(arg_value); } else if (strcmp(func_name, "switch_proportion_preset") == 0) { func = switch_proportion_preset; } else if (strcmp(func_name, "viewtoleft") == 0) { func = viewtoleft; + (*arg).i = atoi(arg_value); } else if (strcmp(func_name, "viewtoright") == 0) { func = viewtoright; + (*arg).i = atoi(arg_value); } else if (strcmp(func_name, "tagsilent") == 0) { func = tagsilent; (*arg).ui = 1 << (atoi(arg_value) - 1); } else if (strcmp(func_name, "tagtoleft") == 0) { func = tagtoleft; + (*arg).i = atoi(arg_value); } else if (strcmp(func_name, "tagtoright") == 0) { func = tagtoright; + (*arg).i = atoi(arg_value); } else if (strcmp(func_name, "killclient") == 0) { func = killclient; + } else if (strcmp(func_name, "centerwin") == 0) { + func = centerwin; } else if (strcmp(func_name, "focuslast") == 0) { func = focuslast; + } else if (strcmp(func_name, "toggle_trackpad_enable") == 0) { + func = toggle_trackpad_enable; + } else if (strcmp(func_name, "setoption") == 0) { + func = setoption; + + (*arg).v = strdup(arg_value); + + // 收集需要拼接的参数 + const char *non_empty_params[4] = {NULL}; + int32_t param_index = 0; + + if (arg_value2 && arg_value2[0] != '\0') + non_empty_params[param_index++] = arg_value2; + if (arg_value3 && arg_value3[0] != '\0') + non_empty_params[param_index++] = arg_value3; + if (arg_value4 && arg_value4[0] != '\0') + non_empty_params[param_index++] = arg_value4; + if (arg_value5 && arg_value5[0] != '\0') + non_empty_params[param_index++] = arg_value5; + + // 处理拼接 + if (param_index == 0) { + (*arg).v2 = strdup(""); + } else { + // 计算总长度 + size_t len = 0; + for (int32_t i = 0; i < param_index; i++) { + len += strlen(non_empty_params[i]); + } + len += (param_index - 1) + 1; // 逗号数 + null终止符 + + char *temp = malloc(len); + if (temp) { + char *cursor = temp; + for (int32_t i = 0; i < param_index; i++) { + if (i > 0) { + *cursor++ = ','; + } + size_t param_len = strlen(non_empty_params[i]); + memcpy(cursor, non_empty_params[i], param_len); + cursor += param_len; + } + *cursor = '\0'; + (*arg).v2 = temp; + } + } + } else if (strcmp(func_name, "setkeymode") == 0) { + func = setkeymode; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "switch_keyboard_layout") == 0) { + func = switch_keyboard_layout; } else if (strcmp(func_name, "setlayout") == 0) { func = setlayout; (*arg).v = strdup(arg_value); } else if (strcmp(func_name, "switch_layout") == 0) { func = switch_layout; - } else if (strcmp(func_name, "switch_keyboard_layout") == 0) { - func = switch_keyboard_layout; } else if (strcmp(func_name, "togglefloating") == 0) { func = togglefloating; } else if (strcmp(func_name, "togglefullscreen") == 0) { @@ -725,10 +937,10 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, func = togglefakefullscreen; } else if (strcmp(func_name, "toggleoverlay") == 0) { func = toggleoverlay; - } else if (strcmp(func_name, "minized") == 0) { - func = minized; - } else if (strcmp(func_name, "restore_minized") == 0) { - func = restore_minized; + } else if (strcmp(func_name, "minimized") == 0) { + func = minimized; + } else if (strcmp(func_name, "restore_minimized") == 0) { + func = restore_minimized; } else if (strcmp(func_name, "toggle_scratchpad") == 0) { func = toggle_scratchpad; } else if (strcmp(func_name, "toggle_render_border") == 0) { @@ -742,7 +954,7 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "tagmon") == 0) { func = tagmon; (*arg).i = parse_direction(arg_value); - (*arg).ui = atoi(arg_value2); + (*arg).i2 = atoi(arg_value2); if ((*arg).i == UNDIR) { (*arg).v = strdup(arg_value); }; @@ -757,6 +969,9 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "spawn") == 0) { func = spawn; (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "spawn_shell") == 0) { + func = spawn_shell; + (*arg).v = strdup(arg_value); } else if (strcmp(func_name, "spawn_on_empty") == 0) { func = spawn_on_empty; (*arg).v = strdup(arg_value); // 注意:之后需要释放这个内存 @@ -770,26 +985,65 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "moveresize") == 0) { func = moveresize; (*arg).ui = parse_mouse_action(arg_value); - } else if (strcmp(func_name, "togglemaxmizescreen") == 0) { - func = togglemaxmizescreen; + } else if (strcmp(func_name, "togglemaximizescreen") == 0) { + func = togglemaximizescreen; } else if (strcmp(func_name, "viewtoleft_have_client") == 0) { func = viewtoleft_have_client; + (*arg).i = atoi(arg_value); } else if (strcmp(func_name, "viewtoright_have_client") == 0) { func = viewtoright_have_client; + (*arg).i = atoi(arg_value); } else if (strcmp(func_name, "reload_config") == 0) { func = reload_config; } else if (strcmp(func_name, "tag") == 0) { func = tag; (*arg).ui = 1 << (atoi(arg_value) - 1); + (*arg).i = atoi(arg_value2); } else if (strcmp(func_name, "view") == 0) { func = bind_to_view; + + uint32_t mask = 0; + char *token; + char *arg_copy = strdup(arg_value); + + if (arg_copy != NULL) { + char *saveptr = NULL; + token = strtok_r(arg_copy, "|", &saveptr); + + while (token != NULL) { + int32_t num = atoi(token); + if (num > 0 && num <= LENGTH(tags)) { + mask |= (1 << (num - 1)); + } + token = strtok_r(NULL, "|", &saveptr); + } + + free(arg_copy); + } + + if (mask) { + (*arg).ui = mask; + } else { + (*arg).ui = atoi(arg_value); + } + (*arg).i = atoi(arg_value2); + } else if (strcmp(func_name, "viewcrossmon") == 0) { + func = viewcrossmon; (*arg).ui = 1 << (atoi(arg_value) - 1); + (*arg).v = strdup(arg_value2); + } else if (strcmp(func_name, "tagcrossmon") == 0) { + func = tagcrossmon; + (*arg).ui = 1 << (atoi(arg_value) - 1); + (*arg).v = strdup(arg_value2); } else if (strcmp(func_name, "toggletag") == 0) { func = toggletag; (*arg).ui = 1 << (atoi(arg_value) - 1); } else if (strcmp(func_name, "toggleview") == 0) { func = toggleview; (*arg).ui = 1 << (atoi(arg_value) - 1); + } else if (strcmp(func_name, "comboview") == 0) { + func = comboview; + (*arg).ui = 1 << (atoi(arg_value) - 1); } else if (strcmp(func_name, "smartmovewin") == 0) { func = smartmovewin; (*arg).i = parse_direction(arg_value); @@ -816,45 +1070,50 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, func = toggle_named_scratchpad; (*arg).v = strdup(arg_value); (*arg).v2 = strdup(arg_value2); - (*arg).v3 = strdup(arg_value5); - (*arg).ui = arg_value3 ? atoi(arg_value3) : 0; - (*arg).ui2 = arg_value4 ? atoi(arg_value4) : 0; + (*arg).v3 = strdup(arg_value3); + } else if (strcmp(func_name, "disable_monitor") == 0) { + func = disable_monitor; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "enable_monitor") == 0) { + func = enable_monitor; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "toggle_monitor") == 0) { + func = toggle_monitor; + (*arg).v = strdup(arg_value); } else { return NULL; } return func; } +void set_env() { + for (int32_t i = 0; i < config.env_count; i++) { + setenv(config.env[i]->type, config.env[i]->value, 1); + } +} + void run_exec() { Arg arg; - for (int i = 0; i < config.exec_count; i++) { + for (int32_t i = 0; i < config.exec_count; i++) { arg.v = config.exec[i]; - spawn(&arg); + spawn_shell(&arg); } } void run_exec_once() { Arg arg; - for (int i = 0; i < config.exec_once_count; i++) { + for (int32_t i = 0; i < config.exec_once_count; i++) { arg.v = config.exec_once[i]; - spawn(&arg); + spawn_shell(&arg); } } -void parse_config_line(Config *config, const char *line) { - char key[256], value[256]; - if (sscanf(line, "%[^=]=%[^\n]", key, value) != 2) { - // fprintf(stderr, "Error: Invalid line format: %s\n", line); - return; - } - - // Then trim each part separately - trim_whitespace(key); - trim_whitespace(value); - - if (strcmp(key, "animations") == 0) { +void parse_option(Config *config, char *key, char *value) { + if (strcmp(key, "keymode") == 0) { + snprintf(config->keymode, sizeof(config->keymode), "%.27s", value); + } else if (strcmp(key, "animations") == 0) { config->animations = atoi(value); } else if (strcmp(key, "layer_animations") == 0) { config->layer_animations = atoi(value); @@ -896,47 +1155,84 @@ void parse_config_line(Config *config, const char *line) { config->animation_duration_tag = atoi(value); } else if (strcmp(key, "animation_duration_close") == 0) { config->animation_duration_close = atoi(value); + } else if (strcmp(key, "animation_duration_focus") == 0) { + config->animation_duration_focus = atoi(value); } else if (strcmp(key, "animation_curve_move") == 0) { - int num = parse_double_array(value, config->animation_curve_move, 4); + int32_t num = + parse_double_array(value, config->animation_curve_move, 4); if (num != 4) { fprintf(stderr, "Error: Failed to parse animation_curve_move: %s\n", value); } } else if (strcmp(key, "animation_curve_open") == 0) { - int num = parse_double_array(value, config->animation_curve_open, 4); + int32_t num = + parse_double_array(value, config->animation_curve_open, 4); if (num != 4) { fprintf(stderr, "Error: Failed to parse animation_curve_open: %s\n", value); } } else if (strcmp(key, "animation_curve_tag") == 0) { - int num = parse_double_array(value, config->animation_curve_tag, 4); + int32_t num = parse_double_array(value, config->animation_curve_tag, 4); if (num != 4) { fprintf(stderr, "Error: Failed to parse animation_curve_tag: %s\n", value); } } else if (strcmp(key, "animation_curve_close") == 0) { - int num = parse_double_array(value, config->animation_curve_close, 4); + int32_t num = + parse_double_array(value, config->animation_curve_close, 4); if (num != 4) { fprintf(stderr, "Error: Failed to parse animation_curve_close: %s\n", value); } + } else if (strcmp(key, "animation_curve_focus") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_focus, 4); + if (num != 4) { + fprintf(stderr, + "Error: Failed to parse animation_curve_focus: %s\n", + value); + } + } else if (strcmp(key, "animation_curve_opafadein") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_opafadein, 4); + if (num != 4) { + fprintf(stderr, + "Error: Failed to parse animation_curve_opafadein: %s\n", + value); + } + } else if (strcmp(key, "animation_curve_opafadeout") == 0) { + int32_t num = + parse_double_array(value, config->animation_curve_opafadeout, 4); + if (num != 4) { + fprintf(stderr, + "Error: Failed to parse animation_curve_opafadeout: %s\n", + value); + } } else if (strcmp(key, "scroller_structs") == 0) { config->scroller_structs = atoi(value); } else if (strcmp(key, "scroller_default_proportion") == 0) { config->scroller_default_proportion = atof(value); } else if (strcmp(key, "scroller_default_proportion_single") == 0) { config->scroller_default_proportion_single = atof(value); + } else if (strcmp(key, "scroller_ignore_proportion_single") == 0) { + config->scroller_ignore_proportion_single = atoi(value); } else if (strcmp(key, "scroller_focus_center") == 0) { config->scroller_focus_center = atoi(value); } else if (strcmp(key, "scroller_prefer_center") == 0) { config->scroller_prefer_center = atoi(value); + } else if (strcmp(key, "edge_scroller_pointer_focus") == 0) { + config->edge_scroller_pointer_focus = atoi(value); } else if (strcmp(key, "focus_cross_monitor") == 0) { config->focus_cross_monitor = atoi(value); + } else if (strcmp(key, "exchange_cross_monitor") == 0) { + config->exchange_cross_monitor = atoi(value); + } else if (strcmp(key, "scratchpad_cross_monitor") == 0) { + config->scratchpad_cross_monitor = atoi(value); } else if (strcmp(key, "focus_cross_tag") == 0) { config->focus_cross_tag = atoi(value); - } else if (strcmp(key, "focus_cross_tag") == 0) { - config->focus_cross_tag = atoi(value); + } else if (strcmp(key, "view_current_to_back") == 0) { + config->view_current_to_back = atoi(value); } else if (strcmp(key, "blur") == 0) { config->blur = atoi(value); } else if (strcmp(key, "blur_layer") == 0) { @@ -977,6 +1273,14 @@ void parse_config_line(Config *config, const char *line) { config->xwayland_persistence = atoi(value); } else if (strcmp(key, "syncobj_enable") == 0) { config->syncobj_enable = atoi(value); + } else if (strcmp(key, "adaptive_sync") == 0) { + config->adaptive_sync = atoi(value); + } else if (strcmp(key, "allow_tearing") == 0) { + config->allow_tearing = atoi(value); + } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { + config->allow_shortcuts_inhibit = atoi(value); + } else if (strcmp(key, "allow_lock_transparent") == 0) { + config->allow_lock_transparent = atoi(value); } else if (strcmp(key, "no_border_when_single") == 0) { config->no_border_when_single = atoi(value); } else if (strcmp(key, "no_radius_when_single") == 0) { @@ -1015,12 +1319,12 @@ void parse_config_line(Config *config, const char *line) { '\0'; // 确保字符串以 null 结尾 } else if (strcmp(key, "scroller_proportion_preset") == 0) { // 1. 统计 value 中有多少个逗号,确定需要解析的浮点数个数 - int count = 0; // 初始化为 0 + int32_t count = 0; // 初始化为 0 for (const char *p = value; *p; p++) { if (*p == ',') count++; } - int float_count = count + 1; // 浮点数的数量是逗号数量加 1 + int32_t float_count = count + 1; // 浮点数的数量是逗号数量加 1 // 2. 动态分配内存,存储浮点数 config->scroller_proportion_preset = @@ -1034,7 +1338,7 @@ void parse_config_line(Config *config, const char *line) { char *value_copy = strdup(value); // 复制 value,因为 strtok 会修改原字符串 char *token = strtok(value_copy, ","); - int i = 0; + int32_t i = 0; float value_set; while (token != NULL && i < float_count) { @@ -1074,12 +1378,12 @@ void parse_config_line(Config *config, const char *line) { free(value_copy); } else if (strcmp(key, "circle_layout") == 0) { // 1. 统计 value 中有多少个逗号,确定需要解析的字符串个数 - int count = 0; // 初始化为 0 + int32_t count = 0; // 初始化为 0 for (const char *p = value; *p; p++) { if (*p == ',') count++; } - int string_count = count + 1; // 字符串的数量是逗号数量加 1 + int32_t string_count = count + 1; // 字符串的数量是逗号数量加 1 // 2. 动态分配内存,存储字符串指针 config->circle_layout = (char **)malloc(string_count * sizeof(char *)); @@ -1093,7 +1397,7 @@ void parse_config_line(Config *config, const char *line) { char *value_copy = strdup(value); // 复制 value,因为 strtok 会修改原字符串 char *token = strtok(value_copy, ","); - int i = 0; + int32_t i = 0; char *cleaned_token; while (token != NULL && i < string_count) { // 为每个字符串分配内存并复制内容 @@ -1104,7 +1408,7 @@ void parse_config_line(Config *config, const char *line) { "Error: Memory allocation failed for string: %s\n", token); // 释放之前分配的内存 - for (int j = 0; j < i; j++) { + for (int32_t j = 0; j < i; j++) { free(config->circle_layout[j]); } free(config->circle_layout); @@ -1121,7 +1425,7 @@ void parse_config_line(Config *config, const char *line) { if (i != string_count) { fprintf(stderr, "Error: Invalid circle_layout format: %s\n", value); // 释放之前分配的内存 - for (int j = 0; j < i; j++) { + for (int32_t j = 0; j < i; j++) { free(config->circle_layout[j]); } free(config->circle_layout); @@ -1138,10 +1442,12 @@ void parse_config_line(Config *config, const char *line) { config->new_is_master = atoi(value); } else if (strcmp(key, "default_mfact") == 0) { config->default_mfact = atof(value); - } else if (strcmp(key, "default_smfact") == 0) { - config->default_smfact = atof(value); } else if (strcmp(key, "default_nmaster") == 0) { config->default_nmaster = atoi(value); + } else if (strcmp(key, "center_master_overspread") == 0) { + config->center_master_overspread = atoi(value); + } else if (strcmp(key, "center_when_single_stack") == 0) { + config->center_when_single_stack = atoi(value); } else if (strcmp(key, "hotarea_size") == 0) { config->hotarea_size = atoi(value); } else if (strcmp(key, "enable_hotarea") == 0) { @@ -1160,8 +1466,8 @@ void parse_config_line(Config *config, const char *line) { config->focus_on_activate = atoi(value); } else if (strcmp(key, "numlockon") == 0) { config->numlockon = atoi(value); - } else if (strcmp(key, "inhibit_regardless_of_visibility") == 0) { - config->inhibit_regardless_of_visibility = atoi(value); + } else if (strcmp(key, "idleinhibit_ignore_visible") == 0) { + config->idleinhibit_ignore_visible = atoi(value); } else if (strcmp(key, "sloppyfocus") == 0) { config->sloppyfocus = atoi(value); } else if (strcmp(key, "warpcursor") == 0) { @@ -1208,6 +1514,8 @@ void parse_config_line(Config *config, const char *line) { config->send_events_mode = atoi(value); } else if (strcmp(key, "button_map") == 0) { config->button_map = atoi(value); + } else if (strcmp(key, "axis_scroll_factor") == 0) { + config->axis_scroll_factor = atof(value); } else if (strcmp(key, "gappih") == 0) { config->gappih = atoi(value); } else if (strcmp(key, "gappiv") == 0) { @@ -1223,7 +1531,7 @@ void parse_config_line(Config *config, const char *line) { } else if (strcmp(key, "borderpx") == 0) { config->borderpx = atoi(value); } else if (strcmp(key, "rootcolor") == 0) { - long int color = parse_color(value); + int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "Error: Invalid rootcolor format: %s\n", value); } else { @@ -1231,43 +1539,43 @@ void parse_config_line(Config *config, const char *line) { } } else if (strcmp(key, "shadowscolor") == 0) { - long int color = parse_color(value); + int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "Error: Invalid shadowscolor format: %s\n", value); } else { convert_hex_to_rgba(config->shadowscolor, color); } } else if (strcmp(key, "bordercolor") == 0) { - long int color = parse_color(value); + int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "Error: Invalid bordercolor format: %s\n", value); } else { convert_hex_to_rgba(config->bordercolor, color); } } else if (strcmp(key, "focuscolor") == 0) { - long int color = parse_color(value); + int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "Error: Invalid focuscolor format: %s\n", value); } else { convert_hex_to_rgba(config->focuscolor, color); } - } else if (strcmp(key, "maxmizescreencolor") == 0) { - long int color = parse_color(value); + } else if (strcmp(key, "maximizescreencolor") == 0) { + int64_t color = parse_color(value); if (color == -1) { - fprintf(stderr, "Error: Invalid maxmizescreencolor format: %s\n", + fprintf(stderr, "Error: Invalid maximizescreencolor format: %s\n", value); } else { - convert_hex_to_rgba(config->maxmizescreencolor, color); + convert_hex_to_rgba(config->maximizescreencolor, color); } } else if (strcmp(key, "urgentcolor") == 0) { - long int color = parse_color(value); + int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "Error: Invalid urgentcolor format: %s\n", value); } else { convert_hex_to_rgba(config->urgentcolor, color); } } else if (strcmp(key, "scratchpadcolor") == 0) { - long int color = parse_color(value); + int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "Error: Invalid scratchpadcolor format: %s\n", value); @@ -1275,27 +1583,19 @@ void parse_config_line(Config *config, const char *line) { convert_hex_to_rgba(config->scratchpadcolor, color); } } else if (strcmp(key, "globalcolor") == 0) { - long int color = parse_color(value); + int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "Error: Invalid globalcolor format: %s\n", value); } else { convert_hex_to_rgba(config->globalcolor, color); } } else if (strcmp(key, "overlaycolor") == 0) { - long int color = parse_color(value); + int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "Error: Invalid overlaycolor format: %s\n", value); } else { convert_hex_to_rgba(config->overlaycolor, color); } - } else if (strcmp(key, "autostart") == 0) { - if (sscanf(value, "%[^,],%[^,],%[^,]", config->autostart[0], - config->autostart[1], config->autostart[2]) != 3) { - fprintf(stderr, "Error: Invalid autostart format: %s\n", value); - } - trim_whitespace(config->autostart[0]); - trim_whitespace(config->autostart[1]); - trim_whitespace(config->autostart[2]); } else if (strcmp(key, "tagrule") == 0) { config->tag_rules = realloc(config->tag_rules, @@ -1325,13 +1625,15 @@ void parse_config_line(Config *config, const char *line) { trim_whitespace(val); if (strcmp(key, "id") == 0) { - rule->id = CLAMP_INT(atoi(val), 1, LENGTH(tags)); + rule->id = CLAMP_INT(atoi(val), 0, LENGTH(tags)); } else if (strcmp(key, "layout_name") == 0) { rule->layout_name = strdup(val); } else if (strcmp(key, "monitor_name") == 0) { rule->monitor_name = strdup(val); } else if (strcmp(key, "no_render_border") == 0) { rule->no_render_border = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "no_hide") == 0) { + rule->no_hide = CLAMP_INT(atoi(val), 0, 1); } } token = strtok(NULL, ","); @@ -1406,39 +1708,54 @@ void parse_config_line(Config *config, const char *line) { ConfigWinRule *rule = &config->window_rules[config->window_rules_count]; memset(rule, 0, sizeof(ConfigWinRule)); + // int32_t rule value, relay to a client property rule->isfloating = -1; rule->isfullscreen = -1; rule->isnoborder = -1; + rule->isnoshadow = -1; + rule->isnoradius = -1; + rule->isnoanimation = -1; rule->isopensilent = -1; rule->istagsilent = -1; rule->isnamedscratchpad = -1; rule->isunglobal = -1; rule->isglobal = -1; rule->isoverlay = -1; + rule->allow_shortcuts_inhibit = -1; rule->ignore_maximize = -1; rule->ignore_minimize = -1; rule->isnosizehint = -1; rule->isterm = -1; + rule->allow_csd = -1; + rule->force_maximize = -1; + rule->force_tearing = -1; rule->noswallow = -1; rule->noblur = -1; - rule->monitor = NULL; - rule->offsetx = 0; - rule->offsety = 0; + rule->nofocus = -1; rule->nofadein = -1; rule->nofadeout = -1; rule->no_force_center = -1; - rule->scratchpad_width = 0; - rule->scratchpad_height = 0; - rule->focused_opacity = 0; - rule->unfocused_opacity = 0; - rule->width = 0; - rule->height = 0; + + // string rule value, relay to a client property rule->animation_type_open = NULL; rule->animation_type_close = NULL; + + // float rule value, relay to a client property + rule->focused_opacity = 0; + rule->unfocused_opacity = 0; + rule->scroller_proportion_single = 0.0f; rule->scroller_proportion = 0; + + // special rule value,not directly set to client property + rule->tags = 0; + rule->offsetx = 0; + rule->offsety = 0; + rule->width = 0; + rule->height = 0; + rule->monitor = NULL; rule->id = NULL; rule->title = NULL; - rule->tags = 0; + rule->globalkeybinding = (KeyBinding){0}; char *token = strtok(value, ","); @@ -1470,22 +1787,26 @@ void parse_config_line(Config *config, const char *line) { rule->offsetx = atoi(val); } else if (strcmp(key, "offsety") == 0) { rule->offsety = atoi(val); + } else if (strcmp(key, "nofocus") == 0) { + rule->nofocus = atoi(val); } else if (strcmp(key, "nofadein") == 0) { rule->nofadein = atoi(val); } else if (strcmp(key, "nofadeout") == 0) { rule->nofadeout = atoi(val); } else if (strcmp(key, "no_force_center") == 0) { rule->no_force_center = atoi(val); - } else if (strcmp(key, "scratchpad_width") == 0) { - rule->scratchpad_width = atoi(val); - } else if (strcmp(key, "scratchpad_height") == 0) { - rule->scratchpad_height = atoi(val); } else if (strcmp(key, "width") == 0) { rule->width = atoi(val); } else if (strcmp(key, "height") == 0) { rule->height = atoi(val); } else if (strcmp(key, "isnoborder") == 0) { rule->isnoborder = atoi(val); + } else if (strcmp(key, "isnoshadow") == 0) { + rule->isnoshadow = atoi(val); + } else if (strcmp(key, "isnoradius") == 0) { + rule->isnoradius = atoi(val); + } else if (strcmp(key, "isnoanimation") == 0) { + rule->isnoanimation = atoi(val); } else if (strcmp(key, "isopensilent") == 0) { rule->isopensilent = atoi(val); } else if (strcmp(key, "istagsilent") == 0) { @@ -1496,12 +1817,16 @@ void parse_config_line(Config *config, const char *line) { rule->isunglobal = atoi(val); } else if (strcmp(key, "isglobal") == 0) { rule->isglobal = atoi(val); + } else if (strcmp(key, "scroller_proportion_single") == 0) { + rule->scroller_proportion_single = atof(val); } else if (strcmp(key, "unfocused_opacity") == 0) { rule->unfocused_opacity = atof(val); } else if (strcmp(key, "focused_opacity") == 0) { rule->focused_opacity = atof(val); } else if (strcmp(key, "isoverlay") == 0) { rule->isoverlay = atoi(val); + } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { + rule->allow_shortcuts_inhibit = atoi(val); } else if (strcmp(key, "ignore_maximize") == 0) { rule->ignore_maximize = atoi(val); } else if (strcmp(key, "ignore_minimize") == 0) { @@ -1510,6 +1835,12 @@ void parse_config_line(Config *config, const char *line) { rule->isnosizehint = atoi(val); } else if (strcmp(key, "isterm") == 0) { rule->isterm = atoi(val); + } else if (strcmp(key, "allow_csd") == 0) { + rule->allow_csd = atoi(val); + } else if (strcmp(key, "force_maximize") == 0) { + rule->force_maximize = atoi(val); + } else if (strcmp(key, "force_tearing") == 0) { + rule->force_tearing = atoi(val); } else if (strcmp(key, "noswallow") == 0) { rule->noswallow = atoi(val); } else if (strcmp(key, "noblur") == 0) { @@ -1520,11 +1851,12 @@ void parse_config_line(Config *config, const char *line) { rule->isfullscreen = atoi(val); } else if (strcmp(key, "globalkeybinding") == 0) { char mod_str[256], keysym_str[256]; - sscanf(val, "%[^-]-%[a-zA-Z]", mod_str, keysym_str); + sscanf(val, "%255[^-]-%255[a-zA-Z]", mod_str, keysym_str); trim_whitespace(mod_str); trim_whitespace(keysym_str); rule->globalkeybinding.mod = parse_mod(mod_str); - rule->globalkeybinding.keysymcode = parse_key(keysym_str); + rule->globalkeybinding.keysymcode = + parse_key(keysym_str, false); } } token = strtok(NULL, ","); @@ -1551,7 +1883,7 @@ void parse_config_line(Config *config, const char *line) { raw_height[256], raw_refresh[256]; // 先读取所有字段为字符串 - int parsed = + int32_t parsed = sscanf(value, "%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[" "^,],%255[^,],%255[^,],%255[^,],%255[^,],%255s", @@ -1602,13 +1934,29 @@ void parse_config_line(Config *config, const char *line) { } else if (strncmp(key, "env", 3) == 0) { char env_type[256], env_value[256]; - if (sscanf(value, "%[^,],%[^\n]", env_type, env_value) < 2) { + if (sscanf(value, "%255[^,],%255[^\n]", env_type, env_value) < 2) { fprintf(stderr, "Error: Invalid bind format: %s\n", value); return; } trim_whitespace(env_type); trim_whitespace(env_value); - setenv(env_type, env_value, 1); + + ConfigEnv *env = calloc(1, sizeof(ConfigEnv)); + env->type = strdup(env_type); + env->value = strdup(env_value); + + config->env = + realloc(config->env, (config->env_count + 1) * sizeof(ConfigEnv)); + if (!config->env) { + free(env->type); + free(env->value); + free(env); + fprintf(stderr, "Error: Failed to allocate memory for env\n"); + return; + } + + config->env[config->env_count] = env; + config->env_count++; } else if (strncmp(key, "exec", 9) == 0) { char **new_exec = @@ -1645,7 +1993,7 @@ void parse_config_line(Config *config, const char *line) { config->exec_once_count++; - } else if (strncmp(key, "bind", 4) == 0) { + } else if (regex_match("^bind[s|l|r|p]*$", key)) { config->key_bindings = realloc(config->key_bindings, (config->key_bindings_count + 1) * sizeof(KeyBinding)); @@ -1659,10 +2007,12 @@ void parse_config_line(Config *config, const char *line) { memset(binding, 0, sizeof(KeyBinding)); char mod_str[256], keysym_str[256], func_name[256], - arg_value[256] = "none", arg_value2[256] = "none", - arg_value3[256] = "none", arg_value4[256] = "none", - arg_value5[256] = "none"; - if (sscanf(value, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^\n]", + arg_value[256] = "0\0", arg_value2[256] = "0\0", + arg_value3[256] = "0\0", arg_value4[256] = "0\0", + arg_value5[256] = "0\0"; + if (sscanf(value, + "%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[" + "^,],%255[^\n]", mod_str, keysym_str, func_name, arg_value, arg_value2, arg_value3, arg_value4, arg_value5) < 3) { fprintf(stderr, "Error: Invalid bind format: %s\n", value); @@ -1677,8 +2027,22 @@ void parse_config_line(Config *config, const char *line) { trim_whitespace(arg_value4); trim_whitespace(arg_value5); + strcpy(binding->mode, config->keymode); + if (strcmp(binding->mode, "common") == 0) { + binding->iscommonmode = true; + binding->isdefaultmode = false; + } else if (strcmp(binding->mode, "default") == 0) { + binding->isdefaultmode = true; + binding->iscommonmode = false; + } else { + binding->isdefaultmode = false; + binding->iscommonmode = false; + } + + parse_bind_flags(key, binding); + binding->keysymcode = + parse_key(keysym_str, binding->keysymcode.type == KEY_TYPE_SYM); binding->mod = parse_mod(mod_str); - binding->keysymcode = parse_key(keysym_str); binding->arg.v = NULL; binding->arg.v2 = NULL; binding->arg.v3 = NULL; @@ -1718,10 +2082,12 @@ void parse_config_line(Config *config, const char *line) { memset(binding, 0, sizeof(MouseBinding)); char mod_str[256], button_str[256], func_name[256], - arg_value[256] = "none", arg_value2[256] = "none", - arg_value3[256] = "none", arg_value4[256] = "none", - arg_value5[256] = "none"; - if (sscanf(value, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^\n]", + arg_value[256] = "0\0", arg_value2[256] = "0\0", + arg_value3[256] = "0\0", arg_value4[256] = "0\0", + arg_value5[256] = "0\0"; + if (sscanf(value, + "%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[" + "^,],%255[^\n]", mod_str, button_str, func_name, arg_value, arg_value2, arg_value3, arg_value4, arg_value5) < 3) { fprintf(stderr, "Error: Invalid mousebind format: %s\n", value); @@ -1777,10 +2143,12 @@ void parse_config_line(Config *config, const char *line) { memset(binding, 0, sizeof(AxisBinding)); char mod_str[256], dir_str[256], func_name[256], - arg_value[256] = "none", arg_value2[256] = "none", - arg_value3[256] = "none", arg_value4[256] = "none", - arg_value5[256] = "none"; - if (sscanf(value, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^\n]", + arg_value[256] = "0\0", arg_value2[256] = "0\0", + arg_value3[256] = "0\0", arg_value4[256] = "0\0", + arg_value5[256] = "0\0"; + if (sscanf(value, + "%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[" + "^,],%255[^\n]", mod_str, dir_str, func_name, arg_value, arg_value2, arg_value3, arg_value4, arg_value5) < 3) { fprintf(stderr, "Error: Invalid axisbind format: %s\n", value); @@ -1824,6 +2192,64 @@ void parse_config_line(Config *config, const char *line) { config->axis_bindings_count++; } + } else if (strncmp(key, "switchbind", 10) == 0) { + config->switch_bindings = realloc(config->switch_bindings, + (config->switch_bindings_count + 1) * + sizeof(SwitchBinding)); + if (!config->switch_bindings) { + fprintf(stderr, + "Error: Failed to allocate memory for switch bindings\n"); + return; + } + + SwitchBinding *binding = + &config->switch_bindings[config->switch_bindings_count]; + memset(binding, 0, sizeof(SwitchBinding)); + + char fold_str[256], func_name[256], + arg_value[256] = "0\0", arg_value2[256] = "0\0", + arg_value3[256] = "0\0", arg_value4[256] = "0\0", + arg_value5[256] = "0\0"; + if (sscanf(value, + "%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[" + "^\n]", + fold_str, func_name, arg_value, arg_value2, arg_value3, + arg_value4, arg_value5) < 3) { + fprintf(stderr, "Error: Invalid switchbind format: %s\n", value); + return; + } + trim_whitespace(fold_str); + trim_whitespace(func_name); + trim_whitespace(arg_value); + trim_whitespace(arg_value2); + trim_whitespace(arg_value3); + trim_whitespace(arg_value4); + trim_whitespace(arg_value5); + + binding->fold = parse_fold_state(fold_str); + binding->func = + parse_func_name(func_name, &binding->arg, arg_value, arg_value2, + arg_value3, arg_value4, arg_value5); + + if (!binding->func) { + if (binding->arg.v) { + free(binding->arg.v); + binding->arg.v = NULL; + } + if (binding->arg.v2) { + free(binding->arg.v2); + binding->arg.v2 = NULL; + } + if (binding->arg.v3) { + free(binding->arg.v3); + binding->arg.v3 = NULL; + } + fprintf(stderr, "Error: Unknown function in switchbind: %s\n", + func_name); + } else { + config->switch_bindings_count++; + } + } else if (strncmp(key, "gesturebind", 11) == 0) { config->gesture_bindings = realloc( config->gesture_bindings, @@ -1839,11 +2265,12 @@ void parse_config_line(Config *config, const char *line) { memset(binding, 0, sizeof(GestureBinding)); char mod_str[256], motion_str[256], fingers_count_str[256], - func_name[256], arg_value[256] = "none", arg_value2[256] = "none", - arg_value3[256] = "none", arg_value4[256] = "none", - arg_value5[256] = "none"; + func_name[256], arg_value[256] = "0\0", arg_value2[256] = "0\0", + arg_value3[256] = "0\0", arg_value4[256] = "0\0", + arg_value5[256] = "0\0"; if (sscanf(value, - "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^\n]", + "%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[^,],%255[" + "^,],%255[^,],%255[^\n]", mod_str, motion_str, fingers_count_str, func_name, arg_value, arg_value2, arg_value3, arg_value4, arg_value5) < 4) { fprintf(stderr, "Error: Invalid gesturebind format: %s\n", value); @@ -1896,37 +2323,71 @@ void parse_config_line(Config *config, const char *line) { } } +void parse_config_line(Config *config, const char *line) { + char key[256], value[256]; + if (sscanf(line, "%255[^=]=%255[^\n]", key, value) != 2) { + // fprintf(stderr, "Error: Invalid line format: %s\n", line); + return; + } + + // Then trim each part separately + trim_whitespace(key); + trim_whitespace(value); + + parse_option(config, key, value); +} + void parse_config_file(Config *config, const char *file_path) { FILE *file; - // 检查路径是否以 ~/ 开头 - if (file_path[0] == '~' && (file_path[1] == '/' || file_path[1] == '\0')) { + char full_path[1024]; + + if (file_path[0] == '.' && file_path[1] == '/') { + // Relative path + + if (cli_config_path) { + char *config_path = strdup(cli_config_path); + char *config_dir = dirname(config_path); + snprintf(full_path, sizeof(full_path), "%s/%s", config_dir, + file_path + 1); + free(config_path); + } else { + const char *home = getenv("HOME"); + if (!home) { + fprintf(stderr, "Error: HOME environment variable not set.\n"); + return; + } + snprintf(full_path, sizeof(full_path), "%s/.config/mango/%s", home, + file_path + 1); + } + file = fopen(full_path, "r"); + + } else if (file_path[0] == '~' && + (file_path[1] == '/' || file_path[1] == '\0')) { + // Home directory + const char *home = getenv("HOME"); if (!home) { fprintf(stderr, "Error: HOME environment variable not set.\n"); return; } - - // 构建完整路径(家目录 + / + 原路径去掉 ~) - char full_path[1024]; snprintf(full_path, sizeof(full_path), "%s%s", home, file_path + 1); - file = fopen(full_path, "r"); - if (!file) { - perror("Error opening file"); - return; - } + } else { + // Absolute path file = fopen(file_path, "r"); - if (!file) { - perror("Error opening file"); - return; - } + } + + if (!file) { + perror("Error opening file"); + return; } char line[512]; while (fgets(line, sizeof(line), file)) { - if (line[0] == '#' || line[0] == '\n') + if (line[0] == '#' || line[0] == '\n') { continue; + } parse_config_line(config, line); } @@ -1936,7 +2397,7 @@ void parse_config_file(Config *config, const char *file_path) { void free_circle_layout(Config *config) { if (config->circle_layout) { // 释放每个字符串 - for (int i = 0; i < config->circle_layout_count; i++) { + for (int32_t i = 0; i < config->circle_layout_count; i++) { if (config->circle_layout[i]) { free(config->circle_layout[i]); // 释放单个字符串 config->circle_layout[i] = NULL; // 防止野指针 @@ -1966,15 +2427,27 @@ void free_baked_points(void) { free(baked_points_tag); baked_points_tag = NULL; } + if (baked_points_focus) { + free(baked_points_focus); + baked_points_focus = NULL; + } + if (baked_points_opafadein) { + free(baked_points_opafadein); + baked_points_opafadein = NULL; + } + if (baked_points_opafadeout) { + free(baked_points_opafadeout); + baked_points_opafadeout = NULL; + } } void free_config(void) { // 释放内存 - int i; + int32_t i; // 释放 window_rules if (config.window_rules) { - for (int i = 0; i < config.window_rules_count; i++) { + for (int32_t i = 0; i < config.window_rules_count; i++) { ConfigWinRule *rule = &config.window_rules[i]; if (rule->id) free((void *)rule->id); @@ -2003,7 +2476,7 @@ void free_config(void) { // 释放 monitor_rules if (config.monitor_rules) { - for (int i = 0; i < config.monitor_rules_count; i++) { + for (int32_t i = 0; i < config.monitor_rules_count; i++) { ConfigMonitorRule *rule = &config.monitor_rules[i]; free((void *)rule->name); free((void *)rule->layout); @@ -2076,6 +2549,27 @@ void free_config(void) { config.axis_bindings_count = 0; } + // 释放 switch_bindings + if (config.switch_bindings) { + for (i = 0; i < config.switch_bindings_count; i++) { + if (config.switch_bindings[i].arg.v) { + free((void *)config.switch_bindings[i].arg.v); + config.switch_bindings[i].arg.v = NULL; + } + if (config.switch_bindings[i].arg.v2) { + free((void *)config.switch_bindings[i].arg.v2); + config.switch_bindings[i].arg.v2 = NULL; + } + if (config.switch_bindings[i].arg.v3) { + free((void *)config.switch_bindings[i].arg.v3); + config.switch_bindings[i].arg.v3 = NULL; + } + } + free(config.switch_bindings); + config.switch_bindings = NULL; + config.switch_bindings_count = 0; + } + // 释放 gesture_bindings if (config.gesture_bindings) { for (i = 0; i < config.gesture_bindings_count; i++) { @@ -2099,7 +2593,7 @@ void free_config(void) { // 释放 tag_rules if (config.tag_rules) { - for (int i = 0; i < config.tag_rules_count; i++) { + for (int32_t i = 0; i < config.tag_rules_count; i++) { if (config.tag_rules[i].layout_name) free((void *)config.tag_rules[i].layout_name); if (config.tag_rules[i].monitor_name) @@ -2112,7 +2606,7 @@ void free_config(void) { // 释放 layer_rules if (config.layer_rules) { - for (int i = 0; i < config.layer_rules_count; i++) { + for (int32_t i = 0; i < config.layer_rules_count; i++) { if (config.layer_rules[i].layer_name) free((void *)config.layer_rules[i].layer_name); if (config.layer_rules[i].animation_type_open) @@ -2125,6 +2619,22 @@ void free_config(void) { config.layer_rules_count = 0; } + // 释放 env + if (config.env) { + for (int32_t i = 0; i < config.env_count; i++) { + if (config.env[i]->type) { + free((void *)config.env[i]->type); + } + if (config.env[i]->value) { + free((void *)config.env[i]->value); + } + free(config.env[i]); + } + free(config.env); + config.env = NULL; + config.env_count = 0; + } + // 释放 exec if (config.exec) { for (i = 0; i < config.exec_count; i++) { @@ -2162,6 +2672,9 @@ void free_config(void) { // 释放动画资源 free_baked_points(); + + // 清理解析按键用的keymap + cleanup_config_keymap(); } void override_config(void) { @@ -2197,20 +2710,27 @@ void override_config(void) { animation_duration_tag = CLAMP_INT(config.animation_duration_tag, 1, 50000); animation_duration_close = CLAMP_INT(config.animation_duration_close, 1, 50000); + animation_duration_focus = + CLAMP_INT(config.animation_duration_focus, 1, 50000); // 滚动布局设置 scroller_default_proportion = CLAMP_FLOAT(config.scroller_default_proportion, 0.1f, 1.0f); scroller_default_proportion_single = CLAMP_FLOAT(config.scroller_default_proportion_single, 0.1f, 1.0f); + scroller_ignore_proportion_single = + CLAMP_INT(config.scroller_ignore_proportion_single, 0, 1); scroller_focus_center = CLAMP_INT(config.scroller_focus_center, 0, 1); scroller_prefer_center = CLAMP_INT(config.scroller_prefer_center, 0, 1); + edge_scroller_pointer_focus = + CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1); scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000); // 主从布局设置 default_mfact = CLAMP_FLOAT(config.default_mfact, 0.1f, 0.9f); - default_smfact = CLAMP_FLOAT(config.default_smfact, 0.1f, 0.9f); default_nmaster = CLAMP_INT(config.default_nmaster, 1, 1000); + center_master_overspread = CLAMP_INT(config.center_master_overspread, 0, 1); + center_when_single_stack = CLAMP_INT(config.center_when_single_stack, 0, 1); new_is_master = CLAMP_INT(config.new_is_master, 0, 1); // 概述模式设置 @@ -2223,15 +2743,22 @@ void override_config(void) { // 杂项设置 xwayland_persistence = CLAMP_INT(config.xwayland_persistence, 0, 1); syncobj_enable = CLAMP_INT(config.syncobj_enable, 0, 1); + adaptive_sync = CLAMP_INT(config.adaptive_sync, 0, 1); + allow_tearing = CLAMP_INT(config.allow_tearing, 0, 2); + allow_shortcuts_inhibit = CLAMP_INT(config.allow_shortcuts_inhibit, 0, 1); + allow_lock_transparent = CLAMP_INT(config.allow_lock_transparent, 0, 1); axis_bind_apply_timeout = CLAMP_INT(config.axis_bind_apply_timeout, 0, 1000); focus_on_activate = CLAMP_INT(config.focus_on_activate, 0, 1); - inhibit_regardless_of_visibility = - CLAMP_INT(config.inhibit_regardless_of_visibility, 0, 1); + idleinhibit_ignore_visible = + CLAMP_INT(config.idleinhibit_ignore_visible, 0, 1); sloppyfocus = CLAMP_INT(config.sloppyfocus, 0, 1); warpcursor = CLAMP_INT(config.warpcursor, 0, 1); focus_cross_monitor = CLAMP_INT(config.focus_cross_monitor, 0, 1); + exchange_cross_monitor = CLAMP_INT(config.exchange_cross_monitor, 0, 1); + scratchpad_cross_monitor = CLAMP_INT(config.scratchpad_cross_monitor, 0, 1); focus_cross_tag = CLAMP_INT(config.focus_cross_tag, 0, 1); + view_current_to_back = CLAMP_INT(config.view_current_to_back, 0, 1); enable_floating_snap = CLAMP_INT(config.enable_floating_snap, 0, 1); snap_distance = CLAMP_INT(config.snap_distance, 0, 99999); cursor_size = CLAMP_INT(config.cursor_size, 4, 512); @@ -2268,6 +2795,7 @@ void override_config(void) { click_method = CLAMP_INT(config.click_method, 0, 2); send_events_mode = CLAMP_INT(config.send_events_mode, 0, 2); button_map = CLAMP_INT(config.button_map, 0, 1); + axis_scroll_factor = CLAMP_FLOAT(config.axis_scroll_factor, 0.1f, 10.0f); // 外观设置 gappih = CLAMP_INT(config.gappih, 0, 1000); @@ -2306,8 +2834,8 @@ void override_config(void) { memcpy(rootcolor, config.rootcolor, sizeof(rootcolor)); memcpy(bordercolor, config.bordercolor, sizeof(bordercolor)); memcpy(focuscolor, config.focuscolor, sizeof(focuscolor)); - memcpy(maxmizescreencolor, config.maxmizescreencolor, - sizeof(maxmizescreencolor)); + memcpy(maximizescreencolor, config.maximizescreencolor, + sizeof(maximizescreencolor)); memcpy(urgentcolor, config.urgentcolor, sizeof(urgentcolor)); memcpy(scratchpadcolor, config.scratchpadcolor, sizeof(scratchpadcolor)); memcpy(globalcolor, config.globalcolor, sizeof(globalcolor)); @@ -2322,6 +2850,12 @@ void override_config(void) { sizeof(animation_curve_tag)); memcpy(animation_curve_close, config.animation_curve_close, sizeof(animation_curve_close)); + memcpy(animation_curve_focus, config.animation_curve_focus, + sizeof(animation_curve_focus)); + memcpy(animation_curve_opafadein, config.animation_curve_opafadein, + sizeof(animation_curve_opafadein)); + memcpy(animation_curve_opafadeout, config.animation_curve_opafadeout, + sizeof(animation_curve_opafadeout)); } void set_value_default() { @@ -2344,16 +2878,21 @@ void set_value_default() { animation_duration_tag; // Animation tag speed config.animation_duration_close = animation_duration_close; // Animation tag speed + config.animation_duration_focus = + animation_duration_focus; // Animation focus opacity speed /* appearance */ config.axis_bind_apply_timeout = axis_bind_apply_timeout; // 滚轮绑定动作的触发的时间间隔 config.focus_on_activate = - focus_on_activate; // 收到窗口激活请求是否自动跳转聚焦 - config.new_is_master = new_is_master; // 新窗口是否插在头部 - config.default_mfact = default_mfact; // master 窗口比例 - config.default_smfact = default_smfact; // 第一个stack比例 + focus_on_activate; // 收到窗口激活请求是否自动跳转聚焦 + config.new_is_master = new_is_master; // 新窗口是否插在头部 + config.default_mfact = default_mfact; // master 窗口比例 config.default_nmaster = default_nmaster; // 默认master数量 + config.center_master_overspread = + center_master_overspread; // 中心master时是否铺满 + config.center_when_single_stack = + center_when_single_stack; // 单个stack时是否居中 config.numlockon = numlockon; // 是否打开右边小键盘 @@ -2375,13 +2914,24 @@ void set_value_default() { config.scroller_default_proportion = scroller_default_proportion; config.scroller_default_proportion_single = scroller_default_proportion_single; + config.scroller_ignore_proportion_single = + scroller_ignore_proportion_single; config.scroller_focus_center = scroller_focus_center; config.scroller_prefer_center = scroller_prefer_center; + config.edge_scroller_pointer_focus = edge_scroller_pointer_focus; config.focus_cross_monitor = focus_cross_monitor; + config.exchange_cross_monitor = exchange_cross_monitor; + config.scratchpad_cross_monitor = scratchpad_cross_monitor; config.focus_cross_tag = focus_cross_tag; + config.axis_scroll_factor = axis_scroll_factor; + config.view_current_to_back = view_current_to_back; config.single_scratchpad = single_scratchpad; config.xwayland_persistence = xwayland_persistence; config.syncobj_enable = syncobj_enable; + config.adaptive_sync = adaptive_sync; + config.allow_tearing = allow_tearing; + config.allow_shortcuts_inhibit = allow_shortcuts_inhibit; + config.allow_lock_transparent = allow_lock_transparent; config.no_border_when_single = no_border_when_single; config.no_radius_when_single = no_radius_when_single; config.snap_distance = snap_distance; @@ -2389,10 +2939,10 @@ void set_value_default() { config.enable_floating_snap = enable_floating_snap; config.swipe_min_threshold = swipe_min_threshold; - config.inhibit_regardless_of_visibility = - inhibit_regardless_of_visibility; /* 1 means idle inhibitors will - disable idle tracking even if it's surface - isn't visible + config.idleinhibit_ignore_visible = + idleinhibit_ignore_visible; /* 1 means idle inhibitors will + disable idle tracking even if it's + surface isn't visible */ config.borderpx = borderpx; @@ -2453,12 +3003,18 @@ void set_value_default() { sizeof(animation_curve_tag)); memcpy(config.animation_curve_close, animation_curve_close, sizeof(animation_curve_close)); + memcpy(config.animation_curve_focus, animation_curve_focus, + sizeof(animation_curve_focus)); + memcpy(config.animation_curve_opafadein, animation_curve_opafadein, + sizeof(animation_curve_opafadein)); + memcpy(config.animation_curve_opafadeout, animation_curve_opafadeout, + sizeof(animation_curve_opafadeout)); memcpy(config.rootcolor, rootcolor, sizeof(rootcolor)); memcpy(config.bordercolor, bordercolor, sizeof(bordercolor)); memcpy(config.focuscolor, focuscolor, sizeof(focuscolor)); - memcpy(config.maxmizescreencolor, maxmizescreencolor, - sizeof(maxmizescreencolor)); + memcpy(config.maximizescreencolor, maximizescreencolor, + sizeof(maximizescreencolor)); memcpy(config.urgentcolor, urgentcolor, sizeof(urgentcolor)); memcpy(config.scratchpadcolor, scratchpadcolor, sizeof(scratchpadcolor)); memcpy(config.globalcolor, globalcolor, sizeof(globalcolor)); @@ -2483,6 +3039,9 @@ void set_default_key_bindings(Config *config) { for (size_t i = 0; i < default_key_bindings_count; i++) { config->key_bindings[config->key_bindings_count + i] = default_key_bindings[i]; + config->key_bindings[config->key_bindings_count + i].iscommonmode = + true; + config->key_bindings[config->key_bindings_count + i].islockapply = true; } // 更新按键绑定的总数 @@ -2497,6 +3056,11 @@ void parse_config(void) { // 重置config结构体,确保所有指针初始化为NULL memset(&config, 0, sizeof(config)); + memset(&xkb_rules_rules, 0, sizeof(xkb_rules_rules)); + memset(&xkb_rules_model, 0, sizeof(xkb_rules_model)); + memset(&xkb_rules_layout, 0, sizeof(xkb_rules_layout)); + memset(&xkb_rules_variant, 0, sizeof(xkb_rules_variant)); + memset(&xkb_rules_options, 0, sizeof(xkb_rules_options)); // 初始化动态数组的指针为NULL,避免野指针 config.window_rules = NULL; @@ -2509,8 +3073,12 @@ void parse_config(void) { config.mouse_bindings_count = 0; config.axis_bindings = NULL; config.axis_bindings_count = 0; + config.switch_bindings = NULL; + config.switch_bindings_count = 0; config.gesture_bindings = NULL; config.gesture_bindings_count = 0; + config.env = NULL; + config.env_count = 0; config.exec = NULL; config.exec_count = 0; config.exec_once = NULL; @@ -2522,12 +3090,13 @@ void parse_config(void) { config.tag_rules = NULL; config.tag_rules_count = 0; config.cursor_theme = NULL; + strcpy(config.keymode, "default"); - // 获取 MANGOCONFIG 环境变量 - const char *mangoconfig = getenv("MANGOCONFIG"); + create_config_keymap(); - // 如果 MANGOCONFIG 环境变量不存在或为空,则使用 HOME 环境变量 - if (!mangoconfig || mangoconfig[0] == '\0') { + if (cli_config_path) { + snprintf(filename, sizeof(filename), "%s", cli_config_path); + } else { // 获取当前用户家目录 const char *homedir = getenv("HOME"); if (!homedir) { @@ -2544,9 +3113,6 @@ void parse_config(void) { snprintf(filename, sizeof(filename), "%s/mango/config.conf", SYSCONFDIR); } - } else { - // 使用 MANGOCONFIG 环境变量作为配置文件夹路径 - snprintf(filename, sizeof(filename), "%s/config.conf", mangoconfig); } set_value_default(); @@ -2557,7 +3123,7 @@ void parse_config(void) { void reset_blur_params(void) { if (blur) { - Monitor *m; + Monitor *m = NULL; wl_list_for_each(m, &mons, link) { if (m->blur != NULL) { wlr_scene_node_destroy(&m->blur->node); @@ -2571,7 +3137,7 @@ void reset_blur_params(void) { blur_params.saturation); } } else { - Monitor *m; + Monitor *m = NULL; wl_list_for_each(m, &mons, link) { if (m->blur) { @@ -2584,8 +3150,8 @@ void reset_blur_params(void) { void reapply_monitor_rules(void) { ConfigMonitorRule *mr; - Monitor *m; - int ji, jk; + Monitor *m = NULL; + int32_t ji, jk; struct wlr_output_state state; struct wlr_output_mode *internal_mode = NULL; wlr_output_state_init(&state); @@ -2616,10 +3182,14 @@ void reapply_monitor_rules(void) { } if (mr->width > 0 && mr->height > 0 && mr->refresh > 0) { - internal_mode = get_output_mode(m->wlr_output, mr->width, - mr->height, mr->refresh); + internal_mode = get_nearest_output_mode( + m->wlr_output, mr->width, mr->height, mr->refresh); if (internal_mode) { wlr_output_state_set_mode(&state, internal_mode); + } else if (wlr_output_is_headless(m->wlr_output)) { + wlr_output_state_set_custom_mode( + &state, mr->width, mr->height, + (int32_t)roundf(mr->refresh * 1000)); } } @@ -2630,19 +3200,57 @@ void reapply_monitor_rules(void) { } } + if (adaptive_sync) { + enable_adaptive_sync(m, &state); + } + wlr_output_commit_state(m->wlr_output, &state); wlr_output_state_finish(&state); updatemons(NULL, NULL); } } +void reapply_cursor_style(void) { + if (hide_source) { + wl_event_source_timer_update(hide_source, 0); + wl_event_source_remove(hide_source); + hide_source = NULL; + } + + wlr_cursor_unset_image(cursor); + + wlr_cursor_set_surface(cursor, NULL, 0, 0); + + if (cursor_mgr) { + wlr_xcursor_manager_destroy(cursor_mgr); + cursor_mgr = NULL; + } + + cursor_mgr = wlr_xcursor_manager_create(config.cursor_theme, cursor_size); + + Monitor *m = NULL; + wl_list_for_each(m, &mons, link) { + wlr_xcursor_manager_load(cursor_mgr, m->wlr_output->scale); + } + + wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr"); + + hide_source = wl_event_loop_add_timer(wl_display_get_event_loop(dpy), + hidecursor, cursor); + if (cursor_hidden) { + wlr_cursor_unset_image(cursor); + } else { + wl_event_source_timer_update(hide_source, cursor_hide_timeout * 1000); + } +} + void reapply_border(void) { - Client *c; + Client *c = NULL; // reset border width when config change wl_list_for_each(c, &clients, link) { if (c && !c->iskilling) { - if (c->bw && !c->isnoborder) { + if (!c->isnoborder && !c->isfullscreen) { c->bw = borderpx; } } @@ -2650,17 +3258,36 @@ void reapply_border(void) { } void reapply_keyboard(void) { - Keyboard *kb; - wl_list_for_each(kb, &keyboards, link) { - wlr_keyboard_set_repeat_info(kb->wlr_keyboard, repeat_rate, - repeat_delay); + InputDevice *id; + wl_list_for_each(id, &inputdevices, link) { + if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { + continue; + } + wlr_keyboard_set_repeat_info((struct wlr_keyboard *)id->device_data, + repeat_rate, repeat_delay); + } +} + +void reapply_pointer(void) { + InputDevice *id; + struct libinput_device *device; + wl_list_for_each(id, &inputdevices, link) { + + if (id->wlr_device->type != WLR_INPUT_DEVICE_POINTER) { + continue; + } + + device = id->libinput_device; + if (wlr_input_device_is_libinput(id->wlr_device) && device) { + configure_pointer(device); + } } } void reapply_master(void) { - int i; - Monitor *m; + int32_t i; + Monitor *m = NULL; for (i = 0; i <= LENGTH(tags); i++) { wl_list_for_each(m, &mons, link) { if (!m->wlr_output->enabled) { @@ -2668,7 +3295,6 @@ void reapply_master(void) { } m->pertag->nmasters[i] = default_nmaster; m->pertag->mfacts[i] = default_mfact; - m->pertag->smfacts[i] = default_smfact; m->gappih = gappih; m->gappiv = gappiv; m->gappoh = gappoh; @@ -2677,48 +3303,63 @@ void reapply_master(void) { } } -void reapply_tagrule(void) { - Monitor *m; - int i, jk; - char *rule_monitor_name = NULL; - wl_list_for_each(m, &mons, link) { - if (!m->wlr_output->enabled) { - continue; - } +void parse_tagrule(Monitor *m) { + int32_t i, jk; + ConfigTagRule tr; - // apply tag rule - for (i = 1; i <= config.tag_rules_count; i++) { - rule_monitor_name = config.tag_rules[i - 1].monitor_name; - if (regex_match(rule_monitor_name, m->wlr_output->name) || - !rule_monitor_name) { - for (jk = 0; jk < LENGTH(layouts); jk++) { - if (config.tag_rules_count > 0 && - config.tag_rules[i - 1].layout_name && - strcmp(layouts[jk].name, - config.tag_rules[i - 1].layout_name) == 0) { - m->pertag->ltidxs[config.tag_rules[i - 1].id] = - &layouts[jk]; - } + for (i = 0; i < config.tag_rules_count; i++) { + + tr = config.tag_rules[i]; + + if (config.tag_rules_count > 0 && + (!tr.monitor_name || + regex_match(tr.monitor_name, m->wlr_output->name))) { + + for (jk = 0; jk < LENGTH(layouts); jk++) { + if (tr.layout_name && + strcmp(layouts[jk].name, tr.layout_name) == 0) { + m->pertag->ltidxs[tr.id] = &layouts[jk]; } } + + m->pertag->no_hide[tr.id] = tr.no_hide; } } } -void reload_config(const Arg *arg) { - parse_config(); +void reapply_tagrule(void) { + Monitor *m = NULL; + wl_list_for_each(m, &mons, link) { + if (!m->wlr_output->enabled) { + continue; + } + parse_tagrule(m); + } +} + +void reset_option(void) { init_baked_points(); handlecursoractivity(); reset_keyboard_layout(); reset_blur_params(); + set_env(); run_exec(); + reapply_cursor_style(); reapply_border(); reapply_keyboard(); + reapply_pointer(); reapply_master(); reapply_tagrule(); reapply_monitor_rules(); - arrange(selmon, false); + arrange(selmon, false, false); +} + +int32_t reload_config(const Arg *arg) { + parse_config(); + reset_option(); + printstatus(); + return 1; } diff --git a/src/config/preset.h b/src/config/preset.h index 4d754499..6f3cd891 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -8,93 +8,107 @@ ((hex >> 8) & 0xFF) / 255.0f, (hex & 0xFF) / 255.0f} /* animaion */ -char *animation_type_open = "slide"; // 是否启用动画 //slide,zoom -char *animation_type_close = "slide"; // 是否启用动画 //slide,zoom -char *layer_animation_type_open = "slide"; // 是否启用layer动画 //slide,zoom -char *layer_animation_type_close = "slide"; // 是否启用layer动画 //slide,zoom -int animations = 1; // 是否启用动画 -int layer_animations = 0; // 是否启用layer动画 -int tag_animation_direction = HORIZONTAL; // 标签动画方向 -int animation_fade_in = 1; // Enable animation fade in -int animation_fade_out = 1; // Enable animation fade out -float zoom_initial_ratio = 0.3; // 动画起始窗口比例 -float zoom_end_ratio = 0.8; // 动画结束窗口比例 +char *animation_type_open = "slide"; // 是否启用动画 //slide,zoom +char *animation_type_close = "slide"; // 是否启用动画 //slide,zoom +char *layer_animation_type_open = "slide"; // 是否启用layer动画 //slide,zoom +char *layer_animation_type_close = "slide"; // 是否启用layer动画 //slide,zoom +int32_t animations = 1; // 是否启用动画 +int32_t layer_animations = 0; // 是否启用layer动画 +int32_t tag_animation_direction = HORIZONTAL; // 标签动画方向 +int32_t animation_fade_in = 1; // Enable animation fade in +int32_t animation_fade_out = 1; // Enable animation fade out +float zoom_initial_ratio = 0.3; // 动画起始窗口比例 +float zoom_end_ratio = 0.8; // 动画结束窗口比例 float fadein_begin_opacity = 0.5; // Begin opac window ratio for animations float fadeout_begin_opacity = 0.5; // Begin opac window ratio for animations uint32_t animation_duration_move = 500; // Animation move speed uint32_t animation_duration_open = 400; // Animation open speed uint32_t animation_duration_tag = 300; // Animation tag speed uint32_t animation_duration_close = 300; // Animation close speed -double animation_curve_move[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 -double animation_curve_open[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 -double animation_curve_tag[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 -double animation_curve_close[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +uint32_t animation_duration_focus = 0; // Animation focus opacity speed +double animation_curve_move[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +double animation_curve_open[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +double animation_curve_tag[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +double animation_curve_close[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +double animation_curve_focus[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +double animation_curve_opafadein[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +double animation_curve_opafadeout[4] = {0.5, 0.5, 0.5, 0.5}; // 动画曲线 /* appearance */ -unsigned int axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔 -unsigned int focus_on_activate = 1; // 收到窗口激活请求是否自动跳转聚焦 -unsigned int new_is_master = 1; // 新窗口是否插在头部 -double default_mfact = 0.55f; // master 窗口比例 -double default_smfact = 0.5f; // 第一个stack窗口比例 -unsigned int default_nmaster = 1; // 默认master数量 +uint32_t axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔 +uint32_t focus_on_activate = 1; // 收到窗口激活请求是否自动跳转聚焦 +uint32_t new_is_master = 1; // 新窗口是否插在头部 +double default_mfact = 0.55f; // master 窗口比例 +uint32_t default_nmaster = 1; // 默认master数量 +int32_t center_master_overspread = 0; // 中心master时是否铺满 +int32_t center_when_single_stack = 1; // 单个stack时是否居中 /* logging */ -int log_level = WLR_ERROR; -unsigned int numlockon = 1; // 是否打开右边小键盘 -unsigned int capslock = 0; // 是否启用快捷键 +int32_t log_level = WLR_ERROR; +uint32_t numlockon = 0; // 是否打开右边小键盘 +uint32_t capslock = 0; // 是否启用快捷键 -unsigned int ov_tab_mode = 0; // alt tab切换模式 -unsigned int hotarea_size = 10; // 热区大小,10x10 -unsigned int enable_hotarea = 1; // 是否启用鼠标热区 -int smartgaps = 0; /* 1 means no outer gap when there is only one window */ -int sloppyfocus = 1; /* focus follows mouse */ -unsigned int gappih = 5; /* horiz inner gap between windows */ -unsigned int gappiv = 5; /* vert inner gap between windows */ -unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ -unsigned int gappov = 10; /* vert outer gap between windows and screen edge */ +uint32_t ov_tab_mode = 0; // alt tab切换模式 +uint32_t hotarea_size = 10; // 热区大小,10x10 +uint32_t enable_hotarea = 1; // 是否启用鼠标热区 +int32_t smartgaps = 0; /* 1 means no outer gap when there is only one window */ +int32_t sloppyfocus = 1; /* focus follows mouse */ +uint32_t gappih = 5; /* horiz inner gap between windows */ +uint32_t gappiv = 5; /* vert inner gap between windows */ +uint32_t gappoh = 10; /* horiz outer gap between windows and screen edge */ +uint32_t gappov = 10; /* vert outer gap between windows and screen edge */ float scratchpad_width_ratio = 0.8; float scratchpad_height_ratio = 0.9; -int scroller_structs = 20; +int32_t scroller_structs = 20; float scroller_default_proportion = 0.9; float scroller_default_proportion_single = 1.0; -int scroller_focus_center = 0; -int scroller_prefer_center = 0; -int focus_cross_monitor = 0; -int focus_cross_tag = 0; -int no_border_when_single = 0; -int no_radius_when_single = 0; -int snap_distance = 30; -int enable_floating_snap = 0; -int drag_tile_to_tile = 0; -unsigned int cursor_size = 24; -unsigned int cursor_hide_timeout = 0; +int32_t scroller_ignore_proportion_single = 0; +int32_t scroller_focus_center = 0; +int32_t scroller_prefer_center = 0; +int32_t focus_cross_monitor = 0; +int32_t focus_cross_tag = 0; +int32_t exchange_cross_monitor = 0; +int32_t scratchpad_cross_monitor = 0; +int32_t view_current_to_back = 0; +int32_t no_border_when_single = 0; +int32_t no_radius_when_single = 0; +int32_t snap_distance = 30; +int32_t enable_floating_snap = 0; +int32_t drag_tile_to_tile = 0; +uint32_t cursor_size = 24; +uint32_t cursor_hide_timeout = 0; -unsigned int swipe_min_threshold = 1; +uint32_t swipe_min_threshold = 1; -int inhibit_regardless_of_visibility = +int32_t idleinhibit_ignore_visible = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ -unsigned int borderpx = 4; /* border pixel of windows */ +uint32_t borderpx = 4; /* border pixel of windows */ float rootcolor[] = COLOR(0x323232ff); float bordercolor[] = COLOR(0x444444ff); float focuscolor[] = COLOR(0xc66b25ff); -float maxmizescreencolor[] = COLOR(0x89aa61ff); +float maximizescreencolor[] = COLOR(0x89aa61ff); float urgentcolor[] = COLOR(0xad401fff); float scratchpadcolor[] = COLOR(0x516c93ff); float globalcolor[] = COLOR(0xb153a7ff); float overlaycolor[] = COLOR(0x14a57cff); // char *cursor_theme = "Bibata-Modern-Ice"; -int overviewgappi = 5; /* overview时 窗口与边缘 缝隙大小 */ -int overviewgappo = 30; /* overview时 窗口与窗口 缝隙大小 */ +int32_t overviewgappi = 5; /* overview时 窗口与边缘 缝隙大小 */ +int32_t overviewgappo = 30; /* overview时 窗口与窗口 缝隙大小 */ /* To conform the xdg-protocol, set the alpha to zero to restore the old * behavior */ float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; -int warpcursor = 1; /* Warp cursor to focused client */ -int xwayland_persistence = 1; /* xwayland persistence */ -int syncobj_enable = 0; +int32_t warpcursor = 1; /* Warp cursor to focused client */ +int32_t xwayland_persistence = 1; /* xwayland persistence */ +int32_t syncobj_enable = 0; +int32_t adaptive_sync = 0; +int32_t allow_lock_transparent = 0; +double drag_refresh_interval = 16.0; +int32_t allow_tearing = TEARING_DISABLED; +int32_t allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; /* keyboard */ @@ -108,6 +122,19 @@ char xkb_rules_layout[256]; char xkb_rules_variant[256]; char xkb_rules_options[256]; +/* keyboard */ +static const struct xkb_rule_names xkb_fallback_rules = { + .layout = "us", + .variant = NULL, + .model = NULL, + .rules = NULL, + .options = NULL, +}; + +static const struct xkb_rule_names xkb_default_rules = { + .options = NULL, +}; + struct xkb_rule_names xkb_rules = { /* can specify fields: rules, model, layout, variant, options */ /* example: @@ -118,20 +145,21 @@ struct xkb_rule_names xkb_rules = { .options = xkb_rules_options, }; -int repeat_rate = 25; -int repeat_delay = 600; +int32_t repeat_rate = 25; +int32_t repeat_delay = 600; /* Trackpad */ -int disable_trackpad = 0; -int tap_to_click = 1; -int tap_and_drag = 1; -int drag_lock = 1; -int mouse_natural_scrolling = 0; -int trackpad_natural_scrolling = 0; -int disable_while_typing = 1; -int left_handed = 0; -int middle_button_emulation = 0; -int single_scratchpad = 1; +int32_t disable_trackpad = 0; +int32_t tap_to_click = 1; +int32_t tap_and_drag = 1; +int32_t drag_lock = 1; +int32_t mouse_natural_scrolling = 0; +int32_t trackpad_natural_scrolling = 0; +int32_t disable_while_typing = 1; +int32_t left_handed = 0; +int32_t middle_button_emulation = 0; +int32_t single_scratchpad = 1; +int32_t edge_scroller_pointer_focus = 1; /* You can choose between: LIBINPUT_CONFIG_SCROLL_NO_SCROLL @@ -140,7 +168,7 @@ LIBINPUT_CONFIG_SCROLL_EDGE LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN */ enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; -unsigned int scroll_button = 274; +uint32_t scroll_button = 274; /* You can choose between: LIBINPUT_CONFIG_CLICK_METHOD_NONE @@ -150,6 +178,8 @@ LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; +double axis_scroll_factor = 1.0; + /* You can choose between: LIBINPUT_CONFIG_SEND_EVENTS_ENABLED LIBINPUT_CONFIG_SEND_EVENTS_DISABLED @@ -180,27 +210,27 @@ static const char *tags[] = { float focused_opacity = 1.0; float unfocused_opacity = 1.0; -int border_radius = 0; -int border_radius_location_default = CORNER_LOCATION_ALL; -int blur = 0; -int blur_layer = 0; -int blur_optimized = 1; +int32_t border_radius = 0; +int32_t border_radius_location_default = CORNER_LOCATION_ALL; +int32_t blur = 0; +int32_t blur_layer = 0; +int32_t blur_optimized = 1; struct blur_data blur_params; -int blur_params_num_passes = 1; -int blur_params_radius = 5; +int32_t blur_params_num_passes = 1; +int32_t blur_params_radius = 5; float blur_params_noise = 0.02; float blur_params_brightness = 0.9; float blur_params_contrast = 0.9; float blur_params_saturation = 1.2; -int shadows = 0; -int shadow_only_floating = 1; -int layer_shadows = 0; -unsigned int shadows_size = 10; +int32_t shadows = 0; +int32_t shadow_only_floating = 1; +int32_t layer_shadows = 0; +uint32_t shadows_size = 10; double shadows_blur = 15; -int shadows_position_x = 0; -int shadows_position_y = 0; +int32_t shadows_position_x = 0; +int32_t shadows_position_y = 0; float shadowscolor[] = COLOR(0x000000ff); ; diff --git a/src/data/static_keymap.h b/src/data/static_keymap.h index 87db83c2..8a0c1f71 100644 --- a/src/data/static_keymap.h +++ b/src/data/static_keymap.h @@ -5,7 +5,7 @@ typedef struct { static const LayoutMapping layout_mappings[] = { {"English (US)", "us"}, - {"English (UK)", "uk"}, + {"English (UK)", "gb"}, {"Russian", "ru"}, {"German", "de"}, {"French", "fr"}, @@ -76,4 +76,4 @@ static const LayoutMapping layout_mappings[] = { {"Kannada", "kn"}, {"Malayalam", "ml"}, {NULL, NULL} // 结束标记 -}; \ No newline at end of file +}; diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index 452908b5..b197778b 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -1,61 +1,71 @@ -void minized(const Arg *arg); -void restore_minized(const Arg *arg); -void toggle_scratchpad(const Arg *arg); -void focusdir(const Arg *arg); -void toggleoverview(const Arg *arg); -void set_proportion(const Arg *arg); -void increase_proportion(const Arg *arg); -void switch_proportion_preset(const Arg *arg); -void zoom(const Arg *arg); -void tagsilent(const Arg *arg); -void tagtoleft(const Arg *arg); -void tagtoright(const Arg *arg); -void viewtoleft(const Arg *arg); -void viewtoright(const Arg *arg); -void viewtoleft_have_client(const Arg *arg); -void viewtoright_have_client(const Arg *arg); -void togglefloating(const Arg *arg); -void togglefullscreen(const Arg *arg); -void togglemaxmizescreen(const Arg *arg); -void togglegaps(const Arg *arg); -void tagmon(const Arg *arg); -void spawn(const Arg *arg); -void spawn_on_empty(const Arg *arg); -void setlayout(const Arg *arg); -void switch_layout(const Arg *arg); -void switch_keyboard_layout(const Arg *arg); -void setmfact(const Arg *arg); -void setsmfact(const Arg *arg); -void quit(const Arg *arg); -void moveresize(const Arg *arg); -void exchange_client(const Arg *arg); -void killclient(const Arg *arg); -void toggleglobal(const Arg *arg); -void incnmaster(const Arg *arg); -void focusmon(const Arg *arg); -void focusstack(const Arg *arg); -void chvt(const Arg *arg); -void reload_config(const Arg *arg); -void smartmovewin(const Arg *arg); -void smartresizewin(const Arg *arg); -void bind_to_view(const Arg *arg); -void toggletag(const Arg *arg); -void toggleview(const Arg *arg); -void tag(const Arg *arg); -void incgaps(const Arg *arg); -void incigaps(const Arg *arg); -void incihgaps(const Arg *arg); -void incivgaps(const Arg *arg); -void incogaps(const Arg *arg); -void incohgaps(const Arg *arg); -void incovgaps(const Arg *arg); -void defaultgaps(const Arg *arg); -void togglefakefullscreen(const Arg *arg); -void toggleoverlay(const Arg *arg); -void movewin(const Arg *arg); -void resizewin(const Arg *arg); -void toggle_named_scratchpad(const Arg *arg); -void toggle_render_border(const Arg *arg); -void create_virtual_output(const Arg *arg); -void destroy_all_virtual_output(const Arg *arg); -void focuslast(const Arg *arg); \ No newline at end of file +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); \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 72ab8fa5..28879df8 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1,20 +1,66 @@ -void bind_to_view(const Arg *arg) { - if ((int)arg->ui == INT_MIN) { - view(&(Arg){.ui = ~0}, false); - } else { - view(arg, true); +int32_t bind_to_view(const Arg *arg) { + + uint32_t target = arg->ui; + + if (view_current_to_back && selmon->pertag->curtag && + (target & TAGMASK) == (selmon->tagset[selmon->seltags])) { + if (selmon->pertag->prevtag) + target = 1 << (selmon->pertag->prevtag - 1); + else + target = 0; } + + if (!view_current_to_back && + (target & TAGMASK) == (selmon->tagset[selmon->seltags])) { + return 0; + } + + if ((int32_t)target == INT_MIN && selmon->pertag->curtag == 0) { + if (view_current_to_back && selmon->pertag->prevtag) + target = 1 << (selmon->pertag->prevtag - 1); + else + target = 0; + } + + if (target == 0 || (int32_t)target == INT_MIN) { + view(&(Arg){.ui = ~0 & TAGMASK, .i = arg->i}, false); + } else { + view(&(Arg){.ui = target, .i = arg->i}, true); + } + return 0; } -void chvt(const Arg *arg) { wlr_session_change_vt(session, arg->ui); } -/** - * This command is intended for developer use only. - */ -void create_virtual_output(const Arg *arg) { +int32_t chvt(const Arg *arg) { + struct timespec ts; + + // prevent the animation to rquest the new frame + allow_frame_scheduling = false; + + // backup current tag and monitor name + if (selmon) { + chvt_backup_tag = selmon->pertag->curtag; + strncpy(chvt_backup_selmon, selmon->wlr_output->name, + sizeof(chvt_backup_selmon) - 1); + } + + wlr_session_change_vt(session, arg->ui); + + // wait for DRM device to stabilize and ensure the session state is inactive + ts.tv_sec = 0; + ts.tv_nsec = 100000000; // 200ms + nanosleep(&ts, NULL); + + // allow frame scheduling, + // because session state is now inactive, rendermon will not enter + allow_frame_scheduling = true; + return 1; +} + +int32_t create_virtual_output(const Arg *arg) { if (!wlr_backend_is_multi(backend)) { wlr_log(WLR_ERROR, "Expected a multi backend"); - return; + return 0; } bool done = false; @@ -22,18 +68,18 @@ void create_virtual_output(const Arg *arg) { if (!done) { wlr_log(WLR_ERROR, "Failed to create virtual output"); - return; + return 0; } wlr_log(WLR_INFO, "Virtual output created"); - return; + return 0; } -void destroy_all_virtual_output(const Arg *arg) { +int32_t destroy_all_virtual_output(const Arg *arg) { if (!wlr_backend_is_multi(backend)) { wlr_log(WLR_ERROR, "Expected a multi backend"); - return; + return 0; } Monitor *m, *tmp; @@ -45,18 +91,43 @@ void destroy_all_virtual_output(const Arg *arg) { wlr_log(WLR_INFO, "Virtual output destroyed"); } } + return 0; } -void defaultgaps(const Arg *arg) { setgaps(gappoh, gappov, gappih, gappiv); } -void exchange_client(const Arg *arg) { +int32_t defaultgaps(const Arg *arg) { + setgaps(gappoh, gappov, gappih, gappiv); + return 0; +} + +int32_t exchange_client(const Arg *arg) { Client *c = selmon->sel; - if (!c || c->isfloating || c->isfullscreen || c->ismaxmizescreen) - return; + if (!c || c->isfloating) + return 0; + + if ((c->isfullscreen || c->ismaximizescreen) && !is_scroller_layout(c->mon)) + return 0; + exchange_two_client(c, direction_select(arg)); + return 0; } -void focusdir(const Arg *arg) { - Client *c; +int32_t exchange_stack_client(const Arg *arg) { + Client *c = selmon->sel; + Client *tc = NULL; + if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen) + return 0; + if (arg->i == NEXT) { + tc = get_next_stack_client(c, false); + } else { + tc = get_next_stack_client(c, true); + } + if (tc) + exchange_two_client(c, tc); + return 0; +} + +int32_t focusdir(const Arg *arg) { + Client *c = NULL; c = direction_select(arg); if (c) { focusclient(c, 1); @@ -65,25 +136,31 @@ void focusdir(const Arg *arg) { } else { if (config.focus_cross_tag) { if (arg->i == LEFT || arg->i == UP) - viewtoleft_have_client(NULL); + viewtoleft_have_client(&(Arg){0}); if (arg->i == RIGHT || arg->i == DOWN) - viewtoright_have_client(NULL); + viewtoright_have_client(&(Arg){0}); + } else if (config.focus_cross_monitor) { + focusmon(arg); } } + return 0; } -void focuslast(const Arg *arg) { + +int32_t focuslast(const Arg *arg) { Client *c = NULL; + Client *tc = NULL; bool begin = false; - unsigned int target = 0; + uint32_t target = 0; wl_list_for_each(c, &fstack, flink) { - if (c->iskilling || c->isminied || c->isunglobal || + if (c->iskilling || c->isminimized || c->isunglobal || !client_surface(c)->mapped || client_is_unmanaged(c) || - client_should_ignore_focus(c)) + client_is_x11_popup(c)) continue; if (selmon && !selmon->sel) { + tc = c; break; } @@ -92,189 +169,191 @@ void focuslast(const Arg *arg) { continue; } - if (begin) + if (begin) { + tc = c; break; + } } - if (!c || !client_surface(c)->mapped) - return; + if (!tc || !client_surface(tc)->mapped) + return 0; - if ((int)c->tags > 0) { - focusclient(c, 1); - target = get_tags_first_tag(c->tags); + if ((int32_t)tc->tags > 0) { + focusclient(tc, 1); + target = get_tags_first_tag(tc->tags); view(&(Arg){.ui = target}, true); } + return 0; } -void focusmon(const Arg *arg) { - Client *c; +int32_t toggle_trackpad_enable(const Arg *arg) { + disable_trackpad = !disable_trackpad; + return 0; +} + +int32_t focusmon(const Arg *arg) { + Client *c = NULL; Monitor *m = NULL; + Monitor *tm = NULL; if (arg->i != UNDIR) { - m = dirtomon(arg->i); + tm = dirtomon(arg->i); } else if (arg->v) { wl_list_for_each(m, &mons, link) { if (!m->wlr_output->enabled) { continue; } if (regex_match(arg->v, m->wlr_output->name)) { + tm = m; break; } } } else { - return; + return 0; } - if (!m || !m->wlr_output->enabled) - return; + if (!tm || !tm->wlr_output->enabled || tm == selmon) + return 0; - selmon = m; - warp_cursor_to_selmon(selmon); + selmon = tm; + if (warpcursor) { + warp_cursor_to_selmon(selmon); + } c = focustop(selmon); if (!c) { selmon->sel = NULL; wlr_seat_pointer_notify_clear_focus(seat); wlr_seat_keyboard_notify_clear_focus(seat); + focusclient(NULL, 0); } else focusclient(c, 1); + + return 0; } -void // 17 -focusstack(const Arg *arg) { +int32_t focusstack(const Arg *arg) { /* Focus the next or previous client (in tiling order) on selmon */ - Client *c, *sel = focustop(selmon); - if (!sel || sel->isfullscreen) - return; - if (arg->i > 0) { - wl_list_for_each(c, &sel->link, link) { - if (&c->link == &clients || c->isunglobal) - continue; /* wrap past the sentinel node */ - if (VISIBLEON(c, selmon)) - break; /* found it */ - } + Client *sel = focustop(selmon); + Client *tc = NULL; + + if (!sel) + return 0; + if (arg->i == NEXT) { + tc = get_next_stack_client(sel, false); } else { - wl_list_for_each_reverse(c, &sel->link, link) { - if (&c->link == &clients) - continue; /* wrap past the sentinel node */ - if (VISIBLEON(c, selmon) || c->isunglobal) - break; /* found it */ - } + tc = get_next_stack_client(sel, true); } /* If only one client is visible on selmon, then c == sel */ - focusclient(c, 1); + + if (!tc) + return 0; + + focusclient(tc, 1); if (warpcursor) - warp_cursor(c); + warp_cursor(tc); + return 0; } -void incnmaster(const Arg *arg) { +int32_t incnmaster(const Arg *arg) { if (!arg || !selmon) - return; + return 0; selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->pertag->nmasters[selmon->pertag->curtag] + arg->i, 0); - arrange(selmon, false); + arrange(selmon, false, false); + return 0; } -void incgaps(const Arg *arg) { +int32_t incgaps(const Arg *arg) { setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, selmon->gappih + arg->i, selmon->gappiv + arg->i); + return 0; } -void incigaps(const Arg *arg) { +int32_t incigaps(const Arg *arg) { setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i, selmon->gappiv + arg->i); + return 0; } -void incogaps(const Arg *arg) { +int32_t incogaps(const Arg *arg) { setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, selmon->gappih, selmon->gappiv); + return 0; } -void incihgaps(const Arg *arg) { +int32_t incihgaps(const Arg *arg) { setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i, selmon->gappiv); + return 0; } -void incivgaps(const Arg *arg) { +int32_t incivgaps(const Arg *arg) { setgaps(selmon->gappoh, selmon->gappov, selmon->gappih, selmon->gappiv + arg->i); + return 0; } -void incohgaps(const Arg *arg) { +int32_t incohgaps(const Arg *arg) { setgaps(selmon->gappoh + arg->i, selmon->gappov, selmon->gappih, selmon->gappiv); + return 0; } -void incovgaps(const Arg *arg) { +int32_t incovgaps(const Arg *arg) { setgaps(selmon->gappoh, selmon->gappov + arg->i, selmon->gappih, selmon->gappiv); + return 0; } -void increase_proportion(const Arg *arg) { - if (selmon->sel) { - unsigned int max_client_width = - selmon->w.width - 2 * scroller_structs - gappih; - selmon->sel->scroller_proportion = - MIN(MAX(arg->f + selmon->sel->scroller_proportion, 0.1), 1.0); - selmon->sel->geom.width = max_client_width * arg->f; - arrange(selmon, false); - } -} - -void setsmfact(const Arg *arg) { +int32_t setmfact(const Arg *arg) { float f; + Client *c = NULL; if (!arg || !selmon || !selmon->pertag->ltidxs[selmon->pertag->curtag]->arrange) - return; - f = arg->f < 1.0 ? arg->f + selmon->pertag->smfacts[selmon->pertag->curtag] - : arg->f - 1.0; - if (f < 0.1 || f > 0.9) - return; - // selmon->mfact = f; - selmon->pertag->smfacts[selmon->pertag->curtag] = f; - arrange(selmon, false); -} -/* arg > 1.0 will set mfact absolutely */ -void // 17 -setmfact(const Arg *arg) { - float f; - - if (!arg || !selmon || - !selmon->pertag->ltidxs[selmon->pertag->curtag]->arrange) - return; + return 0; f = arg->f < 1.0 ? arg->f + selmon->pertag->mfacts[selmon->pertag->curtag] : arg->f - 1.0; if (f < 0.1 || f > 0.9) - return; - // selmon->mfact = f; + return 0; + selmon->pertag->mfacts[selmon->pertag->curtag] = f; - arrange(selmon, false); + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, selmon) && ISTILED(c)) { + c->master_mfact_per = f; + } + } + arrange(selmon, false, false); + return 0; } -void killclient(const Arg *arg) { - Client *c; +int32_t killclient(const Arg *arg) { + Client *c = NULL; c = selmon->sel; if (c) { pending_kill_client(c); } + return 0; } -void // 17 -moveresize(const Arg *arg) { +int32_t moveresize(const Arg *arg) { if (cursor_mode != CurNormal && cursor_mode != CurPressed) - return; + return 0; xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); - if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) - return; - + if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen || + grabc->ismaximizescreen) { + grabc = NULL; + return 0; + } /* Float the window and tell motionnotify to grab it */ - if (grabc->isfloating == 0) { + if (grabc->isfloating == 0 && arg->ui == CurMove) { grabc->drag_to_tile = true; setfloating(grabc, 1); } switch (cursor_mode = arg->ui) { case CurMove: + grabcx = cursor->x - grabc->geom.x; grabcy = cursor->y - grabc->geom.y; wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab"); @@ -282,18 +361,24 @@ moveresize(const Arg *arg) { case CurResize: /* Doesn't work for X11 output - the next absolute motion event * returns the cursor to where it started */ - wlr_cursor_warp_closest(cursor, NULL, grabc->geom.x + grabc->geom.width, - grabc->geom.y + grabc->geom.height); - wlr_cursor_set_xcursor(cursor, cursor_mgr, "bottom_right_corner"); + if (grabc->isfloating) { + wlr_cursor_warp_closest(cursor, NULL, + grabc->geom.x + grabc->geom.width, + grabc->geom.y + grabc->geom.height); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "bottom_right_corner"); + } else { + wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab"); + } break; } + return 0; } -void movewin(const Arg *arg) { - Client *c; +int32_t movewin(const Arg *arg) { + Client *c = NULL; c = selmon->sel; if (!c || c->isfullscreen) - return; + return 0; if (!c->isfloating) togglefloating(NULL); @@ -321,22 +406,52 @@ void movewin(const Arg *arg) { break; } - c->oldgeom = c->geom; + c->iscustomsize = 1; + c->float_geom = c->geom; resize(c, c->geom, 0); + return 0; } -void // 0.5 custom -quit(const Arg *arg) { +int32_t quit(const Arg *arg) { wl_display_terminate(dpy); + return 0; } -void resizewin(const Arg *arg) { - Client *c; +int32_t resizewin(const Arg *arg) { + Client *c = NULL; c = selmon->sel; - if (!c || c->isfullscreen) - return; - if (!c->isfloating) - togglefloating(NULL); + int32_t offsetx = 0, offsety = 0; + + if (!c || c->isfullscreen || c->ismaximizescreen) + return 0; + + if (ISTILED(c)) { + switch (arg->ui) { + case NUM_TYPE_MINUS: + offsetx = -arg->i; + break; + case NUM_TYPE_PLUS: + offsetx = arg->i; + break; + default: + offsetx = arg->i; + break; + } + + switch (arg->ui2) { + case NUM_TYPE_MINUS: + offsety = -arg->i2; + break; + case NUM_TYPE_PLUS: + offsety = arg->i2; + break; + default: + offsety = arg->i2; + break; + } + resize_tile_client(c, false, offsetx, offsety, 0); + return 0; + } switch (arg->ui) { case NUM_TYPE_MINUS: @@ -362,65 +477,97 @@ void resizewin(const Arg *arg) { break; } - c->oldgeom = c->geom; + c->iscustomsize = 1; + c->float_geom = c->geom; resize(c, c->geom, 0); + return 0; } -void restore_minized(const Arg *arg) { - Client *c; + +int32_t restore_minimized(const Arg *arg) { + Client *c = NULL; + + if (selmon && selmon->isoverview) + return 0; + if (selmon && selmon->sel && selmon->sel->is_in_scratchpad && selmon->sel->is_scratchpad_show) { - selmon->sel->isminied = 0; + selmon->sel->isminimized = 0; selmon->sel->is_scratchpad_show = 0; selmon->sel->is_in_scratchpad = 0; selmon->sel->isnamedscratchpad = 0; setborder_color(selmon->sel); - return; + return 0; } wl_list_for_each(c, &clients, link) { - if (c->isminied) { - show_hide_client(c); + if (c->isminimized && !c->isnamedscratchpad) { c->is_scratchpad_show = 0; c->is_in_scratchpad = 0; c->isnamedscratchpad = 0; + show_hide_client(c); setborder_color(c); - break; + arrange(c->mon, false, false); + focusclient(c, 0); + warp_cursor(c); + return 0; } } + return 0; } -void // 17 -setlayout(const Arg *arg) { - int jk; +int32_t setlayout(const Arg *arg) { + int32_t jk; + for (jk = 0; jk < LENGTH(layouts); jk++) { if (strcmp(layouts[jk].name, arg->v) == 0) { selmon->pertag->ltidxs[selmon->pertag->curtag] = &layouts[jk]; - - arrange(selmon, false); + clear_fullscreen_and_maximized_state(selmon); + arrange(selmon, false, false); printstatus(); - return; + return 0; } } + return 0; } -void set_proportion(const Arg *arg) { +int32_t setkeymode(const Arg *arg) { + snprintf(keymode.mode, sizeof(keymode.mode), "%.27s", arg->v); + if (strcmp(keymode.mode, "default") == 0) { + keymode.isdefault = true; + } else { + keymode.isdefault = false; + } + printstatus(); + return 1; +} + +int32_t set_proportion(const Arg *arg) { + + if (selmon->isoverview || !is_scroller_layout(selmon)) + return 0; + + if (selmon->visible_tiling_clients == 1 && + !scroller_ignore_proportion_single) + return 0; + if (selmon->sel) { - unsigned int max_client_width = + uint32_t max_client_width = selmon->w.width - 2 * scroller_structs - gappih; selmon->sel->scroller_proportion = arg->f; selmon->sel->geom.width = max_client_width * arg->f; // resize(selmon->sel, selmon->sel->geom, 0); - arrange(selmon, false); + arrange(selmon, false, false); } + return 0; } -void smartmovewin(const Arg *arg) { - Client *c, *tc; - int nx, ny; - int buttom, top, left, right, tar; +int32_t smartmovewin(const Arg *arg) { + Client *c = NULL, *tc = NULL; + int32_t nx, ny; + int32_t buttom, top, left, right, tar; c = selmon->sel; if (!c || c->isfullscreen) - return; + return 0; if (!c->isfloating) setfloating(selmon->sel, true); nx = c->geom.x; @@ -508,19 +655,20 @@ void smartmovewin(const Arg *arg) { break; } - c->oldgeom = (struct wlr_box){ + c->float_geom = (struct wlr_box){ .x = nx, .y = ny, .width = c->geom.width, .height = c->geom.height}; - - resize(c, c->oldgeom, 1); + c->iscustomsize = 1; + resize(c, c->float_geom, 1); + return 0; } -void smartresizewin(const Arg *arg) { - Client *c, *tc; - int nw, nh; - int buttom, top, left, right, tar; +int32_t smartresizewin(const Arg *arg) { + Client *c = NULL, *tc = NULL; + int32_t nw, nh; + int32_t buttom, top, left, right, tar; c = selmon->sel; if (!c || c->isfullscreen) - return; + return 0; if (!c->isfloating) setfloating(c, true); nw = c->geom.width; @@ -577,16 +725,71 @@ void smartresizewin(const Arg *arg) { break; } - c->oldgeom = (struct wlr_box){ + c->float_geom = (struct wlr_box){ .x = c->geom.x, .y = c->geom.y, .width = nw, .height = nh}; - - resize(c, c->oldgeom, 1); + c->iscustomsize = 1; + resize(c, c->float_geom, 1); + return 0; } -void spawn(const Arg *arg) { +int32_t centerwin(const Arg *arg) { + Client *c = NULL; + c = selmon->sel; + + if (!c || c->isfullscreen || c->ismaximizescreen) + return 0; + + if (c->isfloating) { + c->float_geom = setclient_coordinate_center(c, c->geom, 0, 0); + c->iscustomsize = 1; + resize(c, c->float_geom, 1); + return 0; + } + + if (!is_scroller_layout(selmon)) + return 0; + + if (selmon->pertag->ltidxs[selmon->pertag->curtag]->id == SCROLLER) { + c->geom.x = selmon->w.x + (selmon->w.width - c->geom.width) / 2; + } else { + c->geom.y = selmon->w.y + (selmon->w.height - c->geom.height) / 2; + } + + arrange(selmon, false, false); + return 0; +} + +int32_t spawn_shell(const Arg *arg) { + if (!arg->v) + return 0; + + if (fork() == 0) { + // 1. 忽略可能导致 coredump 的信号 + signal(SIGSEGV, SIG_IGN); + signal(SIGABRT, SIG_IGN); + signal(SIGILL, SIG_IGN); + + dup2(STDERR_FILENO, STDOUT_FILENO); + setsid(); + + execlp("sh", "sh", "-c", arg->v, (char *)NULL); + + // fallback to bash + execlp("bash", "bash", "-c", arg->v, (char *)NULL); + + // if execlp fails, we should not reach here + wlr_log(WLR_ERROR, + "mango: failed to execute command '%s' with shell: %s\n", + arg->v, strerror(errno)); + _exit(EXIT_FAILURE); + } + return 0; +} + +int32_t spawn(const Arg *arg) { if (!arg->v) - return; + return 0; if (fork() == 0) { // 1. 忽略可能导致 coredump 的信号 @@ -599,7 +802,7 @@ void spawn(const Arg *arg) { // 2. 解析参数 char *argv[64]; - int argc = 0; + int32_t argc = 0; char *token = strtok((char *)arg->v, " "); while (token != NULL && argc < 63) { wordexp_t p; @@ -616,15 +819,16 @@ void spawn(const Arg *arg) { execvp(argv[0], argv); // 4. execvp 失败时:打印错误并直接退出(避免 coredump) - wlr_log(WLR_ERROR, "dwl: execvp '%s' failed: %s\n", argv[0], + wlr_log(WLR_ERROR, "mango: execvp '%s' failed: %s\n", argv[0], strerror(errno)); _exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作 } + return 0; } -void spawn_on_empty(const Arg *arg) { +int32_t spawn_on_empty(const Arg *arg) { bool is_empty = true; - Client *c; + Client *c = NULL; wl_list_for_each(c, &clients, link) { if (arg->ui & c->tags && c->mon == selmon) { @@ -634,129 +838,75 @@ void spawn_on_empty(const Arg *arg) { } if (!is_empty) { view(arg, true); - return; + return 0; } else { view(arg, true); - spawn(arg); + spawn_shell(arg); } + return 0; } -void switch_keyboard_layout(const Arg *arg) { +int32_t switch_keyboard_layout(const Arg *arg) { if (!kb_group || !kb_group->wlr_group || !seat) { wlr_log(WLR_ERROR, "Invalid keyboard group or seat"); - return; + return 0; } struct wlr_keyboard *keyboard = &kb_group->wlr_group->keyboard; if (!keyboard || !keyboard->keymap) { wlr_log(WLR_ERROR, "Invalid keyboard or keymap"); - return; + return 0; } // 1. 获取当前布局和计算下一个布局 xkb_layout_index_t current = xkb_state_serialize_layout( keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); - const int num_layouts = xkb_keymap_num_layouts(keyboard->keymap); + const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap); if (num_layouts < 2) { wlr_log(WLR_INFO, "Only one layout available"); - return; + return 0; } xkb_layout_index_t next = (current + 1) % num_layouts; - // 2. 创建上下文 - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (!context) { - wlr_log(WLR_ERROR, "Failed to create XKB context"); - return; - } - - // 3. 分配并获取布局缩写 - char **layout_ids = calloc(num_layouts, sizeof(char *)); - if (!layout_ids) { - wlr_log(WLR_ERROR, "Failed to allocate layout IDs"); - goto cleanup_context; - } - - for (int i = 0; i < num_layouts; i++) { - layout_ids[i] = - get_layout_abbr(xkb_keymap_layout_get_name(keyboard->keymap, i)); - if (!layout_ids[i]) { - wlr_log(WLR_ERROR, "Failed to get layout abbreviation"); - goto cleanup_layouts; - } - } - - // 4. 直接修改 rules.layout(保持原有逻辑) - struct xkb_rule_names rules = xkb_rules; - char *layout_buf = (char *)rules.layout; // 假设这是可修改的 - - // 清空原有内容(安全方式) - unsigned int layout_buf_size = strlen(layout_buf) + 1; - memset(layout_buf, 0, layout_buf_size); - - // 构建新的布局字符串 - for (int i = 0; i < num_layouts; i++) { - const char *layout = layout_ids[(next + i) % num_layouts]; - - if (i > 0) { - strncat(layout_buf, ",", layout_buf_size - strlen(layout_buf) - 1); - } - - if (strchr(layout, ',')) { - // 处理包含逗号的布局名 - char *quoted = malloc(strlen(layout) + 3); - if (quoted) { - snprintf(quoted, strlen(layout) + 3, "\"%s\"", layout); - strncat(layout_buf, quoted, - layout_buf_size - strlen(layout_buf) - 1); - free(quoted); - } - } else { - strncat(layout_buf, layout, - layout_buf_size - strlen(layout_buf) - 1); - } - } - - // 5. 创建新 keymap - struct xkb_keymap *new_keymap = - xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (!new_keymap) { - wlr_log(WLR_ERROR, "Failed to create keymap for layouts: %s", - rules.layout); - goto cleanup_layouts; - } - // 6. 应用新 keymap - unsigned int depressed = keyboard->modifiers.depressed; - unsigned int latched = keyboard->modifiers.latched; - unsigned int locked = keyboard->modifiers.locked; + uint32_t depressed = keyboard->modifiers.depressed; + uint32_t latched = keyboard->modifiers.latched; + uint32_t locked = keyboard->modifiers.locked; - wlr_keyboard_set_keymap(keyboard, new_keymap); - wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, 0); + wlr_keyboard_set_keymap(keyboard, keyboard->keymap); + wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, next); keyboard->modifiers.group = 0; // 7. 更新 seat wlr_seat_set_keyboard(seat, keyboard); wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers); - // 8. 清理资源 - xkb_keymap_unref(new_keymap); + InputDevice *id; + wl_list_for_each(id, &inputdevices, link) { + if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { + continue; + } -cleanup_layouts: - for (int i = 0; i < num_layouts; i++) { - free(layout_ids[i]); + struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data; + + wlr_keyboard_set_keymap(tkb, keyboard->keymap); + wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, next); + tkb->modifiers.group = 0; + + // 7. 更新 seat + wlr_seat_set_keyboard(seat, tkb); + wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers); } - free(layout_ids); -cleanup_context: - xkb_context_unref(context); + printstatus(); + return 0; } -void switch_layout(const Arg *arg) { +int32_t switch_layout(const Arg *arg) { - int jk, ji; + int32_t jk, ji; char *target_layout_name = NULL; - unsigned int len; + uint32_t len; if (config.circle_layout_count != 0) { for (jk = 0; jk < config.circle_layout_count; jk++) { @@ -783,13 +933,14 @@ void switch_layout(const Arg *arg) { len = MAX(strlen(layouts[ji].name), strlen(target_layout_name)); if (strncmp(layouts[ji].name, target_layout_name, len) == 0) { selmon->pertag->ltidxs[selmon->pertag->curtag] = &layouts[ji]; + break; } } - - arrange(selmon, false); + clear_fullscreen_and_maximized_state(selmon); + arrange(selmon, false, false); printstatus(); - return; + return 0; } for (jk = 0; jk < LENGTH(layouts); jk++) { @@ -797,23 +948,32 @@ void switch_layout(const Arg *arg) { selmon->pertag->ltidxs[selmon->pertag->curtag]->name) == 0) { selmon->pertag->ltidxs[selmon->pertag->curtag] = jk == LENGTH(layouts) - 1 ? &layouts[0] : &layouts[jk + 1]; - arrange(selmon, false); + clear_fullscreen_and_maximized_state(selmon); + arrange(selmon, false, false); printstatus(); - return; + return 0; } } + return 0; } -void switch_proportion_preset(const Arg *arg) { +int32_t switch_proportion_preset(const Arg *arg) { float target_proportion = 0; if (config.scroller_proportion_preset_count == 0) { - return; + return 0; } + if (selmon->isoverview || !is_scroller_layout(selmon)) + return 0; + + if (selmon->visible_tiling_clients == 1 && + !scroller_ignore_proportion_single) + return 0; + if (selmon->sel) { - for (int i = 0; i < config.scroller_proportion_preset_count; i++) { + for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) { if (config.scroller_proportion_preset[i] == selmon->sel->scroller_proportion) { if (i == config.scroller_proportion_preset_count - 1) { @@ -831,46 +991,56 @@ void switch_proportion_preset(const Arg *arg) { target_proportion = config.scroller_proportion_preset[0]; } - unsigned int max_client_width = + uint32_t max_client_width = selmon->w.width - 2 * scroller_structs - gappih; selmon->sel->scroller_proportion = target_proportion; selmon->sel->geom.width = max_client_width * target_proportion; // resize(selmon->sel, selmon->sel->geom, 0); - arrange(selmon, false); + arrange(selmon, false, false); } -} -void tag(const Arg *arg) { - Client *target_client = selmon->sel; - tag_client(arg, target_client); + return 0; } -void tagmon(const Arg *arg) { - Monitor *m = NULL; +int32_t tag(const Arg *arg) { + Client *target_client = selmon->sel; + tag_client(arg, target_client); + return 0; +} + +int32_t tagmon(const Arg *arg) { + Monitor *m = NULL, *cm = NULL; Client *c = focustop(selmon); if (!c) - return; + return 0; if (arg->i != UNDIR) { m = dirtomon(arg->i); } else if (arg->v) { - wl_list_for_each(m, &mons, link) { - if (!m->wlr_output->enabled) { + wl_list_for_each(cm, &mons, link) { + if (!cm->wlr_output->enabled) { continue; } - if (regex_match(arg->v, m->wlr_output->name)) { + if (regex_match(arg->v, cm->wlr_output->name)) { + m = cm; break; } } } else { - return; + return 0; } - if (!m || !m->wlr_output->enabled || m == c->mon) - return; + if (!m || !m->wlr_output->enabled) + return 0; + + uint32_t newtags = arg->ui ? arg->ui : arg->i2 ? c->tags : 0; + uint32_t target; + + if (c->mon == m) { + view(&(Arg){.ui = newtags}, true); + return 0; + } - unsigned int newtags = arg->ui ? c->tags : 0; - unsigned int target; if (c == selmon->sel) { selmon->sel = NULL; } @@ -879,35 +1049,41 @@ void tagmon(const Arg *arg) { client_update_oldmonname_record(c, m); reset_foreign_tolevel(c); + + c->float_geom.width = + (int32_t)(c->float_geom.width * c->mon->w.width / selmon->w.width); + c->float_geom.height = + (int32_t)(c->float_geom.height * c->mon->w.height / selmon->w.height); + selmon = c->mon; + c->float_geom = setclient_coordinate_center(c, c->float_geom, 0, 0); + + // 重新计算居中的坐标 // 重新计算居中的坐标 if (c->isfloating) { - c->geom.width = - (int)(c->geom.width * c->mon->w.width / selmon->w.width); - c->geom.height = - (int)(c->geom.height * c->mon->w.height / selmon->w.height); - selmon = c->mon; - c->geom = setclient_coordinate_center(c, c->geom, 0, 0); + c->geom = c->float_geom; target = get_tags_first_tag(c->tags); view(&(Arg){.ui = target}, true); focusclient(c, 1); - c->oldgeom = c->geom; resize(c, c->geom, 1); } else { selmon = c->mon; target = get_tags_first_tag(c->tags); view(&(Arg){.ui = target}, true); focusclient(c, 1); - arrange(selmon, false); + arrange(selmon, false, false); } - warp_cursor_to_selmon(c->mon); + if (warpcursor) { + warp_cursor_to_selmon(c->mon); + } + return 0; } -void tagsilent(const Arg *arg) { - Client *fc; - Client *target_client; +int32_t tagsilent(const Arg *arg) { + Client *fc = NULL; + Client *target_client = NULL; if (!selmon || !selmon->sel) - return; + return 0; target_client = selmon->sel; target_client->tags = arg->ui & TAGMASK; @@ -918,26 +1094,29 @@ void tagsilent(const Arg *arg) { } } focusclient(focustop(selmon), 1); - arrange(target_client->mon, false); + arrange(target_client->mon, false, false); + return 0; } -void tagtoleft(const Arg *arg) { +int32_t tagtoleft(const Arg *arg) { if (selmon->sel != NULL && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 && selmon->tagset[selmon->seltags] > 1) { - tag(&(Arg){.ui = selmon->tagset[selmon->seltags] >> 1}); + tag(&(Arg){.ui = selmon->tagset[selmon->seltags] >> 1, .i = arg->i}); } + return 0; } -void tagtoright(const Arg *arg) { +int32_t tagtoright(const Arg *arg) { if (selmon->sel != NULL && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { - tag(&(Arg){.ui = selmon->tagset[selmon->seltags] << 1}); + tag(&(Arg){.ui = selmon->tagset[selmon->seltags] << 1, .i = arg->i}); } + return 0; } -void toggle_named_scratchpad(const Arg *arg) { +int32_t toggle_named_scratchpad(const Arg *arg) { Client *target_client = NULL; char *arg_id = arg->v; char *arg_title = arg->v2; @@ -946,31 +1125,37 @@ void toggle_named_scratchpad(const Arg *arg) { if (!target_client && arg->v3) { Arg arg_spawn = {.v = arg->v3}; - spawn(&arg_spawn); - return; + spawn_shell(&arg_spawn); + return 0; } target_client->isnamedscratchpad = 1; - target_client->scratchpad_width = arg->ui; - target_client->scratchpad_height = arg->ui2; apply_named_scratchpad(target_client); + return 0; } -void toggle_render_border(const Arg *arg) { + +int32_t toggle_render_border(const Arg *arg) { render_border = !render_border; - arrange(selmon, false); + arrange(selmon, false, false); + return 0; } -void toggle_scratchpad(const Arg *arg) { - Client *c; + +int32_t toggle_scratchpad(const Arg *arg) { + Client *c = NULL; bool hit = false; Client *tmp = NULL; + + if (selmon && selmon->isoverview) + return 0; + wl_list_for_each_safe(c, tmp, &clients, link) { - if (c->mon != selmon) { + if (!scratchpad_cross_monitor && c->mon != selmon) { continue; } - if (single_scratchpad && c->isnamedscratchpad && !c->isminied) { - set_minized(c); + if (single_scratchpad && c->isnamedscratchpad && !c->isminimized) { + set_minimized(c); continue; } @@ -982,32 +1167,39 @@ void toggle_scratchpad(const Arg *arg) { hit = switch_scratchpad_client_state(c); } + return 0; } -void togglefakefullscreen(const Arg *arg) { +int32_t togglefakefullscreen(const Arg *arg) { Client *sel = focustop(selmon); if (sel) setfakefullscreen(sel, !sel->isfakefullscreen); + return 0; } -void togglefloating(const Arg *arg) { + +int32_t togglefloating(const Arg *arg) { Client *sel = focustop(selmon); - if (!sel) - return; + if (selmon && selmon->isoverview) + return 0; - if ((sel->isfullscreen || sel->ismaxmizescreen)) { + if (!sel) + return 0; + + if ((sel->isfullscreen || sel->ismaximizescreen)) { sel->isfloating = 1; } else { sel->isfloating = !sel->isfloating; } setfloating(sel, sel->isfloating); + return 0; } -void togglefullscreen(const Arg *arg) { +int32_t togglefullscreen(const Arg *arg) { Client *sel = focustop(selmon); if (!sel) - return; + return 0; sel->is_scratchpad_show = 0; sel->is_in_scratchpad = 0; @@ -1017,11 +1209,12 @@ void togglefullscreen(const Arg *arg) { setfullscreen(sel, 0); else setfullscreen(sel, 1); + return 0; } -void toggleglobal(const Arg *arg) { +int32_t toggleglobal(const Arg *arg) { if (!selmon->sel) - return; + return 0; if (selmon->sel->is_in_scratchpad) { selmon->sel->is_in_scratchpad = 0; selmon->sel->is_scratchpad_show = 0; @@ -1032,32 +1225,36 @@ void toggleglobal(const Arg *arg) { // selmon->sel->isglobal ? TAGMASK : selmon->tagset[selmon->seltags]; // focustop(selmon); setborder_color(selmon->sel); + return 0; } -void togglegaps(const Arg *arg) { +int32_t togglegaps(const Arg *arg) { enablegaps ^= 1; - arrange(selmon, false); + arrange(selmon, false, false); + return 0; } -void togglemaxmizescreen(const Arg *arg) { +int32_t togglemaximizescreen(const Arg *arg) { Client *sel = focustop(selmon); if (!sel) - return; + return 0; sel->is_scratchpad_show = 0; sel->is_in_scratchpad = 0; sel->isnamedscratchpad = 0; - if (sel->ismaxmizescreen) - setmaxmizescreen(sel, 0); + if (sel->ismaximizescreen) + setmaximizescreen(sel, 0); else - setmaxmizescreen(sel, 1); + setmaximizescreen(sel, 1); setborder_color(sel); + return 0; } -void toggleoverlay(const Arg *arg) { + +int32_t toggleoverlay(const Arg *arg) { if (!selmon->sel || !selmon->sel->mon || selmon->sel->isfullscreen) { - return; + return 0; } selmon->sel->isoverlay ^= 1; @@ -1066,24 +1263,25 @@ void toggleoverlay(const Arg *arg) { wlr_scene_node_reparent(&selmon->sel->scene->node, layers[LyrOverlay]); wlr_scene_node_raise_to_top(&selmon->sel->scene->node); } else if (client_should_overtop(selmon->sel) && selmon->sel->isfloating) { - wlr_scene_node_reparent(&selmon->sel->scene->node, - layers[LyrFSorOverTop]); + wlr_scene_node_reparent(&selmon->sel->scene->node, layers[LyrTop]); } else { wlr_scene_node_reparent( &selmon->sel->scene->node, - layers[selmon->sel->isfloating ? LyrFloat : LyrTile]); + layers[selmon->sel->isfloating ? LyrTop : LyrTile]); } setborder_color(selmon->sel); + return 0; } -void toggletag(const Arg *arg) { - unsigned int newtags; + +int32_t toggletag(const Arg *arg) { + uint32_t newtags; Client *sel = focustop(selmon); if (!sel) - return; + return 0; - if ((int)arg->ui == INT_MIN && sel->tags != (~0 & TAGMASK)) { + if ((int32_t)arg->ui == INT_MIN && sel->tags != (~0 & TAGMASK)) { newtags = ~0 & TAGMASK; - } else if ((int)arg->ui == INT_MIN && sel->tags == (~0 & TAGMASK)) { + } else if ((int32_t)arg->ui == INT_MIN && sel->tags == (~0 & TAGMASK)) { newtags = 1 << (sel->mon->pertag->curtag - 1); } else { newtags = sel->tags ^ (arg->ui & TAGMASK); @@ -1092,14 +1290,15 @@ void toggletag(const Arg *arg) { if (newtags) { sel->tags = newtags; focusclient(focustop(selmon), 1); - arrange(selmon, false); + arrange(selmon, false, false); } printstatus(); + return 0; } -void toggleview(const Arg *arg) { - unsigned int newtagset; - unsigned int target; +int32_t toggleview(const Arg *arg) { + uint32_t newtagset; + uint32_t target; target = arg->ui == 0 ? ~0 & TAGMASK : arg->ui; @@ -1109,175 +1308,137 @@ void toggleview(const Arg *arg) { if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; focusclient(focustop(selmon), 1); - arrange(selmon, false); + arrange(selmon, false, false); } printstatus(); + return 0; } -void viewtoleft(const Arg *arg) { - unsigned int tmptag; - unsigned int target = selmon->tagset[selmon->seltags]; + +int32_t viewtoleft(const Arg *arg) { + uint32_t target = selmon->tagset[selmon->seltags]; if (selmon->isoverview || selmon->pertag->curtag == 0) { - return; + return 0; } target >>= 1; if (target == 0) { - return; + return 0; } if (!selmon || (target) == selmon->tagset[selmon->seltags]) - return; - selmon->seltags ^= 1; /* toggle sel tagset */ - if (target) { - selmon->tagset[selmon->seltags] = target; - selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->pertag->curtag = selmon->pertag->curtag - 1; - } else { - tmptag = selmon->pertag->prevtag; - selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->pertag->curtag = tmptag; - } + return 0; - focusclient(focustop(selmon), 1); - arrange(selmon, true); - printstatus(); + view(&(Arg){.ui = target & TAGMASK, .i = arg->i}, true); + return 0; } -void viewtoright(const Arg *arg) { + +int32_t viewtoright(const Arg *arg) { if (selmon->isoverview || selmon->pertag->curtag == 0) { - return; + return 0; } - unsigned int tmptag; - unsigned int target = selmon->tagset[selmon->seltags]; + uint32_t target = selmon->tagset[selmon->seltags]; target <<= 1; if (!selmon || (target) == selmon->tagset[selmon->seltags]) - return; + return 0; if (!(target & TAGMASK)) { - return; - } - selmon->seltags ^= 1; /* toggle sel tagset */ - if (target) { - selmon->tagset[selmon->seltags] = target; - selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->pertag->curtag = selmon->pertag->curtag + 1; - } else { - tmptag = selmon->pertag->prevtag; - selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->pertag->curtag = tmptag; + return 0; } - focusclient(focustop(selmon), 1); - arrange(selmon, true); - printstatus(); + view(&(Arg){.ui = target & TAGMASK, .i = arg->i}, true); + return 0; } -void viewtoleft_have_client(const Arg *arg) { - unsigned int tmptag; - Client *c; - unsigned int found = 0; - unsigned int n = 1; - unsigned int target = selmon->tagset[selmon->seltags]; - if (selmon->isoverview || selmon->pertag->curtag == 0) { - return; +int32_t viewtoleft_have_client(const Arg *arg) { + uint32_t n; + uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]); + bool found = false; + + if (selmon->isoverview) { + return 0; } - for (target >>= 1; target > 0 && n <= LENGTH(tags); target >>= 1, n++) { - wl_list_for_each(c, &clients, link) { - if (c->mon == selmon && target & c->tags) { - found = 1; - break; - } - } - if (found) { + if (current <= 1) + return 0; + + for (n = current - 1; n >= 1; n--) { + if (get_tag_status(n, selmon)) { + found = true; break; } } - if (target == 0) { - return; - } - - if (!selmon || (target) == selmon->tagset[selmon->seltags]) - return; - selmon->seltags ^= 1; /* toggle sel tagset */ - - int new_tag = selmon->pertag->curtag - n; - if (new_tag < 1) - new_tag = 1; - - if (target) { - selmon->tagset[selmon->seltags] = target; - selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->pertag->curtag = new_tag; - } else { - tmptag = selmon->pertag->prevtag; - selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->pertag->curtag = tmptag; - } - - focusclient(focustop(selmon), 1); - arrange(selmon, true); - printstatus(); + if (found) + view(&(Arg){.ui = (1 << (n - 1)) & TAGMASK, .i = arg->i}, true); + return 0; } -void viewtoright_have_client(const Arg *arg) { - unsigned int tmptag; - Client *c; - unsigned int found = 0; - unsigned int n = 1; - unsigned int target = selmon->tagset[selmon->seltags]; - if (selmon->isoverview || selmon->pertag->curtag == 0) { - return; +int32_t viewtoright_have_client(const Arg *arg) { + uint32_t n; + uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]); + bool found = false; + + if (selmon->isoverview) { + return 0; } - for (target <<= 1; target & TAGMASK && n <= LENGTH(tags); - target <<= 1, n++) { - wl_list_for_each(c, &clients, link) { - if (c->mon == selmon && target & c->tags) { - found = 1; - break; - } - } - if (found) { + if (current >= LENGTH(tags)) + return 0; + + for (n = current + 1; n <= LENGTH(tags); n++) { + if (get_tag_status(n, selmon)) { + found = true; break; } } - if (!(target & TAGMASK)) { - return; - } - - if (!selmon || (target) == selmon->tagset[selmon->seltags]) - return; - selmon->seltags ^= 1; /* toggle sel tagset */ - - int new_tag = selmon->pertag->curtag + n; - if (new_tag > LENGTH(tags)) - new_tag = LENGTH(tags); - - if (target) { - selmon->tagset[selmon->seltags] = target; - selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->pertag->curtag = new_tag; - } else { - tmptag = selmon->pertag->prevtag; - selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->pertag->curtag = tmptag; - } - - focusclient(focustop(selmon), 1); - arrange(selmon, true); - printstatus(); + if (found) + view(&(Arg){.ui = (1 << (n - 1)) & TAGMASK, .i = arg->i}, true); + return 0; } -void zoom(const Arg *arg) { - Client *c, *sel = focustop(selmon); + +int32_t viewcrossmon(const Arg *arg) { + focusmon(&(Arg){.v = arg->v, .i = UNDIR}); + view_in_mon(arg, true, selmon, true); + return 0; +} + +int32_t tagcrossmon(const Arg *arg) { + if (!selmon->sel) + return 0; + + tagmon(&(Arg){.ui = arg->ui, .i = UNDIR, .v = arg->v}); + return 0; +} + +int32_t comboview(const Arg *arg) { + uint32_t newtags = arg->ui & TAGMASK; + + if (!newtags || !selmon) + return 0; + + if (tag_combo) { + selmon->tagset[selmon->seltags] |= newtags; + focusclient(focustop(selmon), 1); + arrange(selmon, false, false); + } else { + tag_combo = true; + view(&(Arg){.ui = newtags}, false); + } + + printstatus(); + return 0; +} + +int32_t zoom(const Arg *arg) { + Client *c = NULL, *sel = focustop(selmon); if (!sel || !selmon || !selmon->pertag->ltidxs[selmon->pertag->curtag]->arrange || sel->isfloating) - return; + return 0; /* Search for the first tiled window that is not sel, marking sel as * NULL if we pass it along the way */ @@ -1290,7 +1451,7 @@ void zoom(const Arg *arg) { /* Return if no other tiled window was found */ if (&c->link == &clients) - return; + return 0; /* If we passed sel, move c to the front; otherwise, move sel to the * front */ @@ -1300,5 +1461,127 @@ void zoom(const Arg *arg) { wl_list_insert(&clients, &sel->link); focusclient(sel, 1); - arrange(selmon, false); + arrange(selmon, false, false); + return 0; +} + +int32_t setoption(const Arg *arg) { + parse_option(&config, arg->v, arg->v2); + override_config(); + reset_option(); + return 0; +} + +int32_t minimized(const Arg *arg) { + + if (selmon && selmon->isoverview) + return 0; + + if (selmon->sel && !selmon->sel->isminimized) { + set_minimized(selmon->sel); + } + return 0; +} + +int32_t toggleoverview(const Arg *arg) { + Client *c = NULL; + + if (selmon->isoverview && ov_tab_mode && arg->i != 1 && selmon->sel) { + focusstack(&(Arg){.i = 1}); + return 0; + } + + selmon->isoverview ^= 1; + uint32_t target; + uint32_t visible_client_number = 0; + + if (selmon->isoverview) { + wl_list_for_each(c, &clients, link) if (c && c->mon == selmon && + !client_is_unmanaged(c) && + !client_is_x11_popup(c) && + !c->isminimized && + !c->isunglobal) { + visible_client_number++; + } + if (visible_client_number > 0) { + target = ~0 & TAGMASK; + } else { + selmon->isoverview ^= 1; + return 0; + } + } else if (!selmon->isoverview && selmon->sel) { + target = get_tags_first_tag(selmon->sel->tags); + } else if (!selmon->isoverview && !selmon->sel) { + target = (1 << (selmon->pertag->prevtag - 1)); + view(&(Arg){.ui = target}, false); + refresh_monitors_workspaces_status(selmon); + return 0; + } + + // 正常视图到overview,退出所有窗口的浮动和全屏状态参与平铺, + // overview到正常视图,还原之前退出的浮动和全屏窗口状态 + if (selmon->isoverview) { + wl_list_for_each(c, &clients, link) { + if (c && c->mon == selmon && !client_is_unmanaged(c) && + !client_is_x11_popup(c) && !c->isunglobal) + overview_backup(c); + } + } else { + wl_list_for_each(c, &clients, link) { + if (c && c->mon == selmon && !c->iskilling && + !client_is_unmanaged(c) && !c->isunglobal && + !client_is_x11_popup(c) && client_surface(c)->mapped) + overview_restore(c, &(Arg){.ui = target}); + } + } + + view(&(Arg){.ui = target}, false); + + refresh_monitors_workspaces_status(selmon); + return 0; +} + +int32_t disable_monitor(const Arg *arg) { + Monitor *m = NULL; + struct wlr_output_state state = {0}; + wl_list_for_each(m, &mons, link) { + if (regex_match(arg->v, m->wlr_output->name)) { + wlr_output_state_set_enabled(&state, false); + wlr_output_commit_state(m->wlr_output, &state); + m->asleep = 1; + updatemons(NULL, NULL); + break; + } + } + return 0; +} + +int32_t enable_monitor(const Arg *arg) { + Monitor *m = NULL; + struct wlr_output_state state = {0}; + wl_list_for_each(m, &mons, link) { + if (regex_match(arg->v, m->wlr_output->name)) { + wlr_output_state_set_enabled(&state, true); + wlr_output_commit_state(m->wlr_output, &state); + m->asleep = 0; + updatemons(NULL, NULL); + break; + } + } + return 0; +} + +int32_t toggle_monitor(const Arg *arg) { + Monitor *m = NULL; + struct wlr_output_state state = {0}; + wl_list_for_each(m, &mons, link) { + if (regex_match(arg->v, m->wlr_output->name)) { + wlr_output_state_set_enabled(&state, !m->wlr_output->enabled); + wlr_output_commit_state(m->wlr_output, &state); + m->asleep = !m->wlr_output->enabled; + updatemons(NULL, NULL); + break; + } + } + return 0; } diff --git a/src/ext-protocol/all.h b/src/ext-protocol/all.h index 1ec7407e..a9551e96 100644 --- a/src/ext-protocol/all.h +++ b/src/ext-protocol/all.h @@ -3,3 +3,5 @@ #include "foreign-toplevel.h" #include "tablet.h" #include "text-input.h" +#include "tearing.h" +#include "text-input.h" diff --git a/src/ext-protocol/dwl-ipc.h b/src/ext-protocol/dwl-ipc.h index ed4d106d..ab0bdb8d 100644 --- a/src/ext-protocol/dwl-ipc.h +++ b/src/ext-protocol/dwl-ipc.h @@ -1,12 +1,11 @@ #include "dwl-ipc-unstable-v2-protocol.h" static void dwl_ipc_manager_bind(struct wl_client *client, void *data, - unsigned int version, unsigned int id); + uint32_t version, uint32_t id); static void dwl_ipc_manager_destroy(struct wl_resource *resource); static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, - unsigned int id, - struct wl_resource *output); + uint32_t id, struct wl_resource *output); static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); static void dwl_ipc_output_destroy(struct wl_resource *resource); @@ -14,15 +13,14 @@ static void dwl_ipc_output_printstatus(Monitor *monitor); static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, - unsigned int and_tags, - unsigned int xor_tags); + uint32_t and_tags, + uint32_t xor_tags); static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, - unsigned int index); + uint32_t index); static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, - unsigned int tagmask, - unsigned int toggle_tagset); + uint32_t tagmask, uint32_t toggle_tagset); static void dwl_ipc_output_quit(struct wl_client *client, struct wl_resource *resource); static void dwl_ipc_output_dispatch(struct wl_client *client, @@ -46,7 +44,7 @@ static struct zdwl_ipc_output_v2_interface dwl_output_implementation = { .set_client_tags = dwl_ipc_output_set_client_tags}; void dwl_ipc_manager_bind(struct wl_client *client, void *data, - unsigned int version, unsigned int id) { + uint32_t version, uint32_t id) { struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); if (!manager_resource) { @@ -59,7 +57,7 @@ void dwl_ipc_manager_bind(struct wl_client *client, void *data, zdwl_ipc_manager_v2_send_tags(manager_resource, LENGTH(tags)); - for (unsigned int i = 0; i < LENGTH(layouts); i++) + for (uint32_t i = 0; i < LENGTH(layouts); i++) zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); } @@ -68,7 +66,7 @@ void dwl_ipc_manager_destroy(struct wl_resource *resource) { } void dwl_ipc_manager_get_output(struct wl_client *client, - struct wl_resource *resource, unsigned int id, + struct wl_resource *resource, uint32_t id, struct wl_resource *output) { DwlIpcOutput *ipc_output; struct wlr_output *op = wlr_output_from_resource(output); @@ -101,19 +99,22 @@ static void dwl_ipc_output_destroy(struct wl_resource *resource) { free(ipc_output); } +// 修改IPC输出函数,接受掩码参数 void dwl_ipc_output_printstatus(Monitor *monitor) { DwlIpcOutput *ipc_output; wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) dwl_ipc_output_printstatus_to(ipc_output); } +// 修改主IPC输出函数,根据掩码发送相应事件 void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) { Monitor *monitor = ipc_output->mon; - Client *c, *focused; + Client *c = NULL, *focused = NULL; struct wlr_keyboard *keyboard; xkb_layout_index_t current; - int tagmask, state, numclients, focused_client, tag; - const char *title, *appid, *symbol, *kb_layout; + int32_t tagmask, state, numclients, focused_client, tag; + const char *title, *appid, *symbol; + char kb_layout[32]; focused = focustop(monitor); zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); @@ -140,19 +141,17 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) { title = focused ? client_get_title(focused) : ""; appid = focused ? client_get_appid(focused) : ""; - if (monitor->pertag->curtag) { - symbol = monitor->pertag->ltidxs[monitor->pertag->curtag]->symbol; - } else if (monitor->isoverview) { + if (monitor->isoverview) { symbol = overviewlayout.symbol; } else { - symbol = monitor->pertag->ltidxs[monitor->pertag->prevtag]->symbol; + symbol = monitor->pertag->ltidxs[monitor->pertag->curtag]->symbol; } keyboard = &kb_group->wlr_group->keyboard; current = xkb_state_serialize_layout(keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); - kb_layout = - get_layout_abbr(xkb_keymap_layout_get_name(keyboard->keymap, current)); + get_layout_abbr(kb_layout, + xkb_keymap_layout_get_name(keyboard->keymap, current)); zdwl_ipc_output_v2_send_layout( ipc_output->resource, @@ -201,17 +200,27 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) { zdwl_ipc_output_v2_send_kb_layout(ipc_output->resource, kb_layout); } + if (wl_resource_get_version(ipc_output->resource) >= + ZDWL_IPC_OUTPUT_V2_KEYMODE_SINCE_VERSION) { + zdwl_ipc_output_v2_send_keymode(ipc_output->resource, keymode.mode); + } + + if (wl_resource_get_version(ipc_output->resource) >= + ZDWL_IPC_OUTPUT_V2_SCALEFACTOR_SINCE_VERSION) { + zdwl_ipc_output_v2_send_scalefactor(ipc_output->resource, + monitor->wlr_output->scale * 100); + } + zdwl_ipc_output_v2_send_frame(ipc_output->resource); } void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, - unsigned int and_tags, - unsigned int xor_tags) { + uint32_t and_tags, uint32_t xor_tags) { DwlIpcOutput *ipc_output; - Monitor *monitor; - Client *selected_client; - unsigned int newtags = 0; + Monitor *monitor = NULL; + Client *selected_client = NULL; + uint32_t newtags = 0; ipc_output = wl_resource_get_user_data(resource); if (!ipc_output) @@ -229,15 +238,14 @@ void dwl_ipc_output_set_client_tags(struct wl_client *client, selected_client->tags = newtags; if (selmon == monitor) focusclient(focustop(monitor), 1); - arrange(selmon, false); + arrange(selmon, false, false); printstatus(); } void dwl_ipc_output_set_layout(struct wl_client *client, - struct wl_resource *resource, - unsigned int index) { + struct wl_resource *resource, uint32_t index) { DwlIpcOutput *ipc_output; - Monitor *monitor; + Monitor *monitor = NULL; ipc_output = wl_resource_get_user_data(resource); if (!ipc_output) @@ -248,23 +256,24 @@ void dwl_ipc_output_set_layout(struct wl_client *client, index = 0; monitor->pertag->ltidxs[monitor->pertag->curtag] = &layouts[index]; - arrange(monitor, false); + clear_fullscreen_and_maximized_state(monitor); + arrange(monitor, false, false); printstatus(); } void dwl_ipc_output_set_tags(struct wl_client *client, - struct wl_resource *resource, unsigned int tagmask, - unsigned int toggle_tagset) { + struct wl_resource *resource, uint32_t tagmask, + uint32_t toggle_tagset) { DwlIpcOutput *ipc_output; - Monitor *monitor; - unsigned int newtags = tagmask & TAGMASK; + Monitor *monitor = NULL; + uint32_t newtags = tagmask & TAGMASK; ipc_output = wl_resource_get_user_data(resource); if (!ipc_output) return; monitor = ipc_output->mon; - view_in_mon(&(Arg){.ui = newtags}, true, monitor); + view_in_mon(&(Arg){.ui = newtags}, true, monitor, true); } void dwl_ipc_output_quit(struct wl_client *client, @@ -278,7 +287,7 @@ void dwl_ipc_output_dispatch(struct wl_client *client, const char *arg3, const char *arg4, const char *arg5) { - void (*func)(const Arg *); + int32_t (*func)(const Arg *); Arg arg; func = parse_func_name((char *)dispatch, &arg, (char *)arg1, (char *)arg2, (char *)arg3, (char *)arg4, (char *)arg5); diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h index 78cc35a8..28aaeeeb 100644 --- a/src/ext-protocol/ext-workspace.h +++ b/src/ext-protocol/ext-workspace.h @@ -1,10 +1,14 @@ #include "wlr_ext_workspace_v1.h" +#define EXT_WORKSPACE_ENABLE_CAPS \ + EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE | \ + EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE + typedef struct Monitor Monitor; struct workspace { struct wl_list link; // Link in global workspaces list - unsigned int tag; // Numeric identifier (1-9, 0=overview) + 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 */ @@ -18,7 +22,7 @@ struct wlr_ext_workspace_manager_v1 *ext_manager; struct wl_list workspaces; void goto_workspace(struct workspace *target) { - unsigned int tag; + uint32_t tag; tag = 1 << (target->tag - 1); if (target->tag == 0) { toggleoverview(&(Arg){.i = -1}); @@ -28,15 +32,44 @@ void goto_workspace(struct workspace *target) { } } +void toggle_workspace(struct workspace *target) { + uint32_t tag; + tag = 1 << (target->tag - 1); + if (target->tag == 0) { + toggleview(&(Arg){.i = -1}); + return; + } else { + toggleview(&(Arg){.ui = tag}); + } +} + static void handle_ext_workspace_activate(struct wl_listener *listener, void *data) { struct workspace *workspace = wl_container_of(listener, workspace, activate); + + if (workspace->m->isoverview) { + return; + } + goto_workspace(workspace); wlr_log(WLR_INFO, "ext activating workspace %d", workspace->tag); } -static const char *get_name_from_tag(unsigned int tag) { +static void handle_ext_workspace_deactivate(struct wl_listener *listener, + void *data) { + struct workspace *workspace = + wl_container_of(listener, workspace, deactivate); + + if (workspace->m->isoverview) { + return; + } + + toggle_workspace(workspace); + wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag); +} + +static const char *get_name_from_tag(uint32_t tag) { static const char *names[] = {"overview", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; return (tag < sizeof(names) / sizeof(names[0])) ? names[tag] : NULL; @@ -44,6 +77,7 @@ static const char *get_name_from_tag(unsigned int 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); @@ -58,7 +92,7 @@ void cleanup_workspaces_by_monitor(Monitor *m) { } } -static void remove_workspace_by_tag(unsigned int tag, Monitor *m) { +static void remove_workspace_by_tag(uint32_t tag, Monitor *m) { struct workspace *workspace, *tmp; wl_list_for_each_safe(workspace, tmp, &workspaces, link) { if (workspace->tag == tag && workspace->m == m) { @@ -68,7 +102,7 @@ static void remove_workspace_by_tag(unsigned int tag, Monitor *m) { } } -static void add_workspace_by_tag(int tag, Monitor *m) { +static void add_workspace_by_tag(int32_t tag, Monitor *m) { const char *name = get_name_from_tag(tag); struct workspace *workspace = ecalloc(1, sizeof(*workspace)); @@ -77,67 +111,29 @@ static void add_workspace_by_tag(int tag, Monitor *m) { workspace->tag = tag; workspace->m = m; workspace->ext_workspace = wlr_ext_workspace_handle_v1_create( - ext_manager, name, WLR_EXT_WORKSPACE_HANDLE_V1_CAP_ACTIVATE); + ext_manager, name, EXT_WORKSPACE_ENABLE_CAPS); wlr_ext_workspace_handle_v1_set_group(workspace->ext_workspace, m->ext_group); wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name); + workspace->activate.notify = handle_ext_workspace_activate; wl_signal_add(&workspace->ext_workspace->events.activate, &workspace->activate); -} -unsigned int get_tag_status(unsigned int tag) { - Client *c; - unsigned int status = 0; - wl_list_for_each(c, &clients, link) { - if (c->tags & 1 << (tag - 1) & TAGMASK) { - if (c->isurgent) { - status = 2; - break; - } - status = 1; - } - } - return status; -} - -unsigned int get_tags_first_tag_num(unsigned int source_tags) { - unsigned int i, tag; - tag = 0; - - if (!source_tags) { - return selmon->pertag->curtag; - } - - for (i = 0; !(tag & 1) && source_tags != 0 && i < LENGTH(tags); i++) { - tag = source_tags >> i; - } - - if (i == 1) { - return 1; - } else if (i > 9) { - return 9; - } else { - return i; - } + workspace->deactivate.notify = handle_ext_workspace_deactivate; + wl_signal_add(&workspace->ext_workspace->events.deactivate, + &workspace->deactivate); } void dwl_ext_workspace_printstatus(Monitor *m) { - unsigned int current_tag; struct workspace *w; - unsigned int tag_status = 0; - bool is_active = false; - - current_tag = get_tags_first_tag_num(m->tagset[m->seltags]); + uint32_t tag_status = 0; wl_list_for_each(w, &workspaces, link) { if (w && w->m == m) { - is_active = (w->tag == current_tag) || m->isoverview; - tag_status = get_tag_status(w->tag); - if (is_active) { - wlr_ext_workspace_handle_v1_set_urgent(w->ext_workspace, false); - wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false); - } else if (tag_status == 2) { + + tag_status = get_tag_status(w->tag, m); + if (tag_status == 2) { wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false); wlr_ext_workspace_handle_v1_set_urgent(w->ext_workspace, true); } else if (tag_status == 1) { @@ -145,10 +141,17 @@ void dwl_ext_workspace_printstatus(Monitor *m) { wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false); } else { wlr_ext_workspace_handle_v1_set_urgent(w->ext_workspace, false); - wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, true); + if (!w->m->pertag->no_hide[w->tag]) + wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, + true); + else { + wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, + false); + } } - if (m->tagset[m->seltags] & (1 << (w->tag - 1)) & TAGMASK) { + if ((m->tagset[m->seltags] & (1 << (w->tag - 1)) & TAGMASK) || + m->isoverview) { wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false); wlr_ext_workspace_handle_v1_set_active(w->ext_workspace, true); } else { @@ -159,7 +162,7 @@ void dwl_ext_workspace_printstatus(Monitor *m) { } void refresh_monitors_workspaces_status(Monitor *m) { - int i; + int32_t i; if (m->isoverview) { for (i = 1; i <= LENGTH(tags); i++) { diff --git a/src/ext-protocol/foreign-toplevel.h b/src/ext-protocol/foreign-toplevel.h index 5e44e390..89f3839a 100644 --- a/src/ext-protocol/foreign-toplevel.h +++ b/src/ext-protocol/foreign-toplevel.h @@ -4,52 +4,100 @@ static struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; void handle_foreign_activate_request(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, foreign_activate_request); - unsigned int target; + uint32_t target; - if (c && c->swallowing) + if (c->swallowing) return; - if (c && !c->isminied && c == selmon->sel) { - set_minized(c); - return; - } - - if (c->isminied) { + if (c->isminimized) { c->is_in_scratchpad = 0; c->isnamedscratchpad = 0; c->is_scratchpad_show = 0; setborder_color(c); show_hide_client(c); - arrange(c->mon, true); + arrange(c->mon, true, false); return; } target = get_tags_first_tag(c->tags); - view(&(Arg){.ui = target}, true); + view_in_mon(&(Arg){.ui = target}, true, c->mon, true); focusclient(c, 1); - wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, true); +} + +void handle_foreign_maximize_request(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, foreign_maximize_request); + struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; + + if (c->swallowing) + return; + + if (c->ismaximizescreen && !event->maximized) { + setmaximizescreen(c, 0); + return; + } + + if (!c->ismaximizescreen && event->maximized) { + setmaximizescreen(c, 1); + return; + } +} + +void handle_foreign_minimize_request(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, foreign_minimize_request); + struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; + + if (c->swallowing) + return; + + if (!c->isminimized && event->minimized) { + set_minimized(c); + return; + } + + if (c->isminimized && !event->minimized) { + c->is_in_scratchpad = 0; + c->isnamedscratchpad = 0; + c->is_scratchpad_show = 0; + setborder_color(c); + show_hide_client(c); + arrange(c->mon, true, false); + return; + } } void handle_foreign_fullscreen_request(struct wl_listener *listener, void *data) { - return; + + Client *c = wl_container_of(listener, c, foreign_fullscreen_request); + struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; + + if (c->swallowing) + return; + + if (c->isfullscreen && !event->fullscreen) { + setfullscreen(c, 0); + return; + } + + if (!c->isfullscreen && event->fullscreen) { + setfullscreen(c, 1); + return; + } } void handle_foreign_close_request(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, foreign_close_request); - if (c) { - pending_kill_client(c); - } + pending_kill_client(c); } void handle_foreign_destroy(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, foreign_destroy); - if (c) { - wl_list_remove(&c->foreign_activate_request.link); - wl_list_remove(&c->foreign_fullscreen_request.link); - wl_list_remove(&c->foreign_close_request.link); - wl_list_remove(&c->foreign_destroy.link); - } + wl_list_remove(&c->foreign_activate_request.link); + wl_list_remove(&c->foreign_minimize_request.link); + wl_list_remove(&c->foreign_maximize_request.link); + wl_list_remove(&c->foreign_fullscreen_request.link); + wl_list_remove(&c->foreign_close_request.link); + wl_list_remove(&c->foreign_destroy.link); } void remove_foreign_topleve(Client *c) { @@ -67,6 +115,10 @@ void add_foreign_toplevel(Client *c) { if (c->foreign_toplevel) { LISTEN(&(c->foreign_toplevel->events.request_activate), &c->foreign_activate_request, handle_foreign_activate_request); + LISTEN(&(c->foreign_toplevel->events.request_minimize), + &c->foreign_minimize_request, handle_foreign_minimize_request); + LISTEN(&(c->foreign_toplevel->events.request_maximize), + &c->foreign_maximize_request, handle_foreign_maximize_request); LISTEN(&(c->foreign_toplevel->events.request_fullscreen), &c->foreign_fullscreen_request, handle_foreign_fullscreen_request); diff --git a/src/ext-protocol/tearing.h b/src/ext-protocol/tearing.h new file mode 100644 index 00000000..8e02656a --- /dev/null +++ b/src/ext-protocol/tearing.h @@ -0,0 +1,171 @@ +#include + +struct tearing_controller { + struct wlr_tearing_control_v1 *tearing_control; + struct wl_listener set_hint; + struct wl_listener destroy; +}; + +struct wlr_tearing_control_manager_v1 *tearing_control; +struct wl_listener tearing_new_object; + +static void handle_controller_set_hint(struct wl_listener *listener, + void *data) { + struct tearing_controller *controller = + wl_container_of(listener, controller, set_hint); + Client *c = NULL; + + toplevel_from_wlr_surface(controller->tearing_control->surface, &c, NULL); + + if (c) { + /* + * tearing_control->current is actually an enum: + * WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC = 0 + * WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC = 1 + * + * Using it as a bool here allows us to not ship the XML. + */ + c->tearing_hint = controller->tearing_control->current; + } +} + +static void handle_controller_destroy(struct wl_listener *listener, + void *data) { + struct tearing_controller *controller = + wl_container_of(listener, controller, destroy); + wl_list_remove(&controller->set_hint.link); + wl_list_remove(&controller->destroy.link); + free(controller); +} + +void handle_tearing_new_object(struct wl_listener *listener, void *data) { + struct wlr_tearing_control_v1 *new_tearing_control = data; + + enum wp_tearing_control_v1_presentation_hint hint = + wlr_tearing_control_manager_v1_surface_hint_from_surface( + tearing_control, new_tearing_control->surface); + wlr_log(WLR_DEBUG, "New presentation hint %d received for surface %p", hint, + new_tearing_control->surface); + + struct tearing_controller *controller = + ecalloc(1, sizeof(struct tearing_controller)); + controller->tearing_control = new_tearing_control; + + controller->set_hint.notify = handle_controller_set_hint; + wl_signal_add(&new_tearing_control->events.set_hint, &controller->set_hint); + + controller->destroy.notify = handle_controller_destroy; + wl_signal_add(&new_tearing_control->events.destroy, &controller->destroy); +} + +bool check_tearing_frame_allow(Monitor *m) { + /* never allow tearing when disabled */ + if (!allow_tearing) { + return false; + } + + Client *c = selmon->sel; + + /* tearing is only allowed for the output with the active client */ + if (!c || c->mon != m) { + return false; + } + + /* allow tearing for any window when requested or forced */ + if (allow_tearing == TEARING_ENABLED) { + if (c->force_tearing == STATE_UNSPECIFIED) { + return c->tearing_hint; + } else { + return c->force_tearing == STATE_ENABLED; + } + } + + /* remaining tearing options apply only to full-screen windows */ + if (!c->isfullscreen) { + return false; + } + + if (c->force_tearing == STATE_UNSPECIFIED) { + /* honor the tearing hint or the fullscreen-force preference */ + return c->tearing_hint || allow_tearing == TEARING_FULLSCREEN_ONLY; + } + + /* honor tearing as requested by action */ + return c->force_tearing == STATE_ENABLED; +} + +bool custom_wlr_scene_output_commit(struct wlr_scene_output *scene_output, + struct wlr_output_state *state) { + struct wlr_output *wlr_output = scene_output->output; + Monitor *m = wlr_output->data; + + // 检查是否需要帧 + if (!wlr_scene_output_needs_frame(scene_output)) { + wlr_log(WLR_DEBUG, "No frame needed for output %s", wlr_output->name); + return true; + } + + // 构建输出状态 + if (!wlr_scene_output_build_state(scene_output, state, NULL)) { + wlr_log(WLR_ERROR, "Failed to build output state for %s", + wlr_output->name); + return false; + } + + // 测试撕裂翻页 + if (state->tearing_page_flip) { + if (!wlr_output_test_state(wlr_output, state)) { + state->tearing_page_flip = false; + } + } + + // 尝试提交 + bool committed = wlr_output_commit_state(wlr_output, state); + + // 如果启用撕裂翻页但提交失败,重试禁用撕裂翻页 + if (!committed && state->tearing_page_flip) { + wlr_log(WLR_DEBUG, "Retrying commit without tearing for %s", + wlr_output->name); + state->tearing_page_flip = false; + committed = wlr_output_commit_state(wlr_output, state); + } + + // 处理状态清理 + if (committed) { + wlr_log(WLR_DEBUG, "Successfully committed output %s", + wlr_output->name); + if (state == &m->pending) { + wlr_output_state_finish(&m->pending); + wlr_output_state_init(&m->pending); + } + } else { + wlr_log(WLR_ERROR, "Failed to commit output %s", wlr_output->name); + // 即使提交失败,也清理状态避免积累 + if (state == &m->pending) { + wlr_output_state_finish(&m->pending); + wlr_output_state_init(&m->pending); + } + return false; + } + + return true; +} + +void apply_tear_state(Monitor *m) { + if (wlr_scene_output_needs_frame(m->scene_output)) { + wlr_output_state_init(&m->pending); + if (wlr_scene_output_build_state(m->scene_output, &m->pending, NULL)) { + struct wlr_output_state *pending = &m->pending; + pending->tearing_page_flip = true; + + if (!custom_wlr_scene_output_commit(m->scene_output, pending)) { + wlr_log(WLR_ERROR, "Failed to commit output %s", + m->scene_output->output->name); + } + } else { + wlr_log(WLR_ERROR, "Failed to build state for output %s", + m->scene_output->output->name); + wlr_output_state_finish(&m->pending); + } + } +} \ No newline at end of file diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h index c82ffac0..dbd97e11 100644 --- a/src/ext-protocol/text-input.h +++ b/src/ext-protocol/text-input.h @@ -68,7 +68,7 @@ void dwl_im_relay_set_focus(struct dwl_input_method_relay *relay, /*------------------协议内部代码------------------------------*/ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) { - Monitor *m; + Monitor *m = NULL; wl_list_for_each(m, &mons, link) { if (m->wlr_output == wlr_output) { return m; @@ -77,7 +77,7 @@ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) { return NULL; } -Monitor *output_nearest_to(int lx, int ly) { +Monitor *output_nearest_to(int32_t lx, int32_t ly) { double closest_x, closest_y; wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x, &closest_y); @@ -222,10 +222,10 @@ static void update_popup_position(struct dwl_input_method_popup *popup) { struct wlr_xdg_surface *xdg_surface; struct wlr_layer_surface_v1 *layer_surface; struct wlr_scene_tree *tree; - Monitor *output; + Monitor *output = NULL; struct wlr_xdg_positioner_rules pointer_rules; struct wlr_box output_box; - int lx, ly; + int32_t lx, ly; struct wlr_box popup_box; if (!text_input || !relay->focused_surface || @@ -490,6 +490,7 @@ static void handle_text_input_enable(struct wl_listener *listener, void *data) { update_popups_position(relay); send_state_to_input_method(relay); } + wlr_text_input_v3_send_done(text_input->input); } static void handle_text_input_disable(struct wl_listener *listener, diff --git a/src/ext-protocol/wlr_ext_workspace_v1.c b/src/ext-protocol/wlr_ext_workspace_v1.c index 3f80e9f2..2d781b34 100644 --- a/src/ext-protocol/wlr_ext_workspace_v1.c +++ b/src/ext-protocol/wlr_ext_workspace_v1.c @@ -29,7 +29,7 @@ struct wlr_ext_workspace_v1_request { // ACTIVATE / DEACTIVATE / ASSIGN / REMOVE struct wlr_ext_workspace_handle_v1 *workspace; - struct wl_list link; // wlr_ext_workspace_manager_client_v1.requests + struct wl_list link; // wlr_ext_workspace_manager_v1_resource.requests }; struct wlr_ext_workspace_v1_group_output { @@ -41,33 +41,40 @@ struct wlr_ext_workspace_v1_group_output { }; // These structs wrap wl_resource of each interface to access the request queue -// (wlr_ext_workspace_manager_client_v1.requests) assigned per manager resource +// (wlr_ext_workspace_manager_v1_resource.requests) assigned per manager +// resource -struct wlr_ext_workspace_manager_client_v1 { +struct wlr_ext_workspace_manager_v1_resource { struct wl_resource *resource; struct wlr_ext_workspace_manager_v1 *manager; - struct wl_list requests; // wlr_ext_workspace_v1_request.link - struct wl_list link; // wlr_ext_workspace_manager_v1.clients + struct wl_list requests; // wlr_ext_workspace_v1_request.link + struct wl_list workspace_resources; // wlr_ext_workspace_v1_resource.link + struct wl_list group_resources; // wlr_ext_workspace_group_v1_resource.link + struct wl_list link; // wlr_ext_workspace_manager_v1.resources }; -struct wlr_ext_workspace_group_client_v1 { +struct wlr_ext_workspace_group_v1_resource { struct wl_resource *resource; struct wlr_ext_workspace_group_handle_v1 *group; - struct wlr_ext_workspace_manager_client_v1 *manager; - struct wl_list link; // wlr_ext_workspace_group_v1.clients + struct wlr_ext_workspace_manager_v1_resource *manager; + struct wl_list link; // wlr_ext_workspace_group_v1.resources + struct wl_list + manager_resource_link; // wlr_ext_workspace_manager_v1_resource.group_resources }; -struct wlr_ext_workspace_client_v1 { +struct wlr_ext_workspace_v1_resource { struct wl_resource *resource; struct wlr_ext_workspace_handle_v1 *workspace; - struct wlr_ext_workspace_manager_client_v1 *manager; - struct wl_list link; // wlr_ext_workspace_v1.clients + struct wlr_ext_workspace_manager_v1_resource *manager; + struct wl_list link; // wlr_ext_workspace_v1.resources + struct wl_list + manager_resource_link; // wlr_ext_workspace_manager_v1_resource.workspace_resources }; static const struct ext_workspace_group_handle_v1_interface group_impl; -static struct wlr_ext_workspace_group_client_v1 * -group_client_from_resource(struct wl_resource *resource) { +static struct wlr_ext_workspace_group_v1_resource * +group_resource_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of( resource, &ext_workspace_group_handle_v1_interface, &group_impl)); return wl_resource_get_user_data(resource); @@ -75,8 +82,8 @@ group_client_from_resource(struct wl_resource *resource) { static const struct ext_workspace_handle_v1_interface workspace_impl; -static struct wlr_ext_workspace_client_v1 * -workspace_client_from_resource(struct wl_resource *resource) { +static struct wlr_ext_workspace_v1_resource * +workspace_resource_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &ext_workspace_handle_v1_interface, &workspace_impl)); return wl_resource_get_user_data(resource); @@ -84,8 +91,8 @@ workspace_client_from_resource(struct wl_resource *resource) { static const struct ext_workspace_manager_v1_interface manager_impl; -static struct wlr_ext_workspace_manager_client_v1 * -manager_client_from_resource(struct wl_resource *resource) { +static struct wlr_ext_workspace_manager_v1_resource * +manager_resource_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of( resource, &ext_workspace_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); @@ -98,9 +105,9 @@ static void workspace_handle_destroy(struct wl_client *client, static void workspace_handle_activate(struct wl_client *client, struct wl_resource *workspace_resource) { - struct wlr_ext_workspace_client_v1 *workspace = - workspace_client_from_resource(workspace_resource); - if (!workspace) { + struct wlr_ext_workspace_v1_resource *workspace_res = + workspace_resource_from_resource(workspace_resource); + if (!workspace_res) { return; } @@ -110,16 +117,16 @@ static void workspace_handle_activate(struct wl_client *client, return; } req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE; - req->workspace = workspace->workspace; - wl_list_insert(workspace->manager->requests.prev, &req->link); + req->workspace = workspace_res->workspace; + wl_list_insert(workspace_res->manager->requests.prev, &req->link); } static void workspace_handle_deactivate(struct wl_client *client, struct wl_resource *workspace_resource) { - struct wlr_ext_workspace_client_v1 *workspace = - workspace_client_from_resource(workspace_resource); - if (!workspace) { + struct wlr_ext_workspace_v1_resource *workspace_res = + workspace_resource_from_resource(workspace_resource); + if (!workspace_res) { return; } @@ -129,18 +136,18 @@ workspace_handle_deactivate(struct wl_client *client, return; } req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE; - req->workspace = workspace->workspace; - wl_list_insert(workspace->manager->requests.prev, &req->link); + req->workspace = workspace_res->workspace; + wl_list_insert(workspace_res->manager->requests.prev, &req->link); } static void workspace_handle_assign(struct wl_client *client, struct wl_resource *workspace_resource, struct wl_resource *group_resource) { - struct wlr_ext_workspace_client_v1 *workspace = - workspace_client_from_resource(workspace_resource); - struct wlr_ext_workspace_group_client_v1 *group = - group_client_from_resource(group_resource); - if (!workspace || !group) { + struct wlr_ext_workspace_v1_resource *workspace_res = + workspace_resource_from_resource(workspace_resource); + struct wlr_ext_workspace_group_v1_resource *group_res = + group_resource_from_resource(group_resource); + if (!workspace_res || !group_res) { return; } @@ -150,16 +157,16 @@ static void workspace_handle_assign(struct wl_client *client, return; } req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN; - req->group = group->group; - req->workspace = workspace->workspace; - wl_list_insert(workspace->manager->requests.prev, &req->link); + req->group = group_res->group; + req->workspace = workspace_res->workspace; + wl_list_insert(workspace_res->manager->requests.prev, &req->link); } static void workspace_handle_remove(struct wl_client *client, struct wl_resource *workspace_resource) { - struct wlr_ext_workspace_client_v1 *workspace = - workspace_client_from_resource(workspace_resource); - if (!workspace) { + struct wlr_ext_workspace_v1_resource *workspace_res = + workspace_resource_from_resource(workspace_resource); + if (!workspace_res) { return; } @@ -169,8 +176,8 @@ static void workspace_handle_remove(struct wl_client *client, return; } req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE; - req->workspace = workspace->workspace; - wl_list_insert(workspace->manager->requests.prev, &req->link); + req->workspace = workspace_res->workspace; + wl_list_insert(workspace_res->manager->requests.prev, &req->link); } static const struct ext_workspace_handle_v1_interface workspace_impl = { @@ -184,9 +191,9 @@ static const struct ext_workspace_handle_v1_interface workspace_impl = { static void group_handle_create_workspace(struct wl_client *client, struct wl_resource *group_resource, const char *name) { - struct wlr_ext_workspace_group_client_v1 *group = - group_client_from_resource(group_resource); - if (!group) { + struct wlr_ext_workspace_group_v1_resource *group_res = + group_resource_from_resource(group_resource); + if (!group_res) { return; } @@ -195,9 +202,15 @@ static void group_handle_create_workspace(struct wl_client *client, wl_resource_post_no_memory(group_resource); return; } + req->name = strdup(name); + if (!req->name) { + free(req); + wl_resource_post_no_memory(group_resource); + return; + } req->type = WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE; - req->group = group->group; - wl_list_insert(group->manager->requests.prev, &req->link); + req->group = group_res->group; + wl_list_insert(group_res->manager->requests.prev, &req->link); } static void group_handle_destroy(struct wl_client *client, @@ -210,89 +223,94 @@ static const struct ext_workspace_group_handle_v1_interface group_impl = { .destroy = group_handle_destroy, }; -static void -destroy_workspace_client(struct wlr_ext_workspace_client_v1 *workspace_client) { - wl_list_remove(&workspace_client->link); - wl_resource_set_user_data(workspace_client->resource, NULL); - free(workspace_client); +static void destroy_workspace_resource( + struct wlr_ext_workspace_v1_resource *workspace_res) { + wl_list_remove(&workspace_res->link); + wl_list_remove(&workspace_res->manager_resource_link); + wl_resource_set_user_data(workspace_res->resource, NULL); + free(workspace_res); } static void workspace_resource_destroy(struct wl_resource *resource) { - struct wlr_ext_workspace_client_v1 *workspace_client = - workspace_client_from_resource(resource); - if (workspace_client) { - destroy_workspace_client(workspace_client); + struct wlr_ext_workspace_v1_resource *workspace_res = + workspace_resource_from_resource(resource); + if (workspace_res) { + destroy_workspace_resource(workspace_res); } } -static struct wlr_ext_workspace_client_v1 *create_workspace_client( +static struct wlr_ext_workspace_v1_resource *create_workspace_resource( struct wlr_ext_workspace_handle_v1 *workspace, - struct wlr_ext_workspace_manager_client_v1 *manager_client) { - struct wlr_ext_workspace_client_v1 *workspace_client = - calloc(1, sizeof(*workspace_client)); - if (!workspace_client) { + struct wlr_ext_workspace_manager_v1_resource *manager_res) { + struct wlr_ext_workspace_v1_resource *workspace_res = + calloc(1, sizeof(*workspace_res)); + if (!workspace_res) { return NULL; } - struct wl_client *client = wl_resource_get_client(manager_client->resource); - workspace_client->resource = wl_resource_create( - client, &ext_workspace_handle_v1_interface, - wl_resource_get_version(manager_client->resource), 0); - if (!workspace_client->resource) { - free(workspace_client); + struct wl_client *client = wl_resource_get_client(manager_res->resource); + workspace_res->resource = + wl_resource_create(client, &ext_workspace_handle_v1_interface, + wl_resource_get_version(manager_res->resource), 0); + if (!workspace_res->resource) { + free(workspace_res); return NULL; } - wl_resource_set_implementation(workspace_client->resource, &workspace_impl, - workspace_client, - workspace_resource_destroy); + wl_resource_set_implementation(workspace_res->resource, &workspace_impl, + workspace_res, workspace_resource_destroy); - workspace_client->workspace = workspace; - workspace_client->manager = manager_client; - wl_list_insert(&workspace->clients, &workspace_client->link); + workspace_res->workspace = workspace; + workspace_res->manager = manager_res; + wl_list_insert(&workspace->resources, &workspace_res->link); + wl_list_insert(&manager_res->workspace_resources, + &workspace_res->manager_resource_link); - return workspace_client; + return workspace_res; } static void -destroy_group_client(struct wlr_ext_workspace_group_client_v1 *group_client) { - wl_list_remove(&group_client->link); - wl_resource_set_user_data(group_client->resource, NULL); - free(group_client); +destroy_group_resource(struct wlr_ext_workspace_group_v1_resource *group_res) { + wl_list_remove(&group_res->link); + wl_list_remove(&group_res->manager_resource_link); + wl_resource_set_user_data(group_res->resource, NULL); + free(group_res); } static void group_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_ext_workspace_group_client_v1 *group_client = - group_client_from_resource(resource); - if (group_client) { - destroy_group_client(group_client); + struct wlr_ext_workspace_group_v1_resource *group_res = + group_resource_from_resource(resource); + if (group_res) { + destroy_group_resource(group_res); } } -static struct wlr_ext_workspace_group_client_v1 *create_group_client( +static struct wlr_ext_workspace_group_v1_resource *create_group_resource( struct wlr_ext_workspace_group_handle_v1 *group, - struct wlr_ext_workspace_manager_client_v1 *manager_client) { - struct wlr_ext_workspace_group_client_v1 *group_client = - calloc(1, sizeof(*group_client)); - if (!group_client) { + struct wlr_ext_workspace_manager_v1_resource *manager_res) { + struct wlr_ext_workspace_group_v1_resource *group_res = + calloc(1, sizeof(*group_res)); + if (!group_res) { return NULL; } - struct wl_client *client = wl_resource_get_client(manager_client->resource); - uint32_t version = wl_resource_get_version(manager_client->resource); - group_client->resource = wl_resource_create( + struct wl_client *client = wl_resource_get_client(manager_res->resource); + uint32_t version = wl_resource_get_version(manager_res->resource); + group_res->resource = wl_resource_create( client, &ext_workspace_group_handle_v1_interface, version, 0); - if (group_client->resource == NULL) { - free(group_client); + if (group_res->resource == NULL) { + free(group_res); return NULL; } - wl_resource_set_implementation(group_client->resource, &group_impl, - group_client, group_handle_resource_destroy); + wl_resource_set_implementation(group_res->resource, &group_impl, group_res, + group_handle_resource_destroy); - group_client->group = group; - group_client->manager = manager_client; - wl_list_insert(&group->clients, &group_client->link); + group_res->group = group; + group_res->manager = manager_res; + wl_list_insert(&group->resources, &group_res->link); + wl_list_insert(&manager_res->group_resources, + &group_res->manager_resource_link); - return group_client; + return group_res; } static void destroy_request(struct wlr_ext_workspace_v1_request *req) { @@ -303,18 +321,22 @@ static void destroy_request(struct wlr_ext_workspace_v1_request *req) { static void manager_handle_commit(struct wl_client *client, struct wl_resource *resource) { - struct wlr_ext_workspace_manager_client_v1 *manager = - manager_client_from_resource(resource); - if (!manager) { + struct wlr_ext_workspace_manager_v1_resource *manager_res = + manager_resource_from_resource(resource); + if (!manager_res) { return; } struct wlr_ext_workspace_v1_request *req, *tmp; - wl_list_for_each_safe(req, tmp, &manager->requests, link) { + wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { switch (req->type) { - case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE: + case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE:; + struct wlr_ext_workspace_group_handle_v1_create_workspace_event + event = { + .name = req->name, + }; wl_signal_emit_mutable(&req->group->events.create_workspace, - req->name); + &event); break; case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE: wl_signal_emit_mutable(&req->workspace->events.activate, NULL); @@ -323,13 +345,11 @@ static void manager_handle_commit(struct wl_client *client, 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); + 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; - default: - abort(); } destroy_request(req); } @@ -338,9 +358,9 @@ static void manager_handle_commit(struct wl_client *client, static void handle_idle(void *data) { struct wlr_ext_workspace_manager_v1 *manager = data; - struct wlr_ext_workspace_manager_client_v1 *manager_client; - wl_list_for_each(manager_client, &manager->clients, link) { - ext_workspace_manager_v1_send_done(manager_client->resource); + struct wlr_ext_workspace_manager_v1_resource *manager_res; + wl_list_for_each(manager_res, &manager->resources, link) { + ext_workspace_manager_v1_send_done(manager_res->resource); } manager->idle_source = NULL; } @@ -354,9 +374,9 @@ manager_schedule_done(struct wlr_ext_workspace_manager_v1 *manager) { } static void -workspace_send_details(struct wlr_ext_workspace_client_v1 *workspace_client) { - struct wlr_ext_workspace_handle_v1 *workspace = workspace_client->workspace; - struct wl_resource *resource = workspace_client->resource; +workspace_send_details(struct wlr_ext_workspace_v1_resource *workspace_res) { + struct wlr_ext_workspace_handle_v1 *workspace = workspace_res->workspace; + struct wl_resource *resource = workspace_res->resource; ext_workspace_handle_v1_send_capabilities(resource, workspace->caps); if (workspace->coordinates.size > 0) { @@ -384,29 +404,41 @@ static const struct ext_workspace_manager_v1_interface manager_impl = { .stop = manager_handle_stop, }; -static void destroy_manager_client( - struct wlr_ext_workspace_manager_client_v1 *manager_client) { +static void destroy_manager_resource( + struct wlr_ext_workspace_manager_v1_resource *manager_res) { struct wlr_ext_workspace_v1_request *req, *tmp; - wl_list_for_each_safe(req, tmp, &manager_client->requests, link) { + wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { destroy_request(req); } - wl_list_remove(&manager_client->link); - wl_resource_set_user_data(manager_client->resource, NULL); - free(manager_client); + struct wlr_ext_workspace_v1_resource *workspace_res, *tmp2; + wl_list_for_each_safe(workspace_res, tmp2, + &manager_res->workspace_resources, + manager_resource_link) { + destroy_workspace_resource(workspace_res); + } + struct wlr_ext_workspace_group_v1_resource *group_res, *tmp3; + wl_list_for_each_safe(group_res, tmp3, &manager_res->group_resources, + manager_resource_link) { + destroy_group_resource(group_res); + } + + wl_list_remove(&manager_res->link); + wl_resource_set_user_data(manager_res->resource, NULL); + free(manager_res); } static void manager_resource_destroy(struct wl_resource *resource) { - struct wlr_ext_workspace_manager_client_v1 *manager_client = - manager_client_from_resource(resource); - if (manager_client) { - destroy_manager_client(manager_client); + struct wlr_ext_workspace_manager_v1_resource *manager_res = + manager_resource_from_resource(resource); + if (manager_res) { + destroy_manager_resource(manager_res); } } static void -group_send_details(struct wlr_ext_workspace_group_client_v1 *group_client) { - struct wlr_ext_workspace_group_handle_v1 *group = group_client->group; - struct wl_resource *resource = group_client->resource; +group_send_details(struct wlr_ext_workspace_group_v1_resource *group_res) { + struct wlr_ext_workspace_group_handle_v1 *group = group_res->group; + struct wl_resource *resource = group_res->resource; struct wl_client *client = wl_resource_get_client(resource); ext_workspace_group_handle_v1_send_capabilities(resource, group->caps); @@ -430,65 +462,67 @@ static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_ext_workspace_manager_v1 *manager = data; - struct wlr_ext_workspace_manager_client_v1 *manager_client = - calloc(1, sizeof(*manager_client)); - if (!manager_client) { + struct wlr_ext_workspace_manager_v1_resource *manager_res = + calloc(1, sizeof(*manager_res)); + if (!manager_res) { wl_client_post_no_memory(client); return; } - manager_client->manager = manager; - wl_list_init(&manager_client->requests); - wl_list_insert(&manager->clients, &manager_client->link); + manager_res->manager = manager; + wl_list_init(&manager_res->requests); + wl_list_init(&manager_res->workspace_resources); + wl_list_init(&manager_res->group_resources); - manager_client->resource = wl_resource_create( + manager_res->resource = wl_resource_create( client, &ext_workspace_manager_v1_interface, version, id); - if (!manager_client->resource) { - free(manager_client); + if (!manager_res->resource) { + free(manager_res); wl_client_post_no_memory(client); return; } - wl_resource_set_implementation(manager_client->resource, &manager_impl, - manager_client, manager_resource_destroy); + wl_resource_set_implementation(manager_res->resource, &manager_impl, + manager_res, manager_resource_destroy); + wl_list_insert(&manager->resources, &manager_res->link); struct wlr_ext_workspace_group_handle_v1 *group; wl_list_for_each(group, &manager->groups, link) { - struct wlr_ext_workspace_group_client_v1 *group_client = - create_group_client(group, manager_client); - if (!group_client) { - wl_resource_post_no_memory(manager_client->resource); + struct wlr_ext_workspace_group_v1_resource *group_res = + create_group_resource(group, manager_res); + if (!group_res) { + wl_resource_post_no_memory(manager_res->resource); continue; } - ext_workspace_manager_v1_send_workspace_group(manager_client->resource, - group_client->resource); - group_send_details(group_client); + ext_workspace_manager_v1_send_workspace_group(manager_res->resource, + group_res->resource); + group_send_details(group_res); } struct wlr_ext_workspace_handle_v1 *workspace; wl_list_for_each(workspace, &manager->workspaces, link) { - struct wlr_ext_workspace_client_v1 *workspace_client = - create_workspace_client(workspace, manager_client); - if (!workspace) { - wl_client_post_no_memory(client); + struct wlr_ext_workspace_v1_resource *workspace_res = + create_workspace_resource(workspace, manager_res); + if (!workspace_res) { + wl_resource_post_no_memory(manager_res->resource); continue; } - ext_workspace_manager_v1_send_workspace(manager_client->resource, - workspace_client->resource); - workspace_send_details(workspace_client); + ext_workspace_manager_v1_send_workspace(manager_res->resource, + workspace_res->resource); + workspace_send_details(workspace_res); if (!workspace->group) { continue; } - struct wlr_ext_workspace_group_client_v1 *group_client; - wl_list_for_each(group_client, &workspace->group->clients, link) { - if (group_client->manager == manager_client) { + struct wlr_ext_workspace_group_v1_resource *group_res; + wl_list_for_each(group_res, &workspace->group->resources, link) { + if (group_res->manager == manager_res) { ext_workspace_group_handle_v1_send_workspace_enter( - group_client->resource, workspace_client->resource); + group_res->resource, workspace_res->resource); } } } - manager_schedule_done(manager); + ext_workspace_manager_v1_send_done(manager_res->resource); } static void manager_handle_display_destroy(struct wl_listener *listener, @@ -509,9 +543,9 @@ static void manager_handle_display_destroy(struct wl_listener *listener, wlr_ext_workspace_handle_v1_destroy(workspace); } - struct wlr_ext_workspace_manager_client_v1 *manager_client, *tmp3; - wl_list_for_each_safe(manager_client, tmp3, &manager->clients, link) { - destroy_manager_client(manager_client); + struct wlr_ext_workspace_manager_v1_resource *manager_res, *tmp3; + wl_list_for_each_safe(manager_res, tmp3, &manager->resources, link) { + destroy_manager_resource(manager_res); } if (manager->idle_source) { @@ -548,7 +582,7 @@ wlr_ext_workspace_manager_v1_create(struct wl_display *display, wl_list_init(&manager->groups); wl_list_init(&manager->workspaces); - wl_list_init(&manager->clients); + wl_list_init(&manager->resources); wl_signal_init(&manager->events.destroy); return manager; @@ -566,22 +600,22 @@ wlr_ext_workspace_group_handle_v1_create( group->caps = caps; wl_list_init(&group->outputs); - wl_list_init(&group->clients); + wl_list_init(&group->resources); wl_signal_init(&group->events.create_workspace); wl_signal_init(&group->events.destroy); wl_list_insert(manager->groups.prev, &group->link); - struct wlr_ext_workspace_manager_client_v1 *manager_client; - wl_list_for_each(manager_client, &manager->clients, link) { - struct wlr_ext_workspace_group_client_v1 *group_client = - create_group_client(group, manager_client); - if (!group_client) { + struct wlr_ext_workspace_manager_v1_resource *manager_res; + wl_list_for_each(manager_res, &manager->resources, link) { + struct wlr_ext_workspace_group_v1_resource *group_res = + create_group_resource(group, manager_res); + if (!group_res) { continue; } - ext_workspace_manager_v1_send_workspace_group(manager_client->resource, - group_client->resource); - group_send_details(group_client); + ext_workspace_manager_v1_send_workspace_group(manager_res->resource, + group_res->resource); + group_send_details(group_res); } manager_schedule_done(manager); @@ -594,19 +628,19 @@ workspace_send_group(struct wlr_ext_workspace_handle_v1 *workspace, struct wlr_ext_workspace_group_handle_v1 *group, bool enter) { - struct wlr_ext_workspace_client_v1 *workspace_client; - wl_list_for_each(workspace_client, &workspace->clients, link) { - struct wlr_ext_workspace_group_client_v1 *group_client; - wl_list_for_each(group_client, &group->clients, link) { - if (group_client->manager != workspace_client->manager) { + struct wlr_ext_workspace_v1_resource *workspace_res; + wl_list_for_each(workspace_res, &workspace->resources, link) { + struct wlr_ext_workspace_group_v1_resource *group_res; + wl_list_for_each(group_res, &group->resources, link) { + if (group_res->manager != workspace_res->manager) { continue; } if (enter) { ext_workspace_group_handle_v1_send_workspace_enter( - group_client->resource, workspace_client->resource); + group_res->resource, workspace_res->resource); } else { ext_workspace_group_handle_v1_send_workspace_leave( - group_client->resource, workspace_client->resource); + group_res->resource, workspace_res->resource); } } } @@ -625,10 +659,9 @@ destroy_group_output(struct wlr_ext_workspace_v1_group_output *group_output) { static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output, bool enter) { - struct wlr_ext_workspace_group_client_v1 *group_client; - wl_list_for_each(group_client, &group->clients, link) { - struct wl_client *client = - wl_resource_get_client(group_client->resource); + struct wlr_ext_workspace_group_v1_resource *group_res; + wl_list_for_each(group_res, &group->resources, link) { + struct wl_client *client = wl_resource_get_client(group_res->resource); struct wl_resource *output_resource; wl_resource_for_each(output_resource, &output->resources) { @@ -637,10 +670,10 @@ static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group, } if (enter) { ext_workspace_group_handle_v1_send_output_enter( - group_client->resource, output_resource); + group_res->resource, output_resource); } else { ext_workspace_group_handle_v1_send_output_leave( - group_client->resource, output_resource); + group_res->resource, output_resource); } } } @@ -650,6 +683,10 @@ static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group, void wlr_ext_workspace_group_handle_v1_destroy( struct wlr_ext_workspace_group_handle_v1 *group) { + if (!group) { + return; + } + wl_signal_emit_mutable(&group->events.destroy, NULL); assert(wl_list_empty(&group->events.create_workspace.listener_list)); @@ -663,16 +700,16 @@ void wlr_ext_workspace_group_handle_v1_destroy( } } - struct wlr_ext_workspace_group_client_v1 *group_client, *tmp; - wl_list_for_each_safe(group_client, tmp, &group->clients, link) { - ext_workspace_group_handle_v1_send_removed(group_client->resource); - destroy_group_client(group_client); + struct wlr_ext_workspace_group_v1_resource *group_res, *tmp; + wl_list_for_each_safe(group_res, tmp, &group->resources, link) { + ext_workspace_group_handle_v1_send_removed(group_res->resource); + destroy_group_resource(group_res); } - struct wlr_ext_workspace_manager_client_v1 *manager_client; - wl_list_for_each(manager_client, &group->manager->clients, link) { + struct wlr_ext_workspace_manager_v1_resource *manager_res; + wl_list_for_each(manager_res, &group->manager->resources, link) { struct wlr_ext_workspace_v1_request *req, *tmp2; - wl_list_for_each_safe(req, tmp2, &manager_client->requests, link) { + wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) { if (req->group == group) { destroy_request(req); } @@ -697,11 +734,11 @@ static void handle_output_bind(struct wl_listener *listener, void *data) { struct wlr_output_event_bind *event = data; struct wl_client *client = wl_resource_get_client(event->resource); - struct wlr_ext_workspace_group_client_v1 *group_client; - wl_list_for_each(group_client, &group_output->group->clients, link) { - if (wl_resource_get_client(group_client->resource) == client) { - ext_workspace_group_handle_v1_send_output_enter( - group_client->resource, event->resource); + struct wlr_ext_workspace_group_v1_resource *group_res; + wl_list_for_each(group_res, &group_output->group->resources, link) { + if (wl_resource_get_client(group_res->resource) == client) { + ext_workspace_group_handle_v1_send_output_enter(group_res->resource, + event->resource); } } @@ -778,11 +815,12 @@ wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager, if (id) { workspace->id = strdup(id); if (!workspace->id) { + free(workspace); return NULL; } } - wl_list_init(&workspace->clients); + wl_list_init(&workspace->resources); wl_array_init(&workspace->coordinates); wl_signal_init(&workspace->events.activate); wl_signal_init(&workspace->events.deactivate); @@ -792,16 +830,16 @@ wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager, wl_list_insert(&manager->workspaces, &workspace->link); - struct wlr_ext_workspace_manager_client_v1 *manager_client; - wl_list_for_each(manager_client, &manager->clients, link) { - struct wlr_ext_workspace_client_v1 *workspace_client = - create_workspace_client(workspace, manager_client); - if (!workspace_client) { + struct wlr_ext_workspace_manager_v1_resource *manager_res; + wl_list_for_each(manager_res, &manager->resources, link) { + struct wlr_ext_workspace_v1_resource *workspace_res = + create_workspace_resource(workspace, manager_res); + if (!workspace_res) { continue; } - ext_workspace_manager_v1_send_workspace(manager_client->resource, - workspace_client->resource); - workspace_send_details(workspace_client); + ext_workspace_manager_v1_send_workspace(manager_res->resource, + workspace_res->resource); + workspace_send_details(workspace_res); } manager_schedule_done(manager); @@ -811,6 +849,10 @@ wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager, void wlr_ext_workspace_handle_v1_destroy( struct wlr_ext_workspace_handle_v1 *workspace) { + if (!workspace) { + return; + } + wl_signal_emit_mutable(&workspace->events.destroy, NULL); assert(wl_list_empty(&workspace->events.activate.listener_list)); @@ -823,16 +865,16 @@ void wlr_ext_workspace_handle_v1_destroy( workspace_send_group(workspace, workspace->group, false); } - struct wlr_ext_workspace_client_v1 *workspace_client, *tmp; - wl_list_for_each_safe(workspace_client, tmp, &workspace->clients, link) { - ext_workspace_handle_v1_send_removed(workspace_client->resource); - destroy_workspace_client(workspace_client); + struct wlr_ext_workspace_v1_resource *workspace_res, *tmp; + wl_list_for_each_safe(workspace_res, tmp, &workspace->resources, link) { + ext_workspace_handle_v1_send_removed(workspace_res->resource); + destroy_workspace_resource(workspace_res); } - struct wlr_ext_workspace_manager_client_v1 *manager_client; - wl_list_for_each(manager_client, &workspace->manager->clients, link) { + struct wlr_ext_workspace_manager_v1_resource *manager_res; + wl_list_for_each(manager_res, &workspace->manager->resources, link) { struct wlr_ext_workspace_v1_request *req, *tmp2; - wl_list_for_each_safe(req, tmp2, &manager_client->requests, link) { + wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) { if (req->workspace == workspace) { destroy_request(req); } @@ -856,7 +898,7 @@ void wlr_ext_workspace_handle_v1_set_group( } if (workspace->group) { - workspace_send_group(workspace, group, false); + workspace_send_group(workspace, workspace->group, false); } workspace->group = group; if (group) { @@ -878,9 +920,9 @@ void wlr_ext_workspace_handle_v1_set_name( return; } - struct wlr_ext_workspace_client_v1 *workspace_client; - wl_list_for_each(workspace_client, &workspace->clients, link) { - ext_workspace_handle_v1_send_name(workspace_client->resource, + struct wlr_ext_workspace_v1_resource *workspace_res; + wl_list_for_each(workspace_res, &workspace->resources, link) { + ext_workspace_handle_v1_send_name(workspace_res->resource, workspace->name); } @@ -905,9 +947,9 @@ void wlr_ext_workspace_handle_v1_set_coordinates( wl_array_init(&workspace->coordinates); wl_array_copy(&workspace->coordinates, coordinates); - struct wlr_ext_workspace_client_v1 *workspace_client; - wl_list_for_each(workspace_client, &workspace->clients, link) { - ext_workspace_handle_v1_send_coordinates(workspace_client->resource, + struct wlr_ext_workspace_v1_resource *workspace_res; + wl_list_for_each(workspace_res, &workspace->resources, link) { + ext_workspace_handle_v1_send_coordinates(workspace_res->resource, &workspace->coordinates); } @@ -927,9 +969,9 @@ static void workspace_set_state(struct wlr_ext_workspace_handle_v1 *workspace, return; } - struct wlr_ext_workspace_client_v1 *workspace_client; - wl_list_for_each(workspace_client, &workspace->clients, link) { - ext_workspace_handle_v1_send_state(workspace_client->resource, + struct wlr_ext_workspace_v1_resource *workspace_res; + wl_list_for_each(workspace_res, &workspace->resources, link) { + ext_workspace_handle_v1_send_state(workspace_res->resource, workspace->state); } diff --git a/src/ext-protocol/wlr_ext_workspace_v1.h b/src/ext-protocol/wlr_ext_workspace_v1.h index e1d285b2..a2a733b3 100644 --- a/src/ext-protocol/wlr_ext_workspace_v1.h +++ b/src/ext-protocol/wlr_ext_workspace_v1.h @@ -2,32 +2,11 @@ // TODO: remove this file // refer: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5115 -/* - * 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 - +#include #include struct wlr_output; -enum wlr_ext_workspace_group_handle_v1_cap { - WLR_EXT_WORKSPACE_GROUP_HANDLE_V1_CAP_CREATE_WORKSPACE = 1 << 0, -}; - -enum wlr_ext_workspace_handle_v1_cap { - WLR_EXT_WORKSPACE_HANDLE_V1_CAP_ACTIVATE = 1 << 0, - WLR_EXT_WORKSPACE_HANDLE_V1_CAP_DEACTIVATE = 1 << 1, - WLR_EXT_WORKSPACE_HANDLE_V1_CAP_REMOVE = 1 << 2, - WLR_EXT_WORKSPACE_HANDLE_V1_CAP_ASSIGN = 1 << 3, -}; - struct wlr_ext_workspace_manager_v1 { struct wl_global *global; struct wl_list groups; // wlr_ext_workspace_group_handle_v1.link @@ -37,24 +16,33 @@ struct wlr_ext_workspace_manager_v1 { struct wl_signal destroy; } events; - struct wl_list clients; // wlr_ext_workspace_manager_client_v1.link - struct wl_event_source *idle_source; - struct wl_event_loop *event_loop; - struct wl_listener display_destroy; + struct { + struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link + struct wl_event_source *idle_source; + struct wl_event_loop *event_loop; + struct wl_listener display_destroy; + }; +}; + +struct wlr_ext_workspace_group_handle_v1_create_workspace_event { + const char *name; }; struct wlr_ext_workspace_group_handle_v1 { struct wlr_ext_workspace_manager_v1 *manager; - uint32_t caps; // wlr_ext_workspace_group_handle_v1_cap + uint32_t caps; // ext_workspace_group_handle_v1_group_capabilities struct { - struct wl_signal create_workspace; // const char * + struct wl_signal + create_workspace; // wlr_ext_workspace_group_handle_v1_create_workspace_event struct wl_signal destroy; } events; struct wl_list link; // wlr_ext_workspace_manager_v1.groups - struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link - struct wl_list clients; // wlr_ext_workspace_manager_client_v1.link + struct { + struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link + struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link + }; }; struct wlr_ext_workspace_handle_v1 { @@ -63,7 +51,7 @@ struct wlr_ext_workspace_handle_v1 { char *id; char *name; struct wl_array coordinates; - uint32_t caps; // wlr_ext_workspace_handle_v1_cap + uint32_t caps; // ext_workspace_handle_v1_workspace_capabilities uint32_t state; // ext_workspace_handle_v1_state struct { @@ -74,9 +62,11 @@ struct wlr_ext_workspace_handle_v1 { struct wl_signal destroy; } events; - struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces; + struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces - struct wl_list clients; + struct { + struct wl_list resources; // wlr_ext_workspace_v1_resource.link + }; }; struct wlr_ext_workspace_manager_v1 * @@ -114,5 +104,3 @@ 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 diff --git a/src/fetch/client.h b/src/fetch/client.h index 49499f95..22db0296 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -1,5 +1,5 @@ bool check_hit_no_border(Client *c) { - int i; + int32_t i; bool hit_no_border = false; if (!render_border) { hit_no_border = true; @@ -18,7 +18,7 @@ bool check_hit_no_border(Client *c) { return hit_no_border; } Client *termforwin(Client *w) { - Client *c; + Client *c = NULL; if (!w->pid || w->isterm || w->noswallow) return NULL; @@ -37,14 +37,25 @@ Client *get_client_by_id_or_title(const char *arg_id, const char *arg_title) { const char *appid, *title; Client *c = NULL; wl_list_for_each(c, &clients, link) { - if (c->mon != selmon) { + if (!scratchpad_cross_monitor && c->mon != selmon) { continue; } - if (!(appid = client_get_appid(c))) + if (c->swallowedby) { + appid = client_get_appid(c->swallowedby); + title = client_get_title(c->swallowedby); + } else { + appid = client_get_appid(c); + title = client_get_title(c); + } + + if (!appid) { appid = broken; - if (!(title = client_get_title(c))) + } + + if (!title) { title = broken; + } if (arg_id && strncmp(arg_id, "none", 4) == 0) arg_id = NULL; @@ -63,16 +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, struct wlr_box geom, int offsetx, - int offsety) { +setclient_coordinate_center(Client *c, struct wlr_box geom, int32_t offsetx, + int32_t offsety) { struct wlr_box tempbox; - int offset = 0; - int len = 0; + int32_t offset = 0; + int32_t len = 0; Monitor *m = c->mon ? c->mon : selmon; - unsigned int cbw = check_hit_no_border(c) ? c->bw : 0; + uint32_t cbw = check_hit_no_border(c) ? c->bw : 0; - if (!c->no_force_center) { + if (!c->no_force_center && m) { tempbox.x = m->w.x + (m->w.width - geom.width) / 2; tempbox.y = m->w.y + (m->w.height - geom.height) / 2; } else { @@ -84,7 +95,7 @@ setclient_coordinate_center(Client *c, struct wlr_box geom, int offsetx, tempbox.height = geom.height; if (offsetx != 0) { - len = (m->w.width - c->geom.width - 2 * m->gappoh) / 2; + len = (m->w.width - tempbox.width - 2 * m->gappoh) / 2; offset = len * (offsetx / 100.0); tempbox.x += offset; @@ -97,7 +108,7 @@ setclient_coordinate_center(Client *c, struct wlr_box geom, int offsetx, } } if (offsety != 0) { - len = (m->w.height - c->geom.height - 2 * m->gappov) / 2; + len = (m->w.height - tempbox.height - 2 * m->gappov) / 2; offset = len * (offsety / 100.0); tempbox.y += offset; @@ -121,15 +132,16 @@ static bool is_window_rule_matches(const ConfigWinRule *r, const char *appid, regex_match(r->title, title)); } -Client *center_select(Monitor *m) { +Client *center_tiled_select(Monitor *m) { Client *c = NULL; Client *target_c = NULL; - long int mini_distance = -1; - int dirx, diry; - long int distance; + int64_t mini_distance = -1; + int32_t dirx, diry; + int64_t distance; wl_list_for_each(c, &clients, link) { - if (c && VISIBLEON(c, m) && client_surface(c)->mapped && - !c->isfloating && !client_is_unmanaged(c)) { + if (c && VISIBLEON(c, m) && ISSCROLLTILED(c) && + client_surface(c)->mapped && !c->isfloating && + !client_is_unmanaged(c)) { dirx = c->geom.x + c->geom.width / 2 - (m->w.x + m->w.width / 2); diry = c->geom.y + c->geom.height / 2 - (m->w.y + m->w.height / 2); distance = dirx * dirx + diry * diry; @@ -142,15 +154,15 @@ Client *center_select(Monitor *m) { return target_c; } Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating, - bool align) { - Client *c; + bool ignore_align) { + Client *c = NULL; Client **tempClients = NULL; // 初始化为 NULL - int last = -1; + int32_t last = -1; // 第一次遍历,计算客户端数量 wl_list_for_each(c, &clients, link) { if (c && (findfloating || !c->isfloating) && !c->isunglobal && - (focus_cross_monitor || c->mon == selmon) && + (focus_cross_monitor || c->mon == tc->mon) && (c->tags & c->mon->tagset[c->mon->seltags])) { last++; } @@ -171,39 +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 && - (focus_cross_monitor || c->mon == selmon) && + (focus_cross_monitor || c->mon == tc->mon) && (c->tags & c->mon->tagset[c->mon->seltags])) { last++; tempClients[last] = c; } } - int sel_x = tc->geom.x; - int sel_y = tc->geom.y; - long long int distance = LLONG_MAX; + int32_t sel_x = tc->geom.x; + int32_t sel_y = tc->geom.y; + int64_t distance = LLONG_MAX; + int64_t same_monitor_distance = LLONG_MAX; Client *tempFocusClients = NULL; + Client *tempSameMonitorFocusClients = NULL; switch (arg->i) { case UP: - for (int _i = 0; _i <= last; _i++) { - if (tempClients[_i]->geom.y < sel_y && - tempClients[_i]->geom.x == sel_x) { - int dis_x = tempClients[_i]->geom.x - sel_x; - int dis_y = tempClients[_i]->geom.y - sel_y; - long long int tmp_distance = - dis_x * dis_x + dis_y * dis_y; // 计算距离 - if (tmp_distance < distance) { - distance = tmp_distance; - tempFocusClients = tempClients[_i]; - } - } - } - if (!tempFocusClients && !align) { - for (int _i = 0; _i <= last; _i++) { - if (tempClients[_i]->geom.y < sel_y) { - int dis_x = tempClients[_i]->geom.x - sel_x; - int dis_y = tempClients[_i]->geom.y - sel_y; - long long int tmp_distance = + if (!ignore_align) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.y < sel_y && + tempClients[_i]->geom.x == sel_x && + tempClients[_i]->mon == tc->mon) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 if (tmp_distance < distance) { distance = tmp_distance; @@ -212,27 +215,35 @@ 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) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = + dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + if (tempClients[_i]->mon == tc->mon && + tmp_distance < same_monitor_distance) { + same_monitor_distance = tmp_distance; + tempSameMonitorFocusClients = tempClients[_i]; + } + } + } + } break; case DOWN: - for (int _i = 0; _i <= last; _i++) { - if (tempClients[_i]->geom.y > sel_y && - tempClients[_i]->geom.x == sel_x) { - int dis_x = tempClients[_i]->geom.x - sel_x; - int dis_y = tempClients[_i]->geom.y - sel_y; - long long int tmp_distance = - dis_x * dis_x + dis_y * dis_y; // 计算距离 - if (tmp_distance < distance) { - distance = tmp_distance; - tempFocusClients = tempClients[_i]; - } - } - } - if (!tempFocusClients && !align) { - for (int _i = 0; _i <= last; _i++) { - if (tempClients[_i]->geom.y > sel_y) { - int dis_x = tempClients[_i]->geom.x - sel_x; - int dis_y = tempClients[_i]->geom.y - sel_y; - long long int tmp_distance = + if (!ignore_align) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.y > sel_y && + tempClients[_i]->geom.x == sel_x && + tempClients[_i]->mon == tc->mon) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 if (tmp_distance < distance) { distance = tmp_distance; @@ -241,27 +252,35 @@ 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) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = + dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + if (tempClients[_i]->mon == tc->mon && + tmp_distance < same_monitor_distance) { + same_monitor_distance = tmp_distance; + tempSameMonitorFocusClients = tempClients[_i]; + } + } + } + } break; case LEFT: - for (int _i = 0; _i <= last; _i++) { - if (tempClients[_i]->geom.x < sel_x && - tempClients[_i]->geom.y == sel_y) { - int dis_x = tempClients[_i]->geom.x - sel_x; - int dis_y = tempClients[_i]->geom.y - sel_y; - long long int tmp_distance = - dis_x * dis_x + dis_y * dis_y; // 计算距离 - if (tmp_distance < distance) { - distance = tmp_distance; - tempFocusClients = tempClients[_i]; - } - } - } - if (!tempFocusClients && !align) { - for (int _i = 0; _i <= last; _i++) { - if (tempClients[_i]->geom.x < sel_x) { - int dis_x = tempClients[_i]->geom.x - sel_x; - int dis_y = tempClients[_i]->geom.y - sel_y; - long long int tmp_distance = + if (!ignore_align) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x < sel_x && + tempClients[_i]->geom.y == sel_y && + tempClients[_i]->mon == tc->mon) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 if (tmp_distance < distance) { distance = tmp_distance; @@ -270,32 +289,59 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating, } } } - break; - case RIGHT: - for (int _i = 0; _i <= last; _i++) { - if (tempClients[_i]->geom.x > sel_x && - tempClients[_i]->geom.y == sel_y) { - int dis_x = tempClients[_i]->geom.x - sel_x; - int dis_y = tempClients[_i]->geom.y - sel_y; - long long int tmp_distance = - dis_x * dis_x + dis_y * dis_y; // 计算距离 - if (tmp_distance < distance) { - distance = tmp_distance; - tempFocusClients = tempClients[_i]; - } - } - } - if (!tempFocusClients && !align) { - for (int _i = 0; _i <= last; _i++) { - if (tempClients[_i]->geom.x > sel_x) { - int dis_x = tempClients[_i]->geom.x - sel_x; - int dis_y = tempClients[_i]->geom.y - sel_y; - long long int tmp_distance = + if (!tempFocusClients) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x < sel_x) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 if (tmp_distance < distance) { distance = tmp_distance; tempFocusClients = tempClients[_i]; } + if (tempClients[_i]->mon == tc->mon && + tmp_distance < same_monitor_distance) { + same_monitor_distance = tmp_distance; + tempSameMonitorFocusClients = tempClients[_i]; + } + } + } + } + break; + case RIGHT: + if (!ignore_align) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x > sel_x && + tempClients[_i]->geom.y == sel_y && + tempClients[_i]->mon == tc->mon) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = + dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + } + if (!tempFocusClients) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x > sel_x) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = + dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + if (tempClients[_i]->mon == tc->mon && + tmp_distance < same_monitor_distance) { + same_monitor_distance = tmp_distance; + tempSameMonitorFocusClients = tempClients[_i]; + } } } } @@ -303,7 +349,11 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating, } free(tempClients); // 释放内存 - return tempFocusClients; + if (tempSameMonitorFocusClients) { + return tempSameMonitorFocusClients; + } else { + return tempFocusClients; + } } Client *direction_select(const Arg *arg) { @@ -313,20 +363,20 @@ Client *direction_select(const Arg *arg) { if (!tc) return NULL; - if (tc && (tc->isfullscreen || tc->ismaxmizescreen)) { - // 不支持全屏窗口的焦点切换 + if (tc && (tc->isfullscreen || tc->ismaximizescreen) && + (!is_scroller_layout(selmon) || tc->isfloating)) { return NULL; } - return find_client_by_direction(tc, arg, true, false); + return find_client_by_direction( + tc, arg, true, is_scroller_layout(selmon) && !selmon->isoverview); } /* We probably should change the name of this, it sounds like * will focus the topmost client of this mon, when actually will * only return that client */ -Client * // 0.5 -focustop(Monitor *m) { - Client *c; +Client *focustop(Monitor *m) { + Client *c = NULL; wl_list_for_each(c, &fstack, flink) { if (c->iskilling || c->isunglobal) continue; @@ -335,3 +385,66 @@ focustop(Monitor *m) { } return NULL; } + +Client *get_next_stack_client(Client *c, bool reverse) { + if (!c || !c->mon) + return NULL; + + Client *next = NULL; + if (reverse) { + wl_list_for_each_reverse(next, &c->link, link) { + if (&next->link == &clients) + continue; /* wrap past the sentinel node */ + + if (next->isunglobal) + continue; + + if (next != c && next->mon && VISIBLEON(next, c->mon)) + return next; + } + } else { + wl_list_for_each(next, &c->link, link) { + if (&next->link == &clients) + continue; /* wrap past the sentinel node */ + + if (next->isunglobal) + continue; + + if (next != c && next->mon && VISIBLEON(next, c->mon)) + return next; + } + } + return NULL; +} + +float *get_border_color(Client *c) { + + if (c->mon != selmon) { + return bordercolor; + } else if (c->isurgent) { + return urgentcolor; + } else if (c->is_in_scratchpad && selmon && c == selmon->sel) { + return scratchpadcolor; + } else if (c->isglobal && selmon && c == selmon->sel) { + return globalcolor; + } else if (c->isoverlay && selmon && c == selmon->sel) { + return overlaycolor; + } else if (c->ismaximizescreen && selmon && c == selmon->sel) { + return maximizescreencolor; + } else if (selmon && c == selmon->sel) { + return focuscolor; + } else { + return bordercolor; + } +} + +int32_t is_single_bit_set(uint32_t x) { return x && !(x & (x - 1)); } + +bool client_only_in_one_tag(Client *c) { + uint32_t masked = c->tags & TAGMASK; + if (is_single_bit_set(masked)) { + return true; + } else { + return false; + } +} \ No newline at end of file diff --git a/src/fetch/common.h b/src/fetch/common.h index 6f7b0764..58e69dc1 100644 --- a/src/fetch/common.h +++ b/src/fetch/common.h @@ -1,5 +1,5 @@ pid_t getparentprocess(pid_t p) { - unsigned int v = 0; + uint32_t v = 0; FILE *f; char buf[256]; @@ -19,36 +19,22 @@ pid_t getparentprocess(pid_t p) { return (pid_t)v; } -int isdescprocess(pid_t p, pid_t c) { +int32_t isdescprocess(pid_t p, pid_t c) { while (p != c && c != 0) c = getparentprocess(c); - return (int)c; + return (int32_t)c; } -char *get_autostart_path(char *autostart_path, unsigned int buf_size) { - const char *mangoconfig = getenv("MANGOCONFIG"); +void get_layout_abbr(char *abbr, const char *full_name) { + // 清空输出缓冲区 + abbr[0] = '\0'; - if (mangoconfig && mangoconfig[0] != '\0') { - snprintf(autostart_path, buf_size, "%s/autostart.sh", mangoconfig); - } else { - const char *homedir = getenv("HOME"); - if (!homedir) { - fprintf(stderr, "Error: HOME environment variable not set.\n"); - return NULL; - } - snprintf(autostart_path, buf_size, "%s/.config/mango/autostart.sh", - homedir); - } - - return autostart_path; -} - -char *get_layout_abbr(const char *full_name) { // 1. 尝试在映射表中查找 - for (int i = 0; layout_mappings[i].full_name != NULL; i++) { + for (int32_t i = 0; layout_mappings[i].full_name != NULL; i++) { if (strcmp(full_name, layout_mappings[i].full_name) == 0) { - return strdup(layout_mappings[i].abbr); + strcpy(abbr, layout_mappings[i].abbr); + return; } } @@ -56,48 +42,39 @@ char *get_layout_abbr(const char *full_name) { const char *open = strrchr(full_name, '('); const char *close = strrchr(full_name, ')'); if (open && close && close > open) { - unsigned int len = close - open - 1; + uint32_t len = close - open - 1; if (len > 0 && len <= 4) { - char *abbr = malloc(len + 1); - if (abbr) { - // 提取并转换为小写 - for (unsigned int j = 0; j < len; j++) { - abbr[j] = tolower(open[j + 1]); - } - abbr[len] = '\0'; - return abbr; + // 提取并转换为小写 + for (uint32_t j = 0; j < len; j++) { + abbr[j] = tolower(open[j + 1]); } + abbr[len] = '\0'; + return; } } // 3. 提取前2-3个字母并转换为小写 - char *abbr = malloc(4); - if (abbr) { - unsigned int j = 0; - for (unsigned int i = 0; full_name[i] != '\0' && j < 3; i++) { - if (isalpha(full_name[i])) { - abbr[j++] = tolower(full_name[i]); - } + uint32_t j = 0; + for (uint32_t i = 0; full_name[i] != '\0' && j < 3; i++) { + if (isalpha(full_name[i])) { + abbr[j++] = tolower(full_name[i]); } - abbr[j] = '\0'; + } + abbr[j] = '\0'; - // 确保至少2个字符 - if (j >= 2) - return abbr; - free(abbr); + // 确保至少2个字符 + if (j >= 2) { + return; } // 4. 回退方案:使用首字母小写 - char *fallback = malloc(3); - if (fallback) { - fallback[0] = tolower(full_name[0]); - fallback[1] = full_name[1] ? tolower(full_name[1]) : '\0'; - fallback[2] = '\0'; - return fallback; + if (j == 1) { + abbr[1] = full_name[1] ? tolower(full_name[1]) : '\0'; + abbr[2] = '\0'; + } else { + // 5. 最终回退:返回 "xx" + strcpy(abbr, "xx"); } - - // 5. 最终回退:返回 "xx" - return strdup("xx"); } void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, @@ -106,30 +83,41 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, struct wlr_surface *surface = NULL; Client *c = NULL; LayerSurface *l = NULL; - int layer; + int32_t layer; for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) { - // ignore text-input layer - if (layer == LyrIMPopup) - continue; - if (layer == LyrFadeOut) continue; if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) continue; + if (!node->enabled) + continue; + if (node->type == WLR_SCENE_NODE_BUFFER) surface = wlr_scene_surface_try_from_buffer( wlr_scene_buffer_from_node(node)) ->surface; - /* Walk the tree to find a node that knows the client */ - for (pnode = node; pnode && !c; pnode = &pnode->parent->node) - c = pnode->data; - if (c && c->type == LayerShell) { + else if (node->type == WLR_SCENE_NODE_RECT) { + surface = NULL; + break; + } + + /* start from the topmost layer, + find a sureface that can be focused by pointer, + impopup neither a client nor a layer surface.*/ + if (layer == LyrIMPopup) { c = NULL; - l = pnode->data; + l = NULL; + } else { + for (pnode = node; pnode && !c; pnode = &pnode->parent->node) + c = pnode->data; + if (c && c->type == LayerShell) { + c = NULL; + l = pnode->data; + } } } diff --git a/src/fetch/monitor.h b/src/fetch/monitor.h index 90581280..47a5b824 100644 --- a/src/fetch/monitor.h +++ b/src/fetch/monitor.h @@ -17,26 +17,54 @@ Monitor *dirtomon(enum wlr_direction dir) { bool is_scroller_layout(Monitor *m) { - if (!m->pertag->curtag && - strcmp(m->pertag->ltidxs[m->pertag->prevtag]->name, "scroller") == 0) + if (m->pertag->ltidxs[m->pertag->curtag]->id == SCROLLER) return true; - if (!m->pertag->curtag && - strcmp(m->pertag->ltidxs[m->pertag->prevtag]->name, - "vertical_scroller") == 0) + if (m->pertag->ltidxs[m->pertag->curtag]->id == VERTICAL_SCROLLER) return true; - if (strcmp(m->pertag->ltidxs[m->pertag->curtag]->name, "scroller") == 0) - return true; - if (strcmp(m->pertag->ltidxs[m->pertag->curtag]->name, - "vertical_scroller") == 0) - return true; return false; } +uint32_t get_tag_status(uint32_t tag, Monitor *m) { + Client *c = NULL; + uint32_t status = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon == m && c->tags & 1 << (tag - 1) & TAGMASK) { + if (c->isurgent) { + status = 2; + break; + } + status = 1; + } + } + return status; +} + +uint32_t get_tags_first_tag_num(uint32_t source_tags) { + uint32_t i, tag; + tag = 0; + + if (!source_tags) { + return selmon->pertag->curtag; + } + + for (i = 0; !(tag & 1) && source_tags != 0 && i < LENGTH(tags); i++) { + tag = source_tags >> i; + } + + if (i == 1) { + return 1; + } else if (i > 9) { + return 9; + } else { + return i; + } +} + // 获取tags中最前面的tag的tagmask -unsigned int get_tags_first_tag(unsigned int source_tags) { - unsigned int i, tag; +uint32_t get_tags_first_tag(uint32_t source_tags) { + uint32_t i, tag; tag = 0; if (!source_tags) { diff --git a/src/layout/arrange.h b/src/layout/arrange.h new file mode 100644 index 00000000..d668f309 --- /dev/null +++ b/src/layout/arrange.h @@ -0,0 +1,697 @@ +void set_size_per(Monitor *m, Client *c) { + Client *fc = NULL; + bool found = false; + + if (!m || !c) + return; + + wl_list_for_each(fc, &clients, link) { + if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) { + c->master_mfact_per = fc->master_mfact_per; + c->master_inner_per = fc->master_inner_per; + c->stack_inner_per = fc->stack_inner_per; + found = true; + break; + } + } + + if (!found) { + c->master_mfact_per = m->pertag->mfacts[m->pertag->curtag]; + c->master_inner_per = 1.0f; + c->stack_inner_per = 1.0f; + } +} + +void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, + int32_t type) { + Client *tc = NULL; + float delta_x, delta_y; + Client *next = NULL; + Client *prev = NULL; + Client *nextnext = NULL; + Client *prevprev = NULL; + struct wl_list *node; + bool begin_find_nextnext = false; + bool begin_find_prevprev = false; + + // 从当前节点的下一个开始遍历 + for (node = grabc->link.next; node != &clients; node = node->next) { + tc = wl_container_of(node, tc, link); + if (begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) { + nextnext = tc; + break; + } + + if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && + ISTILED(tc)) { // 根据你的实际字段名调整 + next = tc; + begin_find_nextnext = true; + continue; + } + } + + // 从当前节点的上一个开始遍历 + for (node = grabc->link.prev; node != &clients; node = node->prev) { + tc = wl_container_of(node, tc, link); + + if (begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) { + prevprev = tc; + break; + } + + if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && + ISTILED(tc)) { // 根据你的实际字段名调整 + prev = tc; + begin_find_prevprev = true; + continue; + } + } + + if (!start_drag_window && isdrag) { + drag_begin_cursorx = cursor->x; + drag_begin_cursory = cursor->y; + start_drag_window = true; + // 记录初始状态 + grabc->old_master_mfact_per = grabc->master_mfact_per; + grabc->old_master_inner_per = grabc->master_inner_per; + grabc->old_stack_inner_per = grabc->stack_inner_per; + grabc->cursor_in_upper_half = + cursor->y < grabc->geom.y + grabc->geom.height / 2; + grabc->cursor_in_left_half = + cursor->x < grabc->geom.x + grabc->geom.width / 2; + // 记录初始几何信息 + grabc->drag_begin_geom = grabc->geom; + } else { + // 计算相对于屏幕尺寸的比例变化 + if (isdrag) { + + offsetx = cursor->x - drag_begin_cursorx; + offsety = cursor->y - drag_begin_cursory; + } else { + grabc->old_master_mfact_per = grabc->master_mfact_per; + grabc->old_master_inner_per = grabc->master_inner_per; + grabc->old_stack_inner_per = grabc->stack_inner_per; + grabc->drag_begin_geom = grabc->geom; + grabc->cursor_in_upper_half = true; + grabc->cursor_in_left_half = false; + } + + if (grabc->ismaster) { + delta_x = (float)(offsetx) * (grabc->old_master_mfact_per) / + grabc->drag_begin_geom.width; + delta_y = (float)(offsety) * (grabc->old_master_inner_per) / + grabc->drag_begin_geom.height; + } else { + delta_x = (float)(offsetx) * (1 - grabc->old_master_mfact_per) / + grabc->drag_begin_geom.width; + delta_y = (float)(offsety) * (grabc->old_stack_inner_per) / + grabc->drag_begin_geom.height; + } + bool moving_up; + bool moving_down; + + if (!isdrag) { + moving_up = offsety < 0 ? true : false; + moving_down = offsety > 0 ? true : false; + } else { + moving_up = cursor->y < drag_begin_cursory; + moving_down = cursor->y > drag_begin_cursory; + } + + if (grabc->ismaster && !prev) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } else if (grabc->ismaster && next && !next->ismaster) { + if (moving_up) { + delta_y = fabsf(delta_y); + } else { + delta_y = -fabsf(delta_y); + } + } else if (!grabc->ismaster && prev && prev->ismaster) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } else if (!grabc->ismaster && !next) { + if (moving_up) { + delta_y = fabsf(delta_y); + } else { + delta_y = -fabsf(delta_y); + } + } else if (type == CENTER_TILE && !grabc->ismaster && !nextnext) { + if (moving_up) { + delta_y = fabsf(delta_y); + } else { + delta_y = -fabsf(delta_y); + } + } else if (type == CENTER_TILE && !grabc->ismaster && prevprev && + prevprev->ismaster) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } else if ((grabc->cursor_in_upper_half && moving_up) || + (!grabc->cursor_in_upper_half && moving_down)) { + // 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度 + delta_y = fabsf(delta_y); + delta_y = delta_y * 2; + } else { + // 其他情况 → 减小高度 + delta_y = -fabsf(delta_y); + delta_y = delta_y * 2; + } + + if (!grabc->ismaster && grabc->isleftstack && type == CENTER_TILE) { + delta_x = delta_x * -1.0f; + } + + if (grabc->ismaster && type == CENTER_TILE && + grabc->cursor_in_left_half) { + delta_x = delta_x * -1.0f; + } + + if (grabc->ismaster && type == CENTER_TILE) { + delta_x = delta_x * 2; + } + + if (type == RIGHT_TILE) { + delta_x = delta_x * -1.0f; + } + + // 直接设置新的比例,基于初始值 + 变化量 + float new_master_mfact_per = grabc->old_master_mfact_per + delta_x; + float new_master_inner_per = grabc->old_master_inner_per + delta_y; + float new_stack_inner_per = grabc->old_stack_inner_per + delta_y; + + // 应用限制,确保比例在合理范围内 + new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per)); + new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per)); + new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per)); + + // 应用到所有平铺窗口 + wl_list_for_each(tc, &clients, link) { + if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) { + tc->master_mfact_per = new_master_mfact_per; + } + } + + grabc->master_inner_per = new_master_inner_per; + grabc->stack_inner_per = new_stack_inner_per; + + if (!isdrag) { + arrange(grabc->mon, false, false); + return; + } + + if (last_apply_drap_time == 0 || + time - last_apply_drap_time > drag_refresh_interval) { + arrange(grabc->mon, false, false); + last_apply_drap_time = time; + } + } +} + +void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, int32_t type) { + Client *tc = NULL; + float delta_x, delta_y; + Client *next = NULL; + Client *prev = NULL; + struct wl_list *node; + + // 从当前节点的下一个开始遍历 + for (node = grabc->link.next; node != &clients; node = node->next) { + tc = wl_container_of(node, tc, link); + + if (VISIBLEON(tc, grabc->mon) && + ISTILED(tc)) { // 根据你的实际字段名调整 + next = tc; + break; + } + } + + // 从当前节点的上一个开始遍历 + for (node = grabc->link.prev; node != &clients; node = node->prev) { + tc = wl_container_of(node, tc, link); + + if (VISIBLEON(tc, grabc->mon) && + ISTILED(tc)) { // 根据你的实际字段名调整 + prev = tc; + break; + } + } + + if (!start_drag_window && isdrag) { + drag_begin_cursorx = cursor->x; + drag_begin_cursory = cursor->y; + start_drag_window = true; + + // 记录初始状态 + grabc->old_master_mfact_per = grabc->master_mfact_per; + grabc->old_master_inner_per = grabc->master_inner_per; + grabc->old_stack_inner_per = grabc->stack_inner_per; + grabc->cursor_in_upper_half = + cursor->y < grabc->geom.y + grabc->geom.height / 2; + grabc->cursor_in_left_half = + cursor->x < grabc->geom.x + grabc->geom.width / 2; + // 记录初始几何信息 + grabc->drag_begin_geom = grabc->geom; + } else { + // 计算相对于屏幕尺寸的比例变化 + // 计算相对于屏幕尺寸的比例变化 + if (isdrag) { + + offsetx = cursor->x - drag_begin_cursorx; + offsety = cursor->y - drag_begin_cursory; + } else { + grabc->old_master_mfact_per = grabc->master_mfact_per; + grabc->old_master_inner_per = grabc->master_inner_per; + grabc->old_stack_inner_per = grabc->stack_inner_per; + grabc->drag_begin_geom = grabc->geom; + grabc->cursor_in_upper_half = true; + grabc->cursor_in_left_half = false; + } + + if (grabc->ismaster) { + // 垂直版本:左右移动调整高度比例,上下移动调整宽度比例 + delta_x = (float)(offsetx) * (grabc->old_master_inner_per) / + grabc->drag_begin_geom.width; + delta_y = (float)(offsety) * (grabc->old_master_mfact_per) / + grabc->drag_begin_geom.height; + } else { + delta_x = (float)(offsetx) * (grabc->old_stack_inner_per) / + grabc->drag_begin_geom.width; + delta_y = (float)(offsety) * (1 - grabc->old_master_mfact_per) / + grabc->drag_begin_geom.height; + } + + bool moving_left; + bool moving_right; + + if (!isdrag) { + moving_left = offsetx < 0 ? true : false; + moving_right = offsetx > 0 ? true : false; + } else { + moving_left = cursor->x < drag_begin_cursorx; + moving_right = cursor->x > drag_begin_cursorx; + } + + // 调整主区域和栈区域的高度比例(垂直分割) + if (grabc->ismaster && !prev) { + if (moving_left) { + delta_x = -fabsf(delta_x); // 向上移动减少主区域高度 + } else { + delta_x = fabsf(delta_x); // 向下移动增加主区域高度 + } + } else if (grabc->ismaster && next && !next->ismaster) { + if (moving_left) { + delta_x = fabsf(delta_x); // 向上移动增加主区域高度 + } else { + delta_x = -fabsf(delta_x); // 向下移动减少主区域高度 + } + } else if (!grabc->ismaster && prev && prev->ismaster) { + if (moving_left) { + delta_x = -fabsf(delta_x); // 向上移动减少栈区域高度 + } else { + delta_x = fabsf(delta_x); // 向下移动增加栈区域高度 + } + } else if (!grabc->ismaster && !next) { + if (moving_left) { + delta_x = fabsf(delta_x); // 向上移动增加栈区域高度 + } else { + delta_x = -fabsf(delta_x); // 向下移动减少栈区域高度 + } + } else if ((grabc->cursor_in_left_half && moving_left) || + (!grabc->cursor_in_left_half && moving_right)) { + // 光标在窗口左侧且向左移动,或在窗口右侧且向右移动 → 增加宽度 + delta_x = fabsf(delta_x); + delta_x = delta_x * 2; + } else { + // 其他情况 → 减小宽度 + delta_x = -fabsf(delta_x); + delta_x = delta_x * 2; + } + + // 直接设置新的比例,基于初始值 + 变化量 + float new_master_mfact_per = grabc->old_master_mfact_per + + delta_y; // 垂直:delta_y调整主区域高度 + float new_master_inner_per = grabc->old_master_inner_per + + delta_x; // 垂直:delta_x调整主区域内部宽度 + float new_stack_inner_per = grabc->old_stack_inner_per + + delta_x; // 垂直:delta_x调整栈区域内部宽度 + + // 应用限制,确保比例在合理范围内 + new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per)); + new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per)); + new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per)); + + // 应用到所有平铺窗口 + wl_list_for_each(tc, &clients, link) { + if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) { + tc->master_mfact_per = new_master_mfact_per; + } + } + + grabc->master_inner_per = new_master_inner_per; + grabc->stack_inner_per = new_stack_inner_per; + + if (!isdrag) { + arrange(grabc->mon, false, false); + return; + } + + if (last_apply_drap_time == 0 || + time - last_apply_drap_time > drag_refresh_interval) { + arrange(grabc->mon, false, false); + last_apply_drap_time = time; + } + } +} + +void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time, bool isvertical) { + float delta_x, delta_y; + float new_scroller_proportion; + + if (grabc && grabc->mon->visible_tiling_clients == 1 && + !scroller_ignore_proportion_single) + return; + + if (!start_drag_window && isdrag) { + drag_begin_cursorx = cursor->x; + drag_begin_cursory = cursor->y; + start_drag_window = true; + + // 记录初始状态 + grabc->old_scroller_pproportion = grabc->scroller_proportion; + + grabc->cursor_in_left_half = + cursor->x < grabc->geom.x + grabc->geom.width / 2; + grabc->cursor_in_upper_half = + cursor->y < grabc->geom.y + grabc->geom.height / 2; + // 记录初始几何信息 + grabc->drag_begin_geom = grabc->geom; + } else { + // 计算相对于屏幕尺寸的比例变化 + // 计算相对于屏幕尺寸的比例变化 + if (isdrag) { + + offsetx = cursor->x - drag_begin_cursorx; + offsety = cursor->y - drag_begin_cursory; + } else { + grabc->old_master_mfact_per = grabc->master_mfact_per; + grabc->old_master_inner_per = grabc->master_inner_per; + grabc->old_stack_inner_per = grabc->stack_inner_per; + grabc->drag_begin_geom = grabc->geom; + grabc->old_scroller_pproportion = grabc->scroller_proportion; + grabc->cursor_in_upper_half = false; + grabc->cursor_in_left_half = false; + } + + delta_x = (float)(offsetx) * (grabc->old_scroller_pproportion) / + grabc->drag_begin_geom.width; + delta_y = (float)(offsety) * (grabc->old_scroller_pproportion) / + grabc->drag_begin_geom.height; + + bool moving_up; + bool moving_down; + bool moving_left; + bool moving_right; + + if (!isdrag) { + moving_up = offsety < 0 ? true : false; + moving_down = offsety > 0 ? true : false; + moving_left = offsetx < 0 ? true : false; + moving_right = offsetx > 0 ? true : false; + } else { + moving_up = cursor->y < drag_begin_cursory; + moving_down = cursor->y > drag_begin_cursory; + moving_left = cursor->x < drag_begin_cursorx; + moving_right = cursor->x > drag_begin_cursorx; + } + + if ((grabc->cursor_in_upper_half && moving_up) || + (!grabc->cursor_in_upper_half && moving_down)) { + // 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度 + delta_y = fabsf(delta_y); + } else { + // 其他情况 → 减小高度 + delta_y = -fabsf(delta_y); + } + + if ((grabc->cursor_in_left_half && moving_left) || + (!grabc->cursor_in_left_half && moving_right)) { + delta_x = fabsf(delta_x); + } else { + delta_x = -fabsf(delta_x); + } + + // 直接设置新的比例,基于初始值 + 变化量 + if (isvertical) { + new_scroller_proportion = grabc->old_scroller_pproportion + delta_y; + } else { + new_scroller_proportion = grabc->old_scroller_pproportion + delta_x; + } + + // 应用限制,确保比例在合理范围内 + new_scroller_proportion = + fmaxf(0.1f, fminf(1.0f, new_scroller_proportion)); + + grabc->scroller_proportion = new_scroller_proportion; + + if (!isdrag) { + arrange(grabc->mon, false, false); + return; + } + + if (last_apply_drap_time == 0 || + time - last_apply_drap_time > drag_refresh_interval) { + arrange(grabc->mon, false, false); + last_apply_drap_time = time; + } + } +} + +void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time) { + + if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen) + return; + + if (grabc->mon->isoverview) + return; + + const Layout *current_layout = + grabc->mon->pertag->ltidxs[grabc->mon->pertag->curtag]; + if (current_layout->id == TILE || current_layout->id == DECK || + current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE || + (current_layout->id == TGMIX && grabc->mon->visible_tiling_clients <= 3) + + ) { + resize_tile_master_horizontal(grabc, isdrag, offsetx, offsety, time, + current_layout->id); + } else if (current_layout->id == VERTICAL_TILE || + current_layout->id == VERTICAL_DECK) { + resize_tile_master_vertical(grabc, isdrag, offsetx, offsety, time, + current_layout->id); + } else if (current_layout->id == SCROLLER) { + resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, false); + } else if (current_layout->id == VERTICAL_SCROLLER) { + resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, true); + } +} + +void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num, + double total_left_stack_hight_percent, + double total_right_stack_hight_percent, + double total_stack_hight_percent, + double total_master_inner_percent, int32_t master_num, + int32_t stack_num) { + Client *c = NULL; + int32_t i = 0; + uint32_t stack_index = 0; + uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag]; + + if (m->pertag->ltidxs[m->pertag->curtag]->id != CENTER_TILE) { + + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, m) && ISTILED(c)) { + if (total_master_inner_percent > 0.0 && i < nmasters) { + c->ismaster = true; + c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f; + c->master_inner_per = + c->master_inner_per / total_master_inner_percent; + } else { + c->ismaster = false; + c->master_inner_per = + master_num > 0 ? 1.0f / master_num : 1.0f; + c->stack_inner_per = + total_stack_hight_percent + ? c->stack_inner_per / total_stack_hight_percent + : 1.0f; + } + i++; + } + } + } else { + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, m) && ISTILED(c)) { + if (total_master_inner_percent > 0.0 && i < nmasters) { + c->ismaster = true; + if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) { + c->stack_inner_per = + stack_num > 1 ? 1.0f / ((stack_num - 1) / 2) : 1.0f; + + } else { + c->stack_inner_per = + stack_num > 1 ? 2.0f / stack_num : 1.0f; + } + + c->master_inner_per = + c->master_inner_per / total_master_inner_percent; + } else { + stack_index = i - nmasters; + + c->ismaster = false; + c->master_inner_per = + master_num > 0 ? 1.0f / master_num : 1.0f; + if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) { + c->stack_inner_per = + total_right_stack_hight_percent + ? c->stack_inner_per / + total_right_stack_hight_percent + : 1.0f; + } else { + c->stack_inner_per = + total_left_stack_hight_percent + ? c->stack_inner_per / + total_left_stack_hight_percent + : 1.0f; + } + } + i++; + } + } + } +} + +void // 17 +arrange(Monitor *m, bool want_animation, bool from_view) { + Client *c = NULL; + double total_stack_inner_percent = 0; + double total_master_inner_percent = 0; + double total_right_stack_hight_percent = 0; + double total_left_stack_hight_percent = 0; + int32_t i = 0; + int32_t nmasters = 0; + int32_t stack_index = 0; + int32_t master_num = 0; + int32_t stack_num = 0; + + if (!m) + return; + + if (!m->wlr_output->enabled) + return; + m->visible_clients = 0; + m->visible_tiling_clients = 0; + m->visible_scroll_tiling_clients = 0; + + wl_list_for_each(c, &clients, link) { + + if (from_view && (c->isglobal || c->isunglobal)) { + set_size_per(m, c); + } + + if (c->mon == m && (c->isglobal || c->isunglobal)) { + c->tags = m->tagset[m->seltags]; + if (c->mon->sel == NULL) + focusclient(c, 0); + } + + if (VISIBLEON(c, m)) { + if (from_view && !client_only_in_one_tag(c)) { + set_size_per(m, c); + } + + if (!c->isunglobal) + m->visible_clients++; + + if (ISTILED(c)) { + m->visible_tiling_clients++; + } + + if (ISSCROLLTILED(c)) { + m->visible_scroll_tiling_clients++; + } + } + } + + nmasters = m->pertag->nmasters[m->pertag->curtag]; + + wl_list_for_each(c, &clients, link) { + if (c->iskilling) + continue; + + if (c->mon == m) { + if (VISIBLEON(c, m)) { + if (ISTILED(c)) { + + if (i < nmasters) { + master_num++; + total_master_inner_percent += c->master_inner_per; + } else { + stack_num++; + total_stack_inner_percent += c->stack_inner_per; + stack_index = i - nmasters; + if ((stack_index % 2) ^ + (m->visible_tiling_clients % 2 == 0)) { + c->isleftstack = false; + total_right_stack_hight_percent += + c->stack_inner_per; + } else { + c->isleftstack = true; + total_left_stack_hight_percent += + c->stack_inner_per; + } + } + + i++; + } + + set_arrange_visible(m, c, want_animation); + } else { + set_arrange_hidden(m, c, want_animation); + } + } + + if (c->mon == m && c->ismaximizescreen && !c->animation.tagouted && + !c->animation.tagouting && VISIBLEON(c, m)) { + reset_maximizescreen_size(c); + } + } + + reset_size_per_mon( + m, m->visible_tiling_clients, total_left_stack_hight_percent, + total_right_stack_hight_percent, total_stack_inner_percent, + total_master_inner_percent, master_num, stack_num); + + if (m->isoverview) { + overviewlayout.arrange(m); + } else { + m->pertag->ltidxs[m->pertag->curtag]->arrange(m); + } + + if (!start_drag_window) { + motionnotify(0, NULL, 0, 0, 0, 0); + checkidleinhibitor(NULL); + } + + printstatus(); +} diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index eb68ef19..a2f5e5c1 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -1,145 +1,19 @@ -void fibonacci(Monitor *mon, int s) { - unsigned int i = 0, n = 0, nx, ny, nw, nh; - Client *c; - unsigned int cur_gappih = enablegaps ? mon->gappih : 0; - unsigned int cur_gappiv = enablegaps ? mon->gappiv : 0; - unsigned int cur_gappoh = enablegaps ? mon->gappoh : 0; - unsigned int cur_gappov = enablegaps ? mon->gappov : 0; - - cur_gappih = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappih; - cur_gappiv = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappiv; - cur_gappoh = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappoh; - cur_gappov = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappov; - // Count visible clients - wl_list_for_each(c, &clients, link) if (VISIBLEON(c, mon) && ISTILED(c)) - n++; - - if (n == 0) - return; - - // Initial dimensions including outer gaps - nx = mon->w.x + cur_gappoh; - ny = mon->w.y + cur_gappov; - nw = mon->w.width - 2 * cur_gappoh; - nh = mon->w.height - 2 * cur_gappov; - - // First pass: calculate client geometries - wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, mon) || !ISTILED(c)) - continue; - - c->bw = mon->visible_tiling_clients == 1 && no_border_when_single && - smartgaps - ? 0 - : borderpx; - if ((i % 2 && nh / 2 > 2 * c->bw) || (!(i % 2) && nw / 2 > 2 * c->bw)) { - if (i < n - 1) { - if (i % 2) { - if (i == 1) { - nh = nh * mon->pertag->smfacts[mon->pertag->curtag]; - } else { - nh = (nh - cur_gappiv) / 2; - } - } else { - nw = (nw - cur_gappih) / 2; - } - - if ((i % 4) == 2 && !s) - nx += nw + cur_gappih; - else if ((i % 4) == 3 && !s) - ny += nh + cur_gappiv; - } - - if ((i % 4) == 0) { - if (s) - ny += nh + cur_gappiv; - else - ny -= nh + cur_gappiv; - } else if ((i % 4) == 1) - nx += nw + cur_gappih; - else if ((i % 4) == 2) - ny += nh + cur_gappiv; - else if ((i % 4) == 3) { - if (s) - nx += nw + cur_gappih; - else - nx -= nw + cur_gappih; - } - - if (i == 0) { - if (n != 1) - nw = (mon->w.width - 2 * cur_gappoh) * - mon->pertag->mfacts[mon->pertag->curtag]; - ny = mon->w.y + cur_gappov; - } else if (i == 1) { - nw = mon->w.width - 2 * cur_gappoh - nw - cur_gappih; - } else if (i == 2) { - nh = mon->w.height - 2 * cur_gappov - nh - cur_gappiv; - } - i++; - } - - c->geom = (struct wlr_box){.x = nx, .y = ny, .width = nw, .height = nh}; - } - - // Second pass: apply gaps between clients - wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, mon) || !ISTILED(c)) - continue; - - unsigned int right_gap = 0; - unsigned int bottom_gap = 0; - Client *nc; - - wl_list_for_each(nc, &clients, link) { - if (!VISIBLEON(nc, mon) || !ISTILED(nc)) - continue; - - if (c == nc) - continue; - - // Check for right neighbor - if (c->geom.y == nc->geom.y && - c->geom.x + c->geom.width == nc->geom.x) { - right_gap = cur_gappih; - } - - // Check for bottom neighbor - if (c->geom.x == nc->geom.x && - c->geom.y + c->geom.height == nc->geom.y) { - bottom_gap = cur_gappiv; - } - } - - resize(c, - (struct wlr_box){.x = c->geom.x, - .y = c->geom.y, - .width = c->geom.width - right_gap, - .height = c->geom.height - bottom_gap}, - 0); - } -} - -void dwindle(Monitor *mon) { fibonacci(mon, 1); } - -void spiral(Monitor *mon) { fibonacci(mon, 0); } - // 网格布局窗口大小和位置计算 void grid(Monitor *m) { - unsigned int i, n; - unsigned int cx, cy, cw, ch; - unsigned int dx; - unsigned int cols, rows, overcols; - Client *c; + int32_t i, n; + int32_t cx, cy, cw, ch; + int32_t dx; + int32_t cols, rows, overcols; + Client *c = NULL; n = 0; + int32_t target_gappo = + enablegaps ? m->isoverview ? overviewgappo : gappoh : 0; + int32_t target_gappi = + enablegaps ? m->isoverview ? overviewgappi : gappih : 0; + float single_width_ratio = m->isoverview ? 0.7 : 0.9; + float single_height_ratio = m->isoverview ? 0.8 : 0.9; - // 第一次遍历,计算 n 的值 - wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && !c->isunglobal && - ((m->isoverview && !client_should_ignore_focus(c)) || ISTILED(c))) { - n++; - } - } + n = m->isoverview ? m->visible_clients : m->visible_tiling_clients; if (n == 0) { return; // 没有需要处理的客户端,直接返回 @@ -147,19 +21,18 @@ void grid(Monitor *m) { if (n == 1) { wl_list_for_each(c, &clients, link) { - c->bw = m->visible_tiling_clients == 1 && no_border_when_single && - smartgaps - ? 0 - : borderpx; + + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && - ((m->isoverview && !client_should_ignore_focus(c)) || - ISTILED(c))) { - cw = (m->w.width - 2 * overviewgappo) * 0.7; - ch = (m->w.height - 2 * overviewgappo) * 0.8; + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + cw = (m->w.width - 2 * target_gappo) * single_width_ratio; + ch = (m->w.height - 2 * target_gappo) * single_height_ratio; c->geom.x = m->w.x + (m->w.width - cw) / 2; c->geom.y = m->w.y + (m->w.height - ch) / 2; - c->geom.width = cw - 2 * c->bw; - c->geom.height = ch - 2 * c->bw; + c->geom.width = cw; + c->geom.height = ch; resize(c, c->geom, 0); return; } @@ -167,28 +40,26 @@ void grid(Monitor *m) { } if (n == 2) { - cw = (m->w.width - 2 * overviewgappo - overviewgappi) / 2; - ch = (m->w.height - 2 * overviewgappo) * 0.65; + cw = (m->w.width - 2 * target_gappo - target_gappi) / 2; + ch = (m->w.height - 2 * target_gappo) * 0.65; i = 0; wl_list_for_each(c, &clients, link) { - c->bw = m->visible_tiling_clients == 1 && no_border_when_single && - smartgaps - ? 0 - : borderpx; + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && - ((m->isoverview && !client_should_ignore_focus(c)) || - ISTILED(c))) { + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { if (i == 0) { - c->geom.x = m->w.x + overviewgappo; - c->geom.y = m->w.y + (m->w.height - ch) / 2 + overviewgappo; - c->geom.width = cw - 2 * c->bw; - c->geom.height = ch - 2 * c->bw; + c->geom.x = m->w.x + target_gappo; + c->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo; + c->geom.width = cw; + c->geom.height = ch; resize(c, c->geom, 0); } else if (i == 1) { - c->geom.x = m->w.x + cw + overviewgappo + overviewgappi; - c->geom.y = m->w.y + (m->w.height - ch) / 2 + overviewgappo; - c->geom.width = cw - 2 * c->bw; - c->geom.height = ch - 2 * c->bw; + c->geom.x = m->w.x + cw + target_gappo + target_gappi; + c->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo; + c->geom.width = cw; + c->geom.height = ch; resize(c, c->geom, 0); } i++; @@ -206,34 +77,34 @@ void grid(Monitor *m) { rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols; // 计算每个客户端的高度和宽度 - ch = (m->w.height - 2 * overviewgappo - (rows - 1) * overviewgappi) / rows; - cw = (m->w.width - 2 * overviewgappo - (cols - 1) * overviewgappi) / cols; + ch = (m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows; + cw = (m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols; // 处理多余的列 overcols = n % cols; if (overcols) { - dx = (m->w.width - overcols * cw - (overcols - 1) * overviewgappi) / 2 - - overviewgappo; + dx = (m->w.width - overcols * cw - (overcols - 1) * target_gappi) / 2 - + target_gappo; } // 调整每个客户端的位置和大小 i = 0; wl_list_for_each(c, &clients, link) { - c->bw = - m->visible_tiling_clients == 1 && no_border_when_single && smartgaps - ? 0 - : borderpx; + + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && - ((m->isoverview && !client_should_ignore_focus(c)) || ISTILED(c))) { - cx = m->w.x + (i % cols) * (cw + overviewgappi); - cy = m->w.y + (i / cols) * (ch + overviewgappi); + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + cx = m->w.x + (i % cols) * (cw + target_gappi); + cy = m->w.y + (i / cols) * (ch + target_gappi); if (overcols && i >= n - overcols) { cx += dx; } - c->geom.x = cx + overviewgappo; - c->geom.y = cy + overviewgappo; - c->geom.width = cw - 2 * c->bw; - c->geom.height = ch - 2 * c->bw; + 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++; } @@ -241,23 +112,34 @@ void grid(Monitor *m) { } void deck(Monitor *m) { - unsigned int mw, my; - int i, n = 0; - Client *c; - unsigned int cur_gappih = enablegaps ? m->gappih : 0; - unsigned int cur_gappoh = enablegaps ? m->gappoh : 0; - unsigned int cur_gappov = enablegaps ? m->gappov : 0; + int32_t mw, my; + int32_t i, n = 0; + Client *c = NULL; + Client *fc = NULL; + float mfact; + + int32_t cur_gappih = enablegaps ? m->gappih : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; - wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && ISTILED(c)) n++; + n = m->visible_tiling_clients; + if (n == 0) return; + wl_list_for_each(fc, &clients, link) { + + if (VISIBLEON(fc, m) && ISTILED(fc)) + break; + } + // Calculate master width using mfact from pertag - float mfact = m->pertag ? m->pertag->mfacts[m->pertag->curtag] : m->mfact; + mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per + : m->pertag->mfacts[m->pertag->curtag]; // Calculate master width including outer gaps if (n > m->nmaster) @@ -270,6 +152,7 @@ void deck(Monitor *m) { if (!VISIBLEON(c, m) || !ISTILED(c)) continue; if (i < m->nmaster) { + c->master_mfact_per = mfact; // Master area clients resize( c, @@ -282,6 +165,7 @@ void deck(Monitor *m) { my += c->geom.height; } else { // Stack area clients + c->master_mfact_per = mfact; resize(c, (struct wlr_box){.x = m->w.x + mw + cur_gappoh + cur_gappih, .y = m->w.y + cur_gappov, @@ -296,33 +180,62 @@ void deck(Monitor *m) { } } +void horizontal_scroll_adjust_fullandmax(Client *c, + struct wlr_box *target_geom) { + Monitor *m = c->mon; + int32_t cur_gappih = enablegaps ? m->gappih : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; + + cur_gappih = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappih; + cur_gappoh = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh; + cur_gappov = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov; + + if (c->isfullscreen) { + target_geom->height = m->m.height; + target_geom->width = m->m.width; + target_geom->y = m->m.y; + return; + } + + if (c->ismaximizescreen) { + target_geom->height = m->w.height - 2 * cur_gappov; + target_geom->width = m->w.width - 2 * cur_gappoh; + target_geom->y = m->w.y + cur_gappov; + return; + } + + target_geom->height = m->w.height - 2 * cur_gappov; + target_geom->y = m->w.y + (m->w.height - target_geom->height) / 2; +} + // 滚动布局 void scroller(Monitor *m) { - unsigned int i, n; + int32_t i, n, j; + float single_proportion = 1.0; - Client *c, *root_client = NULL; + Client *c = NULL, *root_client = NULL; Client **tempClients = NULL; // 初始化为 NULL - n = 0; struct wlr_box target_geom; - int focus_client_index = 0; + int32_t focus_client_index = 0; bool need_scroller = false; - unsigned int cur_gappih = enablegaps ? m->gappih : 0; - unsigned int cur_gappoh = enablegaps ? m->gappoh : 0; - unsigned int cur_gappov = enablegaps ? m->gappov : 0; + int32_t cur_gappih = enablegaps ? m->gappih : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; - cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; - cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; - cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; + cur_gappih = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappih; + cur_gappoh = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh; + cur_gappov = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov; - unsigned int max_client_width = - m->w.width - 2 * scroller_structs - cur_gappih; + int32_t max_client_width = m->w.width - 2 * scroller_structs - cur_gappih; - // 第一次遍历,计算 n 的值 - wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && ISTILED(c)) { - n++; - } - } + n = m->visible_scroll_tiling_clients; if (n == 0) { return; // 没有需要处理的客户端,直接返回 @@ -336,19 +249,24 @@ void scroller(Monitor *m) { } // 第二次遍历,填充 tempClients - n = 0; + j = 0; wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && ISTILED(c)) { - tempClients[n] = c; - n++; + if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { + tempClients[j] = c; + j++; } } - if (n == 1) { + if (n == 1 && !scroller_ignore_proportion_single && + !tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) { c = tempClients[0]; + + single_proportion = c->scroller_proportion_single > 0.0f + ? c->scroller_proportion_single + : scroller_default_proportion_single; + target_geom.height = m->w.height - 2 * cur_gappov; - target_geom.width = - (m->w.width - 2 * cur_gappoh) * scroller_default_proportion_single; + target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion; target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; resize(c, target_geom, 0); @@ -356,15 +274,13 @@ void scroller(Monitor *m) { return; } - if (m->sel && !client_is_unmanaged(m->sel) && !m->sel->isfloating && - !m->sel->ismaxmizescreen && !m->sel->isfullscreen) { + if (m->sel && !client_is_unmanaged(m->sel) && ISSCROLLTILED(m->sel)) { root_client = m->sel; - } else if (m->prevsel && !client_is_unmanaged(m->prevsel) && - !m->prevsel->isfloating && !m->prevsel->ismaxmizescreen && - !m->prevsel->isfullscreen) { + } else if (m->prevsel && ISSCROLLTILED(m->prevsel) && + VISIBLEON(m->prevsel, m) && !client_is_unmanaged(m->prevsel)) { root_client = m->prevsel; } else { - root_client = center_select(m); + root_client = center_tiled_select(m); } if (!root_client) { @@ -375,8 +291,7 @@ void scroller(Monitor *m) { for (i = 0; i < n; i++) { c = tempClients[i]; if (root_client == c) { - if (!c->is_pending_open_animation && - c->geom.x >= m->w.x + scroller_structs && + if (c->geom.x >= m->w.x + scroller_structs && c->geom.x + c->geom.width <= m->w.x + m->w.width - scroller_structs) { need_scroller = false; @@ -388,16 +303,31 @@ void scroller(Monitor *m) { } } + if (n == 1 && scroller_ignore_proportion_single) { + need_scroller = true; + } + + if (start_drag_window) + need_scroller = false; + target_geom.height = m->w.height - 2 * cur_gappov; target_geom.width = max_client_width * c->scroller_proportion; target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; - - if (need_scroller) { + horizontal_scroll_adjust_fullandmax(tempClients[focus_client_index], + &target_geom); + if (tempClients[focus_client_index]->isfullscreen) { + target_geom.x = m->m.x; + resize(tempClients[focus_client_index], target_geom, 0); + } else if (tempClients[focus_client_index]->ismaximizescreen) { + target_geom.x = m->w.x + cur_gappoh; + resize(tempClients[focus_client_index], target_geom, 0); + } else if (need_scroller) { if (scroller_focus_center || ((!m->prevsel || - (m->prevsel->scroller_proportion * max_client_width) + - (root_client->scroller_proportion * max_client_width) > - m->w.width - 2 * scroller_structs - cur_gappih) && + (ISSCROLLTILED(m->prevsel) && + (m->prevsel->scroller_proportion * max_client_width) + + (root_client->scroller_proportion * max_client_width) > + m->w.width - 2 * scroller_structs - cur_gappih)) && scroller_prefer_center)) { target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; } else { @@ -417,14 +347,17 @@ void scroller(Monitor *m) { for (i = 1; i <= focus_client_index; i++) { c = tempClients[focus_client_index - i]; target_geom.width = max_client_width * c->scroller_proportion; + horizontal_scroll_adjust_fullandmax(c, &target_geom); target_geom.x = tempClients[focus_client_index - i + 1]->geom.x - cur_gappih - target_geom.width; + resize(c, target_geom, 0); } for (i = 1; i < n - focus_client_index; i++) { c = tempClients[focus_client_index + i]; target_geom.width = max_client_width * c->scroller_proportion; + horizontal_scroll_adjust_fullandmax(c, &target_geom); target_geom.x = tempClients[focus_client_index + i - 1]->geom.x + cur_gappih + tempClients[focus_client_index + i - 1]->geom.width; @@ -434,39 +367,310 @@ void scroller(Monitor *m) { free(tempClients); // 最后释放内存 } -void tile(Monitor *m) { - unsigned int i, n = 0, h, r, ie = enablegaps, mw, my, ty; - Client *c; +void center_tile(Monitor *m) { + int32_t i, n = 0, h, r, ie = enablegaps, mw, mx, my, oty, ety, tw; + Client *c = NULL; + Client *fc = NULL; + double mfact = 0; + int32_t master_num = 0; + int32_t stack_num = 0; + + 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; - wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && ISTILED(c)) n++; if (n == 0) return; - unsigned int cur_gappiv = enablegaps ? m->gappiv : 0; - unsigned int cur_gappih = enablegaps ? m->gappih : 0; - unsigned int cur_gappov = enablegaps ? m->gappov : 0; - unsigned int cur_gappoh = enablegaps ? m->gappoh : 0; + // 获取第一个可见的平铺客户端用于主区域宽度百分比 + wl_list_for_each(fc, &clients, link) { + if (VISIBLEON(fc, m) && ISTILED(fc)) + break; + } + + // 间隙参数处理 + 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; // 外部水平间隙 + + // 智能间隙处理 + cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; + cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; + cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; + cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; + + int32_t nmasters = m->pertag->nmasters[m->pertag->curtag]; + mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per + : m->pertag->mfacts[m->pertag->curtag]; + + // 初始化区域 + mw = m->w.width; + mx = cur_gappoh; + my = cur_gappov; + tw = mw; + + // 判断是否需要主区域铺满 + int32_t should_overspread = center_master_overspread && (n <= nmasters); + + int32_t master_surplus_height = + (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1)); + float master_surplus_ratio = 1.0; + + int32_t slave_left_surplus_height = + (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num / 2 - 1)); + float slave_left_surplus_ratio = 1.0; + + int32_t slave_right_surplus_height = + (m->w.height - 2 * cur_gappov - + cur_gappiv * ie * ((stack_num + 1) / 2 - 1)); + float slave_right_surplus_ratio = 1.0; + + if (n > nmasters || !should_overspread) { + // 计算主区域宽度(居中模式) + mw = nmasters ? (m->w.width - 2 * cur_gappoh - cur_gappih * ie) * mfact + : 0; + + if (n - nmasters > 1) { + // 多个堆叠窗口:主区域居中,左右两侧各有一个堆叠区域 + tw = (m->w.width - mw) / 2 - cur_gappoh - cur_gappih * ie; + mx = cur_gappoh + tw + cur_gappih * ie; + } else if (n - nmasters == 1) { + // 单个堆叠窗口的处理 + if (center_when_single_stack) { + // stack在右边,master居中,左边空着 + tw = (m->w.width - mw) / 2 - cur_gappoh - cur_gappih * ie; + mx = cur_gappoh + tw + cur_gappih * ie; // master居中 + } else { + // stack在右边,master在左边 + tw = m->w.width - mw - 2 * cur_gappoh - cur_gappih * ie; + mx = cur_gappoh; // master在左边 + } + } else { + // 只有主区域窗口:居中显示 + tw = (m->w.width - mw) / 2 - cur_gappoh - cur_gappih * ie; + mx = cur_gappoh + tw + cur_gappih * ie; + } + } else { + // 主区域铺满模式(只有主区域窗口时) + mw = m->w.width - 2 * cur_gappoh; + mx = cur_gappoh; + tw = 0; // 堆叠区域宽度为0 + } + + oty = cur_gappov; + ety = cur_gappov; + + i = 0; + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || !ISTILED(c)) + continue; + + if (i < nmasters) { + // 主区域窗口 + r = MIN(n, nmasters) - i; + if (c->master_inner_per > 0.0f) { + h = master_surplus_height * c->master_inner_per / + master_surplus_ratio; + master_surplus_height = master_surplus_height - h; + master_surplus_ratio = + master_surplus_ratio - c->master_inner_per; + c->master_mfact_per = mfact; + } else { + h = (m->w.height - my - cur_gappov - + cur_gappiv * ie * (r - 1)) / + r; + c->master_inner_per = h / (m->w.height - my - cur_gappov - + cur_gappiv * ie * (r - 1)); + c->master_mfact_per = mfact; + } + + resize(c, + (struct wlr_box){.x = m->w.x + mx, + .y = m->w.y + my, + .width = mw, + .height = h}, + 0); + my += c->geom.height + cur_gappiv * ie; + } else { + // 堆叠区域窗口 + int32_t stack_index = i - nmasters; + + if (n - nmasters == 1) { + // 单个堆叠窗口 + r = n - i; + if (c->stack_inner_per > 0.0f) { + h = (m->w.height - 2 * cur_gappov - + cur_gappiv * ie * (stack_num - 1)) * + c->stack_inner_per; + c->master_mfact_per = mfact; + } else { + h = (m->w.height - ety - cur_gappov - + cur_gappiv * ie * (r - 1)) / + r; + c->stack_inner_per = h / (m->w.height - ety - cur_gappov - + cur_gappiv * ie * (r - 1)); + c->master_mfact_per = mfact; + } + + int32_t stack_x; + if (center_when_single_stack) { + // 放在右侧(master居中时,stack在右边) + stack_x = m->w.x + mx + mw + cur_gappih * ie; + } else { + // 放在右侧(master在左边时,stack在右边) + stack_x = m->w.x + mx + mw + cur_gappih * ie; + } + + resize(c, + (struct wlr_box){.x = stack_x, + .y = m->w.y + ety, + .width = tw, + .height = h}, + 0); + ety += c->geom.height + cur_gappiv * ie; + } else { + // 多个堆叠窗口:交替放在左右两侧 + r = (n - i + 1) / 2; + + if ((stack_index % 2) ^ (n % 2 == 0)) { + // 右侧堆叠窗口 + if (c->stack_inner_per > 0.0f) { + h = slave_right_surplus_height * c->stack_inner_per / + slave_right_surplus_ratio; + slave_right_surplus_height = + slave_right_surplus_height - h; + slave_right_surplus_ratio = + slave_right_surplus_ratio - c->stack_inner_per; + c->master_mfact_per = mfact; + } else { + h = (m->w.height - ety - cur_gappov - + cur_gappiv * ie * (r - 1)) / + r; + c->stack_inner_per = + h / (m->w.height - ety - cur_gappov - + cur_gappiv * ie * (r - 1)); + c->master_mfact_per = mfact; + } + + int32_t stack_x = m->w.x + mx + mw + cur_gappih * ie; + + resize(c, + (struct wlr_box){.x = stack_x, + .y = m->w.y + ety, + .width = tw, + .height = h}, + 0); + ety += c->geom.height + cur_gappiv * ie; + } else { + // 左侧堆叠窗口 + if (c->stack_inner_per > 0.0f) { + h = slave_left_surplus_height * c->stack_inner_per / + slave_left_surplus_ratio; + slave_left_surplus_height = + slave_left_surplus_height - h; + slave_left_surplus_ratio = + slave_left_surplus_ratio - c->stack_inner_per; + c->master_mfact_per = mfact; + } else { + h = (m->w.height - oty - cur_gappov - + cur_gappiv * ie * (r - 1)) / + r; + c->stack_inner_per = + h / (m->w.height - oty - cur_gappov - + cur_gappiv * ie * (r - 1)); + c->master_mfact_per = mfact; + } + + int32_t stack_x = m->w.x + cur_gappoh; + resize(c, + (struct wlr_box){.x = stack_x, + .y = m->w.y + oty, + .width = tw, + .height = h}, + 0); + oty += c->geom.height + cur_gappiv * ie; + } + } + } + i++; + } +} + +void tile(Monitor *m) { + int32_t i, n = 0, h, r, ie = enablegaps, mw, my, ty; + Client *c = NULL; + Client *fc = NULL; + double mfact = 0; + int32_t master_num = 0; + int32_t stack_num = 0; + + n = m->visible_tiling_clients; + master_num = m->pertag->nmasters[m->pertag->curtag]; + master_num = n > master_num ? master_num : n; + stack_num = n - master_num; + + if (n == 0) + return; + + 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; cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; - if (n > selmon->pertag->nmasters[selmon->pertag->curtag]) - mw = selmon->pertag->nmasters[selmon->pertag->curtag] - ? (m->w.width + cur_gappih * ie) * - selmon->pertag->mfacts[selmon->pertag->curtag] + wl_list_for_each(fc, &clients, link) { + + if (VISIBLEON(fc, m) && ISTILED(fc)) + break; + } + + mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per + : m->pertag->mfacts[m->pertag->curtag]; + + if (n > m->pertag->nmasters[m->pertag->curtag]) + mw = m->pertag->nmasters[m->pertag->curtag] + ? (m->w.width + cur_gappih * ie) * mfact : 0; else mw = m->w.width - 2 * cur_gappoh + cur_gappih * ie; i = 0; my = ty = cur_gappov; + + int32_t master_surplus_height = + (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1)); + float master_surplus_ratio = 1.0; + + int32_t slave_surplus_height = + (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num - 1)); + float slave_surplus_ratio = 1.0; + wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || !ISTILED(c)) continue; - if (i < selmon->pertag->nmasters[selmon->pertag->curtag]) { - r = MIN(n, selmon->pertag->nmasters[selmon->pertag->curtag]) - i; - h = (m->w.height - my - cur_gappov - cur_gappiv * ie * (r - 1)) / r; + if (i < m->pertag->nmasters[m->pertag->curtag]) { + r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; + if (c->master_inner_per > 0.0f) { + h = master_surplus_height * c->master_inner_per / + master_surplus_ratio; + master_surplus_height = master_surplus_height - h; + master_surplus_ratio = + master_surplus_ratio - c->master_inner_per; + c->master_mfact_per = mfact; + } else { + h = (m->w.height - my - cur_gappov - + cur_gappiv * ie * (r - 1)) / + r; + c->master_inner_per = h / (m->w.height - my - cur_gappov - + cur_gappiv * ie * (r - 1)); + c->master_mfact_per = mfact; + } resize(c, (struct wlr_box){.x = m->w.x + cur_gappoh, .y = m->w.y + my, @@ -476,7 +680,23 @@ void tile(Monitor *m) { my += c->geom.height + cur_gappiv * ie; } else { r = n - i; - h = (m->w.height - ty - cur_gappov - cur_gappiv * ie * (r - 1)) / r; + if (c->stack_inner_per > 0.0f) { + h = slave_surplus_height * c->stack_inner_per / + slave_surplus_ratio; + slave_surplus_height = slave_surplus_height - h; + slave_surplus_ratio = slave_surplus_ratio - c->stack_inner_per; + c->master_mfact_per = mfact; + } else { + h = (m->w.height - ty - cur_gappov - + cur_gappiv * ie * (r - 1)) / + r; + c->stack_inner_per = h / (m->w.height - ty - cur_gappov - + cur_gappiv * ie * (r - 1)); + c->master_mfact_per = mfact; + } + + // wlr_log(WLR_ERROR, "stack_inner_per: %f", c->stack_inner_per); + resize(c, (struct wlr_box){.x = m->w.x + mw + cur_gappoh, .y = m->w.y + ty, @@ -489,15 +709,148 @@ void tile(Monitor *m) { } } -void // 17 -monocle(Monitor *m) { - Client *c; +void right_tile(Monitor *m) { + int32_t i, n = 0, h, r, ie = enablegaps, mw, my, ty; + Client *c = NULL; + Client *fc = NULL; + double mfact = 0; + int32_t master_num = 0; + int32_t stack_num = 0; + + n = m->visible_tiling_clients; + master_num = m->pertag->nmasters[m->pertag->curtag]; + master_num = n > master_num ? master_num : n; + stack_num = n - master_num; + + if (n == 0) + return; + + 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; + + cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; + cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; + cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; + cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; + + wl_list_for_each(fc, &clients, link) { + + if (VISIBLEON(fc, m) && ISTILED(fc)) + break; + } + + mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per + : m->pertag->mfacts[m->pertag->curtag]; + + if (n > m->pertag->nmasters[m->pertag->curtag]) + mw = m->pertag->nmasters[m->pertag->curtag] + ? (m->w.width + cur_gappih * ie) * mfact + : 0; + else + mw = m->w.width - 2 * cur_gappoh + cur_gappih * ie; + i = 0; + my = ty = cur_gappov; + + int32_t master_surplus_height = + (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1)); + float master_surplus_ratio = 1.0; + + int32_t slave_surplus_height = + (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num - 1)); + float slave_surplus_ratio = 1.0; wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || !ISTILED(c)) continue; - resize(c, m->w, 0); + if (i < m->pertag->nmasters[m->pertag->curtag]) { + r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; + if (c->master_inner_per > 0.0f) { + h = master_surplus_height * c->master_inner_per / + master_surplus_ratio; + master_surplus_height = master_surplus_height - h; + master_surplus_ratio = + master_surplus_ratio - c->master_inner_per; + c->master_mfact_per = mfact; + } else { + h = (m->w.height - my - cur_gappov - + cur_gappiv * ie * (r - 1)) / + r; + c->master_inner_per = h / (m->w.height - my - cur_gappov - + cur_gappiv * ie * (r - 1)); + c->master_mfact_per = mfact; + } + resize(c, + (struct wlr_box){.x = m->w.x + m->w.width - mw - cur_gappoh + + cur_gappih * ie, + .y = m->w.y + my, + .width = mw - cur_gappih * ie, + .height = h}, + 0); + my += c->geom.height + cur_gappiv * ie; + } else { + r = n - i; + if (c->stack_inner_per > 0.0f) { + h = slave_surplus_height * c->stack_inner_per / + slave_surplus_ratio; + slave_surplus_height = slave_surplus_height - h; + slave_surplus_ratio = slave_surplus_ratio - c->stack_inner_per; + c->master_mfact_per = mfact; + } else { + h = (m->w.height - ty - cur_gappov - + cur_gappiv * ie * (r - 1)) / + r; + c->stack_inner_per = h / (m->w.height - ty - cur_gappov - + cur_gappiv * ie * (r - 1)); + c->master_mfact_per = mfact; + } + + // wlr_log(WLR_ERROR, "stack_inner_per: %f", c->stack_inner_per); + + resize(c, + (struct wlr_box){.x = m->w.x + cur_gappoh, + .y = m->w.y + ty, + .width = m->w.width - mw - 2 * cur_gappoh, + .height = h}, + 0); + ty += c->geom.height + cur_gappiv * ie; + } + i++; + } +} + +void // 17 +monocle(Monitor *m) { + Client *c = NULL; + struct wlr_box geom; + + int32_t cur_gappov = enablegaps ? m->gappov : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + + cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; + cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; + + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || !ISTILED(c)) + continue; + geom.x = m->w.x + cur_gappoh; + geom.y = m->w.y + cur_gappov; + geom.width = m->w.width - 2 * cur_gappoh; + geom.height = m->w.height - 2 * cur_gappov; + resize(c, geom, 0); } if ((c = focustop(m))) wlr_scene_node_raise_to_top(&c->scene->node); +} + +void tgmix(Monitor *m) { + int32_t n = m->visible_tiling_clients; + if (n <= 3) { + tile(m); + return; + } else { + grid(m); + return; + } } \ No newline at end of file diff --git a/src/layout/layout.h b/src/layout/layout.h index 05190cf8..169ab119 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -1,38 +1,50 @@ static void tile(Monitor *m); +static void center_tile(Monitor *m); +static void right_tile(Monitor *m); static void overview(Monitor *m); static void grid(Monitor *m); static void scroller(Monitor *m); static void deck(Monitor *mon); -static void dwindle(Monitor *mon); -static void spiral(Monitor *mon); static void monocle(Monitor *m); static void vertical_tile(Monitor *m); static void vertical_overview(Monitor *m); static void vertical_grid(Monitor *m); static void vertical_scroller(Monitor *m); static void vertical_deck(Monitor *mon); -static void vertical_dwindle(Monitor *mon); -static void vertical_spiral(Monitor *mon); -static void vertical_monocle(Monitor *m); +static void tgmix(Monitor *m); /* layout(s) */ Layout overviewlayout = {"󰃇", overview, "overview"}; +enum { + SCROLLER, + TILE, + GRID, + MONOCLE, + DECK, + CENTER_TILE, + VERTICAL_SCROLLER, + VERTICAL_TILE, + VERTICAL_GRID, + VERTICAL_DECK, + RIGHT_TILE, + TGMIX, +}; + Layout layouts[] = { // 最少两个,不能删除少于两个 /* symbol arrange function name */ - {"S", scroller, "scroller"}, // 滚动布局 - {"T", tile, "tile"}, // 堆栈布局 - {"G", grid, "grid"}, - {"M", monocle, "monocle"}, - {"D", dwindle, "dwindle"}, - {"P", spiral, "spiral"}, - {"K", deck, "deck"}, - {"VS", vertical_scroller, "vertical_scroller"}, - {"VT", vertical_tile, "vertical_tile"}, - {"VM", vertical_monocle, "vertical_monocle"}, - {"VD", vertical_dwindle, "vertical_dwindle"}, - {"VP", vertical_spiral, "vertical_spiral"}, - {"VG", vertical_grid, "vertical_grid"}, - {"VK", vertical_deck, "vertical_deck"}, + {"S", scroller, "scroller", SCROLLER}, // 滚动布局 + {"T", tile, "tile", TILE}, // 平铺布局 + {"G", grid, "grid", GRID}, // 格子布局 + {"M", monocle, "monocle", MONOCLE}, // 单屏布局 + {"K", deck, "deck", DECK}, // 卡片布局 + {"CT", center_tile, "center_tile", CENTER_TILE}, // 居中布局 + {"RT", right_tile, "right_tile", RIGHT_TILE}, // 右布局 + {"VS", vertical_scroller, "vertical_scroller", + VERTICAL_SCROLLER}, // 垂直滚动布局 + {"VT", vertical_tile, "vertical_tile", VERTICAL_TILE}, // 垂直平铺布局 + {"VG", vertical_grid, "vertical_grid", VERTICAL_GRID}, // 垂直格子布局 + {"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局 + {"TG", tgmix, "tgmix", TGMIX}, // 混合布局 }; \ No newline at end of file diff --git a/src/layout/vertical.h b/src/layout/vertical.h index d9024bd6..95138248 100644 --- a/src/layout/vertical.h +++ b/src/layout/vertical.h @@ -1,257 +1,138 @@ -void vertical_fibonacci(Monitor *mon, int s) { - unsigned int i = 0, n = 0, nx, ny, nw, nh; - Client *c; - unsigned int cur_gappih = enablegaps ? mon->gappih : 0; - unsigned int cur_gappiv = enablegaps ? mon->gappiv : 0; - unsigned int cur_gappoh = enablegaps ? mon->gappoh : 0; - unsigned int cur_gappov = enablegaps ? mon->gappov : 0; +void vertical_tile(Monitor *m) { + int32_t i, n = 0, w, r, ie = enablegaps, mh, mx, tx; + Client *c = NULL; + Client *fc = NULL; + double mfact = 0; + int32_t master_num = 0; + int32_t stack_num = 0; - cur_gappih = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappih; - cur_gappiv = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappiv; - cur_gappoh = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappoh; - cur_gappov = smartgaps && mon->visible_tiling_clients == 1 ? 0 : cur_gappov; - // Count visible clients - wl_list_for_each(c, &clients, link) if (VISIBLEON(c, mon) && ISTILED(c)) - n++; + n = m->visible_tiling_clients; + master_num = m->pertag->nmasters[m->pertag->curtag]; + master_num = n > master_num ? master_num : n; + stack_num = n - master_num; if (n == 0) return; - // Initial dimensions including outer gaps - nx = mon->w.x + cur_gappoh; - ny = mon->w.y + cur_gappov; - nw = mon->w.width - 2 * cur_gappoh; - nh = mon->w.height - 2 * cur_gappov; + int32_t cur_gapih = enablegaps ? m->gappih : 0; + int32_t cur_gapiv = enablegaps ? m->gappiv : 0; + int32_t cur_gapoh = enablegaps ? m->gappoh : 0; + int32_t cur_gapov = enablegaps ? m->gappov : 0; - // First pass: calculate client geometries - wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, mon) || !ISTILED(c)) - continue; + cur_gapih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapih; + cur_gapiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapiv; + cur_gapoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapoh; + cur_gapov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapov; - c->bw = mon->visible_tiling_clients == 1 && no_border_when_single && - smartgaps - ? 0 - : borderpx; - if ((i % 2 && nw / 2 > 2 * c->bw) || (!(i % 2) && nh / 2 > 2 * c->bw)) { - if (i < n - 1) { - if (i % 2) { - if (i == 1) { - nw = nw * mon->pertag->smfacts[mon->pertag->curtag]; - } else { - nw = (nw - cur_gappih) / 2; - } - } else { - nh = (nh - cur_gappiv) / 2; - } - - if ((i % 4) == 2 && !s) - ny += nh + cur_gappiv; - else if ((i % 4) == 3 && !s) - nx += nw + cur_gappih; - } - - if ((i % 4) == 0) { - if (s) - nx += nw + cur_gappih; - else - nx -= nw + cur_gappih; - } else if ((i % 4) == 1) - ny += nh + cur_gappiv; - else if ((i % 4) == 2) - nx += nw + cur_gappih; - else if ((i % 4) == 3) { - if (s) - ny += nh + cur_gappiv; - else - ny -= nh + cur_gappiv; - } - - if (i == 0) { - if (n != 1) - nh = (mon->w.height - 2 * cur_gappov) * - mon->pertag->mfacts[mon->pertag->curtag]; - nx = mon->w.x + cur_gappoh; - } else if (i == 1) { - nh = mon->w.height - 2 * cur_gappov - nh - cur_gappiv; - } else if (i == 2) { - nw = mon->w.width - 2 * cur_gappoh - nw - cur_gappih; - } - i++; - } - - c->geom = (struct wlr_box){.x = nx, .y = ny, .width = nw, .height = nh}; - } - - // Second pass: apply gaps between clients - wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, mon) || !ISTILED(c)) - continue; - - unsigned int right_gap = 0; - unsigned int bottom_gap = 0; - Client *nc; - - wl_list_for_each(nc, &clients, link) { - if (!VISIBLEON(nc, mon) || !ISTILED(nc)) - continue; - - if (c == nc) - continue; - - // Check for right neighbor - if (c->geom.y == nc->geom.y && - c->geom.x + c->geom.width == nc->geom.x) { - right_gap = cur_gappih; - } - - // Check for bottom neighbor - if (c->geom.x == nc->geom.x && - c->geom.y + c->geom.height == nc->geom.y) { - bottom_gap = cur_gappiv; - } - } - - resize(c, - (struct wlr_box){.x = c->geom.x, - .y = c->geom.y, - .width = c->geom.width - right_gap, - .height = c->geom.height - bottom_gap}, - 0); - } -} - -void vertical_dwindle(Monitor *mon) { vertical_fibonacci(mon, 1); } - -void vertical_spiral(Monitor *mon) { vertical_fibonacci(mon, 0); } - -void vertical_grid(Monitor *m) { - unsigned int i, n; - unsigned int cx, cy, cw, ch; - unsigned int dy; - unsigned int rows, cols, overrows; - Client *c; - n = 0; - - wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && !c->isunglobal && - ((m->isoverview && !client_should_ignore_focus(c)) || ISTILED(c))) { - n++; - } - } - - if (n == 0) { - return; - } - - if (n == 1) { - wl_list_for_each(c, &clients, link) { - c->bw = m->visible_tiling_clients == 1 && no_border_when_single && - smartgaps - ? 0 - : borderpx; - if (VISIBLEON(c, m) && !c->isunglobal && - ((m->isoverview && !client_should_ignore_focus(c)) || - ISTILED(c))) { - ch = (m->w.height - 2 * overviewgappo) * 0.7; - cw = (m->w.width - 2 * overviewgappo) * 0.8; - c->geom.x = m->w.x + (m->w.width - cw) / 2; - c->geom.y = m->w.y + (m->w.height - ch) / 2; - c->geom.width = cw - 2 * c->bw; - c->geom.height = ch - 2 * c->bw; - resize(c, c->geom, 0); - return; - } - } - } - - if (n == 2) { - ch = (m->w.height - 2 * overviewgappo - overviewgappi) / 2; - cw = (m->w.width - 2 * overviewgappo) * 0.65; - i = 0; - wl_list_for_each(c, &clients, link) { - c->bw = m->visible_tiling_clients == 1 && no_border_when_single && - smartgaps - ? 0 - : borderpx; - if (VISIBLEON(c, m) && !c->isunglobal && - ((m->isoverview && !client_should_ignore_focus(c)) || - ISTILED(c))) { - if (i == 0) { - c->geom.x = m->w.x + (m->w.width - cw) / 2 + overviewgappo; - c->geom.y = m->w.y + overviewgappo; - c->geom.width = cw - 2 * c->bw; - c->geom.height = ch - 2 * c->bw; - resize(c, c->geom, 0); - } else if (i == 1) { - c->geom.x = m->w.x + (m->w.width - cw) / 2 + overviewgappo; - c->geom.y = m->w.y + ch + overviewgappo + overviewgappi; - c->geom.width = cw - 2 * c->bw; - c->geom.height = ch - 2 * c->bw; - resize(c, c->geom, 0); - } - i++; - } - } - return; - } - - for (rows = 0; rows <= n / 2; rows++) { - if (rows * rows >= n) { + wl_list_for_each(fc, &clients, link) { + if (VISIBLEON(fc, m) && ISTILED(fc)) break; - } } - cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; - cw = (m->w.width - 2 * overviewgappo - (cols - 1) * overviewgappi) / cols; - ch = (m->w.height - 2 * overviewgappo - (rows - 1) * overviewgappi) / rows; + mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per + : m->pertag->mfacts[m->pertag->curtag]; - overrows = n % rows; - if (overrows) { - dy = - (m->w.height - overrows * ch - (overrows - 1) * overviewgappi) / 2 - - overviewgappo; - } + if (n > m->pertag->nmasters[m->pertag->curtag]) + mh = m->pertag->nmasters[m->pertag->curtag] + ? (m->w.height + cur_gapiv * ie) * mfact + : 0; + else + mh = m->w.height - 2 * cur_gapov + cur_gapiv * ie; i = 0; + mx = tx = cur_gapoh; + + int32_t master_surplus_width = + (m->w.width - 2 * cur_gapoh - cur_gapih * ie * (master_num - 1)); + float master_surplus_ratio = 1.0; + + int32_t slave_surplus_width = + (m->w.width - 2 * cur_gapoh - cur_gapih * ie * (stack_num - 1)); + float slave_surplus_ratio = 1.0; + wl_list_for_each(c, &clients, link) { - c->bw = - m->visible_tiling_clients == 1 && no_border_when_single && smartgaps - ? 0 - : borderpx; - if (VISIBLEON(c, m) && !c->isunglobal && - ((m->isoverview && !client_should_ignore_focus(c)) || ISTILED(c))) { - cx = m->w.x + (i / rows) * (cw + overviewgappi); - cy = m->w.y + (i % rows) * (ch + overviewgappi); - if (overrows && i >= n - overrows) { - cy += dy; + if (!VISIBLEON(c, m) || !ISTILED(c)) + continue; + if (i < m->pertag->nmasters[m->pertag->curtag]) { + r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; + if (c->master_inner_per > 0.0f) { + w = master_surplus_width * c->master_inner_per / + master_surplus_ratio; + master_surplus_width = master_surplus_width - w; + master_surplus_ratio = + master_surplus_ratio - c->master_inner_per; + c->master_mfact_per = mfact; + } else { + w = (m->w.width - mx - cur_gapih - cur_gapih * ie * (r - 1)) / + r; + c->master_inner_per = w / (m->w.width - mx - cur_gapih - + cur_gapih * ie * (r - 1)); + c->master_mfact_per = mfact; } - c->geom.x = cx + overviewgappo; - c->geom.y = cy + overviewgappo; - c->geom.width = cw - 2 * c->bw; - c->geom.height = ch - 2 * c->bw; - resize(c, c->geom, 0); - i++; + resize(c, + (struct wlr_box){.x = m->w.x + mx, + .y = m->w.y + cur_gapov, + .width = w, + .height = mh - cur_gapiv * ie}, + 0); + mx += c->geom.width + cur_gapih * ie; + } else { + r = n - i; + if (c->stack_inner_per > 0.0f) { + w = slave_surplus_width * c->stack_inner_per / + slave_surplus_ratio; + slave_surplus_width = slave_surplus_width - w; + slave_surplus_ratio = slave_surplus_ratio - c->stack_inner_per; + c->master_mfact_per = mfact; + } else { + w = (m->w.width - tx - cur_gapih - cur_gapih * ie * (r - 1)) / + r; + c->stack_inner_per = w / (m->w.width - tx - cur_gapih - + cur_gapih * ie * (r - 1)); + c->master_mfact_per = mfact; + } + + resize(c, + (struct wlr_box){.x = m->w.x + tx, + .y = m->w.y + mh + cur_gapov, + .width = w, + .height = m->w.height - mh - 2 * cur_gapov}, + 0); + tx += c->geom.width + cur_gapih * ie; } + i++; } } void vertical_deck(Monitor *m) { - unsigned int mh, mx; - int i, n = 0; - Client *c; - unsigned int cur_gappiv = enablegaps ? m->gappiv : 0; - unsigned int cur_gappoh = enablegaps ? m->gappoh : 0; - unsigned int cur_gappov = enablegaps ? m->gappov : 0; + int32_t mh, mx; + int32_t i, n = 0; + Client *c = NULL; + Client *fc = NULL; + float mfact; + + int32_t cur_gappiv = enablegaps ? m->gappiv : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; - wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && ISTILED(c)) n++; + n = m->visible_tiling_clients; + if (n == 0) return; - float mfact = m->pertag ? m->pertag->mfacts[m->pertag->curtag] : m->mfact; + wl_list_for_each(fc, &clients, link) { + + if (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 > m->nmaster) mh = m->nmaster ? round((m->w.height - 2 * cur_gappov) * mfact) : 0; @@ -287,31 +168,62 @@ void vertical_deck(Monitor *m) { } } -void vertical_scroller(Monitor *m) { - unsigned int i, n; - Client *c, *root_client = NULL; - Client **tempClients = NULL; - n = 0; - struct wlr_box target_geom; - int focus_client_index = 0; - bool need_scroller = false; - unsigned int cur_gappiv = enablegaps ? m->gappiv : 0; - unsigned int cur_gappov = enablegaps ? m->gappov : 0; - unsigned int cur_gappoh = enablegaps ? m->gappoh : 0; +void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) { + Monitor *m = c->mon; + int32_t cur_gappiv = enablegaps ? m->gappiv : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; - cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; - cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; - cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; + cur_gappiv = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv; + cur_gappov = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov; + cur_gappoh = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh; - unsigned int max_client_height = - m->w.height - 2 * scroller_structs - cur_gappiv; - - wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && ISTILED(c)) { - n++; - } + if (c->isfullscreen) { + target_geom->width = m->m.width; + target_geom->height = m->m.height; + target_geom->x = m->m.x; + return; } + if (c->ismaximizescreen) { + target_geom->width = m->w.width - 2 * cur_gappoh; + target_geom->height = m->w.height - 2 * cur_gappov; + target_geom->x = m->w.x + cur_gappoh; + return; + } + + target_geom->width = m->w.width - 2 * cur_gappoh; + target_geom->x = m->w.x + (m->w.width - target_geom->width) / 2; +} + +// 竖屏滚动布局 +void vertical_scroller(Monitor *m) { + int32_t i, n, j; + float single_proportion = 1.0; + + Client *c = NULL, *root_client = NULL; + Client **tempClients = NULL; + struct wlr_box target_geom; + int32_t focus_client_index = 0; + bool need_scroller = false; + int32_t cur_gappiv = enablegaps ? m->gappiv : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + + cur_gappiv = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv; + cur_gappov = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov; + cur_gappoh = + smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh; + + int32_t max_client_height = m->w.height - 2 * scroller_structs - cur_gappiv; + + n = m->visible_scroll_tiling_clients; + if (n == 0) { return; } @@ -321,35 +233,38 @@ void vertical_scroller(Monitor *m) { return; } - n = 0; + j = 0; wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && ISTILED(c)) { - tempClients[n] = c; - n++; + if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { + tempClients[j] = c; + j++; } } - if (n == 1) { + if (n == 1 && !scroller_ignore_proportion_single && + !tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) { c = tempClients[0]; + + single_proportion = c->scroller_proportion_single > 0.0f + ? c->scroller_proportion_single + : scroller_default_proportion_single; + target_geom.width = m->w.width - 2 * cur_gappoh; - target_geom.height = - (m->w.height - 2 * cur_gappov) * scroller_default_proportion_single; - target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; + target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion; target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; + 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) && !m->sel->isfloating && - !m->sel->ismaxmizescreen && !m->sel->isfullscreen) { + if (m->sel && !client_is_unmanaged(m->sel) && ISSCROLLTILED(m->sel)) { root_client = m->sel; - } else if (m->prevsel && !client_is_unmanaged(m->prevsel) && - !m->prevsel->isfloating && !m->prevsel->ismaxmizescreen && - !m->prevsel->isfullscreen) { + } else if (m->prevsel && ISSCROLLTILED(m->prevsel) && + VISIBLEON(m->prevsel, m) && !client_is_unmanaged(m->prevsel)) { root_client = m->prevsel; } else { - root_client = center_select(m); + root_client = center_tiled_select(m); } if (!root_client) { @@ -360,8 +275,7 @@ void vertical_scroller(Monitor *m) { for (i = 0; i < n; i++) { c = tempClients[i]; if (root_client == c) { - if (!c->is_pending_open_animation && - c->geom.y >= m->w.y + scroller_structs && + if (c->geom.y >= m->w.y + scroller_structs && c->geom.y + c->geom.height <= m->w.y + m->w.height - scroller_structs) { need_scroller = false; @@ -373,16 +287,32 @@ void vertical_scroller(Monitor *m) { } } + if (n == 1 && scroller_ignore_proportion_single) { + need_scroller = true; + } + + if (start_drag_window) + need_scroller = false; + target_geom.width = m->w.width - 2 * cur_gappoh; target_geom.height = max_client_height * c->scroller_proportion; target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; + vertical_scroll_adjust_fullandmax(tempClients[focus_client_index], + &target_geom); - if (need_scroller) { + if (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 || - (m->prevsel->scroller_proportion * max_client_height) + - (root_client->scroller_proportion * max_client_height) > - m->w.height - 2 * scroller_structs - cur_gappiv) && + (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 { @@ -402,14 +332,17 @@ void vertical_scroller(Monitor *m) { 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; @@ -419,70 +352,109 @@ void vertical_scroller(Monitor *m) { free(tempClients); } -void vertical_tile(Monitor *m) { - unsigned int i, n = 0, w, r, ie = enablegaps, mh, mx, tx; - Client *c; +void vertical_grid(Monitor *m) { + int32_t i, n; + int32_t cx, cy, cw, ch; + int32_t dy; + int32_t rows, cols, overrows; + Client *c = NULL; + int32_t target_gappo = + enablegaps ? m->isoverview ? overviewgappo : gappov : 0; + int32_t target_gappi = + enablegaps ? m->isoverview ? overviewgappi : gappiv : 0; + float single_width_ratio = m->isoverview ? 0.7 : 0.9; + float single_height_ratio = m->isoverview ? 0.8 : 0.9; - wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && ISTILED(c)) n++; - if (n == 0) + n = m->isoverview ? m->visible_clients : m->visible_tiling_clients; + + if (n == 0) { return; + } - unsigned int cur_gappiv = enablegaps ? m->gappiv : 0; - unsigned int cur_gappih = enablegaps ? m->gappih : 0; - unsigned int cur_gappov = enablegaps ? m->gappov : 0; - unsigned int cur_gappoh = enablegaps ? m->gappoh : 0; + if (n == 1) { + wl_list_for_each(c, &clients, link) { - cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; - cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; - cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; - cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; + if (c->mon != m) + continue; - if (n > selmon->pertag->nmasters[selmon->pertag->curtag]) - mh = selmon->pertag->nmasters[selmon->pertag->curtag] - ? (m->w.height + cur_gappiv * ie) * - selmon->pertag->mfacts[selmon->pertag->curtag] - : 0; - else - mh = m->w.height - 2 * cur_gappoh + cur_gappiv * ie; - i = 0; - mx = tx = cur_gappov; - wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, m) || !ISTILED(c)) - continue; - if (i < selmon->pertag->nmasters[selmon->pertag->curtag]) { - r = MIN(n, selmon->pertag->nmasters[selmon->pertag->curtag]) - i; - w = (m->w.width - mx - cur_gappov - cur_gappiv * ie * (r - 1)) / r; - resize(c, - (struct wlr_box){.x = m->w.x + mx, - .y = m->w.y + cur_gappoh, - .width = w, - .height = mh - cur_gappih * ie}, - 0); - mx += c->geom.width + cur_gappiv * ie; - } else { - r = n - i; - w = (m->w.width - tx - cur_gappov - cur_gappiv * ie * (r - 1)) / r; - resize( - c, - (struct wlr_box){.x = m->w.x + tx, - .y = m->w.y + mh + cur_gappoh, - .width = w, - .height = m->w.height - mh - 2 * cur_gappoh}, - 0); - tx += c->geom.width + cur_gappiv * ie; + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + ch = (m->w.height - 2 * target_gappo) * single_height_ratio; + cw = (m->w.width - 2 * target_gappo) * single_width_ratio; + c->geom.x = m->w.x + (m->w.width - cw) / 2; + c->geom.y = m->w.y + (m->w.height - ch) / 2; + c->geom.width = cw; + c->geom.height = ch; + resize(c, c->geom, 0); + return; + } } - i++; } -} -void vertical_monocle(Monitor *m) { - Client *c; + if (n == 2) { + ch = (m->w.height - 2 * target_gappo - target_gappi) / 2; + cw = (m->w.width - 2 * target_gappo) * 0.65; + i = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + if (i == 0) { + c->geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo; + c->geom.y = m->w.y + target_gappo; + c->geom.width = cw; + c->geom.height = ch; + resize(c, c->geom, 0); + } else if (i == 1) { + c->geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo; + c->geom.y = m->w.y + ch + target_gappo + target_gappi; + c->geom.width = cw; + c->geom.height = ch; + resize(c, c->geom, 0); + } + i++; + } + } + return; + } + + for (rows = 0; rows <= n / 2; rows++) { + if (rows * rows >= n) { + break; + } + } + cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; + + cw = (m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols; + ch = (m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows; + + overrows = n % rows; + if (overrows) { + dy = (m->w.height - overrows * ch - (overrows - 1) * target_gappi) / 2 - + target_gappo; + } + + i = 0; wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, m) || !ISTILED(c)) + if (c->mon != m) continue; - resize(c, m->w, 0); + + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + cx = m->w.x + (i / rows) * (cw + target_gappi); + cy = m->w.y + (i % rows) * (ch + target_gappi); + if (overrows && i >= n - overrows) { + cy += dy; + } + c->geom.x = cx + target_gappo; + c->geom.y = cy + target_gappo; + c->geom.width = cw; + c->geom.height = ch; + resize(c, c->geom, 0); + i++; + } } - if ((c = focustop(m))) - wlr_scene_node_raise_to_top(&c->scene->node); } \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index 1743775f..a1cbb64c 100644 --- a/src/mango.c +++ b/src/mango.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -97,19 +100,25 @@ #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define GEZERO(A) ((A) >= 0 ? (A) : 0) #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) +#define INSIDEMON(A) \ + (A->geom.x >= A->mon->m.x && A->geom.y >= A->mon->m.y && \ + A->geom.x + A->geom.width <= A->mon->m.x + A->mon->m.width && \ + A->geom.y + A->geom.height <= A->mon->m.y + A->mon->m.height) #define ISTILED(A) \ - (!(A)->isfloating && !(A)->isminied && !(A)->iskilling && \ - !client_should_ignore_focus(A) && !(A)->isunglobal && \ - !(A)->animation.tagouting && !(A)->ismaxmizescreen && !(A)->isfullscreen) + (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ + !(A)->ismaximizescreen && !(A)->isfullscreen && !(A)->isunglobal) +#define ISSCROLLTILED(A) \ + (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ + !(A)->isunglobal) #define VISIBLEON(C, M) \ - ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) + ((C) && (M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) #define ISFULLSCREEN(A) \ - ((A)->isfullscreen || (A)->ismaxmizescreen || \ - (A)->overview_ismaxmizescreenbak || (A)->overview_isfullscreenbak) + ((A)->isfullscreen || (A)->ismaximizescreen || \ + (A)->overview_ismaximizescreenbak || (A)->overview_isfullscreenbak) #define LISTEN_STATIC(E, H) \ do { \ struct wl_listener *_l = ecalloc(1, sizeof(*_l)); \ @@ -142,10 +151,8 @@ enum { LyrBlur, LyrBottom, LyrTile, - LyrFloat, LyrTop, LyrFadeOut, - LyrFSorOverTop, LyrOverlay, LyrIMPopup, // text-input layer LyrBlock, @@ -161,42 +168,102 @@ enum { }; /* EWMH atoms */ #endif enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */ -enum { NONE, OPEN, MOVE, CLOSE, TAG }; +enum { NONE, OPEN, MOVE, CLOSE, TAG, FOCUS, OPAFADEIN, OPAFADEOUT }; +enum { UNFOLD, FOLD, INVALIDFOLD }; +enum { PREV, NEXT }; +enum { STATE_UNSPECIFIED = 0, STATE_ENABLED, STATE_DISABLED }; + +enum tearing_mode { + TEARING_DISABLED = 0, + TEARING_ENABLED, + TEARING_FULLSCREEN_ONLY, +}; + +enum seat_config_shortcuts_inhibit { + SHORTCUTS_INHIBIT_DISABLE, + SHORTCUTS_INHIBIT_ENABLE, +}; + +// 事件掩码枚举 +enum print_event_type { + PRINT_ACTIVE = 1 << 0, + PRINT_TAG = 1 << 1, + PRINT_LAYOUT = 1 << 2, + PRINT_TITLE = 1 << 3, + PRINT_APPID = 1 << 4, + PRINT_LAYOUT_SYMBOL = 1 << 5, + PRINT_FULLSCREEN = 1 << 6, + PRINT_FLOATING = 1 << 7, + PRINT_X = 1 << 8, + PRINT_Y = 1 << 9, + PRINT_WIDTH = 1 << 10, + PRINT_HEIGHT = 1 << 11, + PRINT_LAST_LAYER = 1 << 12, + PRINT_KB_LAYOUT = 1 << 13, + PRINT_KEYMODE = 1 << 14, + PRINT_SCALEFACTOR = 1 << 15, + PRINT_FRAME = 1 << 16, + PRINT_ALL = (1 << 17) - 1 // 所有位都设为1 +}; + +typedef struct Pertag Pertag; +typedef struct Monitor Monitor; +typedef struct Client Client; struct dvec2 { double x, y; }; struct ivec2 { - int x, y, width, height; + int32_t x, y, width, height; }; typedef struct { - int i; - int i2; + int32_t i; + int32_t i2; float f; float f2; char *v; char *v2; char *v3; - unsigned int ui; - unsigned int ui2; + uint32_t ui; + uint32_t ui2; } Arg; typedef struct { - unsigned int mod; - unsigned int button; - void (*func)(const Arg *); + uint32_t mod; + uint32_t button; + int32_t (*func)(const Arg *); const Arg arg; } Button; // 鼠标按键 typedef struct { - unsigned int mod; - unsigned int dir; - void (*func)(const Arg *); + char mode[28]; + bool isdefault; +} KeyMode; + +typedef struct { + uint32_t mod; + uint32_t dir; + int32_t (*func)(const Arg *); const Arg arg; } Axis; +typedef struct { + struct wl_list link; + struct wlr_input_device *wlr_device; + struct libinput_device *libinput_device; + struct wl_listener destroy_listener; // 用于监听设备销毁事件 + void *device_data; // 新增:指向设备特定数据(如 Switch) +} InputDevice; + +typedef struct { + struct wl_list link; + struct wlr_switch *wlr_switch; + struct wl_listener toggle; + InputDevice *input_dev; +} Switch; + struct dwl_animation { bool should_animate; bool running; @@ -205,35 +272,40 @@ struct dwl_animation { bool tagouting; bool begin_fade_in; bool tag_from_rule; - unsigned int total_frames; - unsigned int passed_frames; - unsigned int duration; + uint32_t time_started; + uint32_t duration; struct wlr_box initial; struct wlr_box current; - int action; + int32_t action; }; -typedef struct Pertag Pertag; -typedef struct Monitor Monitor; -struct wlr_foreign_toplevel_handle_v1; +struct dwl_opacity_animation { + bool running; + float current_opacity; + float target_opacity; + float initial_opacity; + uint32_t time_started; + uint32_t duration; + float current_border_color[4]; + float target_border_color[4]; + float initial_border_color[4]; +}; typedef struct { float width_scale; float height_scale; - int width; - int height; - double percent; - float opacity; + int32_t width; + int32_t height; enum corner_location corner_location; bool should_scale; } BufferData; -typedef struct Client Client; struct Client { /* Must keep these three elements in this order */ - unsigned int type; /* XDGShell or X11* */ - struct wlr_box geom, pending, oldgeom, animainit_geom, overview_backup_geom, - current; /* layout-relative, includes border */ + uint32_t type; /* XDGShell or X11* */ + struct wlr_box geom, pending, float_geom, animainit_geom, + overview_backup_geom, current, + drag_begin_geom; /* layout-relative, includes border */ Monitor *mon; struct wlr_scene_tree *scene; struct wlr_scene_rect *border; /* top, bottom, left, right */ @@ -262,19 +334,19 @@ struct Client { struct wl_listener set_hints; struct wl_listener set_geometry; #endif - unsigned int bw; - unsigned int tags, oldtags, mini_restore_tag; + uint32_t bw; + uint32_t tags, oldtags, mini_restore_tag; bool dirty; - unsigned int configure_serial; + uint32_t configure_serial; struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; - int isfloating, isurgent, isfullscreen, isfakefullscreen, - need_float_size_reduce, isminied, isoverlay, isnosizehint, + int32_t isfloating, isurgent, isfullscreen, isfakefullscreen, + need_float_size_reduce, isminimized, isoverlay, isnosizehint, ignore_maximize, ignore_minimize; - int ismaxmizescreen; - int overview_backup_bw; - int fullscreen_backup_x, fullscreen_backup_y, fullscreen_backup_w, + int32_t ismaximizescreen; + int32_t overview_backup_bw; + int32_t fullscreen_backup_x, fullscreen_backup_y, fullscreen_backup_w, fullscreen_backup_h; - int overview_isfullscreenbak, overview_ismaxmizescreenbak, + int32_t overview_isfullscreenbak, overview_ismaximizescreenbak, overview_isfloatingbak; struct wlr_xdg_toplevel_decoration_v1 *decoration; @@ -282,39 +354,62 @@ struct Client { struct wl_listener foreign_fullscreen_request; struct wl_listener foreign_close_request; struct wl_listener foreign_destroy; + struct wl_listener foreign_minimize_request; + struct wl_listener foreign_maximize_request; struct wl_listener set_decoration_mode; struct wl_listener destroy_decoration; const char *animation_type_open; const char *animation_type_close; - int is_in_scratchpad; - int is_scratchpad_show; - int isglobal; - int isnoborder; - int isopensilent; - int istagsilent; - int iskilling; - int isnamedscratchpad; + int32_t is_in_scratchpad; + int32_t iscustomsize; + int32_t iscustompos; + int32_t is_scratchpad_show; + int32_t isglobal; + int32_t isnoborder; + int32_t isnoshadow; + int32_t isnoradius; + int32_t isnoanimation; + int32_t isopensilent; + int32_t istagsilent; + int32_t iskilling; + int32_t istagswitching; + int32_t isnamedscratchpad; bool is_pending_open_animation; bool is_restoring_from_ov; float scroller_proportion; bool need_output_flush; struct dwl_animation animation; - int isterm, noswallow; + struct dwl_opacity_animation opacity_animation; + int32_t isterm, noswallow; + int32_t allow_csd; + int32_t force_maximize; pid_t pid; Client *swallowing, *swallowedby; bool is_clip_to_hide; bool drag_to_tile; + bool scratchpad_switching_mon; bool fake_no_border; - int nofadein; - int nofadeout; - int no_force_center; - int isunglobal; + int32_t nofocus; + int32_t nofadein; + int32_t nofadeout; + int32_t no_force_center; + int32_t isunglobal; float focused_opacity; float unfocused_opacity; char oldmonname[128]; - int scratchpad_width, scratchpad_height; - int noblur; + int32_t noblur; + double master_mfact_per, master_inner_per, stack_inner_per; + double old_master_mfact_per, old_master_inner_per, old_stack_inner_per; + double old_scroller_pproportion; + bool ismaster; + bool cursor_in_upper_half, cursor_in_left_half; + bool isleftstack; + int32_t tearing_hint; + int32_t force_tearing; + int32_t allow_shortcuts_inhibit; + float scroller_proportion_single; + bool isfocusing; }; typedef struct { @@ -324,19 +419,19 @@ typedef struct { } DwlIpcOutput; typedef struct { - unsigned int mod; + uint32_t mod; xkb_keysym_t keysym; - void (*func)(const Arg *); + int32_t (*func)(const Arg *); const Arg arg; } Key; typedef struct { struct wlr_keyboard_group *wlr_group; - int nsyms; + int32_t nsyms; const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ - unsigned int mods; /* invalid if nsyms == 0 */ - unsigned int keycode; + uint32_t mods; /* invalid if nsyms == 0 */ + uint32_t keycode; struct wl_event_source *key_repeat_source; struct wl_listener modifiers; @@ -345,22 +440,14 @@ typedef struct { } KeyboardGroup; typedef struct { - struct wl_list link; - struct wlr_keyboard *wlr_keyboard; - - int nsyms; - const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ - unsigned int mods; /* invalid if nsyms == 0 */ - struct wl_event_source *key_repeat_source; - - struct wl_listener modifiers; - struct wl_listener key; + struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor; struct wl_listener destroy; -} Keyboard; + struct wl_list link; +} KeyboardShortcutsInhibitor; typedef struct { /* Must keep these three elements in this order */ - unsigned int type; /* LayerShell */ + uint32_t type; /* LayerShell */ struct wlr_box geom, current, pending, animainit_geom; Monitor *mon; struct wlr_scene_tree *scene; @@ -369,7 +456,7 @@ typedef struct { struct wlr_scene_layer_surface_v1 *scene_layer; struct wl_list link; struct wl_list fadeout_link; - int mapped; + int32_t mapped; struct wlr_layer_surface_v1 *layer_surface; struct wl_listener destroy; @@ -379,9 +466,9 @@ typedef struct { struct dwl_animation animation; bool dirty; - int noblur; - int noanim; - int noshadow; + int32_t noblur; + int32_t noanim; + int32_t noshadow; char *animation_type_open; char *animation_type_close; bool need_output_flush; @@ -391,12 +478,14 @@ typedef struct { const char *symbol; void (*arrange)(Monitor *); const char *name; + uint32_t id; } Layout; struct Monitor { struct wl_list link; struct wlr_output *wlr_output; struct wlr_scene_output *scene_output; + struct wlr_output_state pending; struct wl_listener frame; struct wl_listener destroy; struct wl_listener request_state; @@ -406,24 +495,24 @@ struct Monitor { struct wlr_box w; /* window area, layout-relative */ struct wl_list layers[4]; /* LayerSurface::link */ const Layout *lt; - unsigned int seltags; - unsigned int tagset[2]; + uint32_t seltags; + uint32_t tagset[2]; double mfact; - int nmaster; + int32_t nmaster; struct wl_list dwl_ipc_outputs; - int gappih; /* horizontal gap between windows */ - int gappiv; /* vertical gap between windows */ - int gappoh; /* horizontal outer gaps */ - int gappov; /* vertical outer gaps */ + int32_t gappih; /* horizontal gap between windows */ + int32_t gappiv; /* vertical gap between windows */ + int32_t gappoh; /* horizontal outer gaps */ + int32_t gappov; /* vertical outer gaps */ Pertag *pertag; Client *sel, *prevsel; - int isoverview; - int is_in_hotarea; - int gamma_lut_changed; - int asleep; - unsigned int visible_clients; - unsigned int visible_tiling_clients; + int32_t isoverview; + int32_t is_in_hotarea; + int32_t asleep; + uint32_t visible_clients; + uint32_t visible_tiling_clients; + uint32_t visible_scroll_tiling_clients; struct wlr_scene_optimized_blur *blur; char last_surface_ws_name[256]; struct wlr_ext_workspace_group_handle_v1 *ext_group; @@ -434,21 +523,6 @@ typedef struct { struct wl_listener destroy; } PointerConstraint; -typedef struct { - const char *id; - const char *title; - unsigned int tags; - int isfloating; - int isfullscreen; - float scroller_proportion; - const char *animation_type_open; - const char *animation_type_close; - int isnoborder; - int monitor; - unsigned int width; - unsigned int height; -} Rule; - typedef struct { struct wlr_scene_tree *scene; @@ -463,18 +537,17 @@ static void applybounds( Client *c, struct wlr_box *bbox); // 设置边界规则,能让一些窗口拥有比较适合的大小 static void applyrules(Client *c); // 窗口规则应用,应用config.h中定义的窗口规则 -static void -arrange(Monitor *m, - bool want_animation); // 布局函数,让窗口俺平铺规则移动和重置大小 +static void arrange(Monitor *m, bool want_animation, + bool from_view); // 布局函数,让窗口俺平铺规则移动和重置大小 static void arrangelayer(Monitor *m, struct wl_list *list, - struct wlr_box *usable_area, int exclusive); + struct wlr_box *usable_area, int32_t exclusive); static void arrangelayers(Monitor *m); -static char *get_autostart_path(char *, unsigned int); // 自启动命令执行 +static void handle_print_status(struct wl_listener *listener, void *data); static void axisnotify(struct wl_listener *listener, void *data); // 滚轮事件处理 static void buttonpress(struct wl_listener *listener, void *data); // 鼠标按键事件处理 -static int ongesture(struct wlr_pointer_swipe_end_event *event); +static int32_t ongesture(struct wlr_pointer_swipe_end_event *event); static void swipe_begin(struct wl_listener *listener, void *data); static void swipe_update(struct wl_listener *listener, void *data); static void swipe_end(struct wl_listener *listener, void *data); @@ -484,13 +557,11 @@ static void pinch_end(struct wl_listener *listener, void *data); static void hold_begin(struct wl_listener *listener, void *data); static void hold_end(struct wl_listener *listener, void *data); static void checkidleinhibitor(struct wlr_surface *exclude); -static void cleanup(void); // 退出清理 -static void cleanupkeyboard(struct wl_listener *listener, - void *data); // 退出清理 +static void cleanup(void); // 退出清理 static void cleanupmon(struct wl_listener *listener, void *data); // 退出清理 static void closemon(Monitor *m); static void cleanuplisteners(void); -static void toggle_hotarea(int x_root, int y_root); // 触发热区 +static void toggle_hotarea(int32_t x_root, int32_t y_root); // 触发热区 static void maplayersurfacenotify(struct wl_listener *listener, void *data); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); @@ -503,6 +574,10 @@ static void createlocksurface(struct wl_listener *listener, void *data); static void createmon(struct wl_listener *listener, void *data); static void createnotify(struct wl_listener *listener, void *data); static void createpointer(struct wlr_pointer *pointer); +static void configure_pointer(struct libinput_device *device); +static void destroyinputdevice(struct wl_listener *listener, void *data); +static void createswitch(struct wlr_switch *switch_device); +static void switch_toggle(struct wl_listener *listener, void *data); static void createpointerconstraint(struct wl_listener *listener, void *data); static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); static void commitpopup(struct wl_listener *listener, void *data); @@ -513,7 +588,7 @@ static void destroydecoration(struct wl_listener *listener, void *data); static void destroydragicon(struct wl_listener *listener, void *data); static void destroyidleinhibitor(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); -static void destroylock(SessionLock *lock, int unlocked); +static void destroylock(SessionLock *lock, int32_t unlocked); static void destroylocksurface(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroypointerconstraint(struct wl_listener *listener, void *data); @@ -522,31 +597,30 @@ static void destroykeyboardgroup(struct wl_listener *listener, void *data); static Monitor *dirtomon(enum wlr_direction dir); static void setcursorshape(struct wl_listener *listener, void *data); -static void focusclient(Client *c, int lift); +static void focusclient(Client *c, int32_t lift); static void setborder_color(Client *c); static Client *focustop(Monitor *m); static void fullscreennotify(struct wl_listener *listener, void *data); static void gpureset(struct wl_listener *listener, void *data); -static int keyrepeat(void *data); +static int32_t keyrepeat(void *data); static void inputdevice(struct wl_listener *listener, void *data); -static int keybinding(unsigned int mods, xkb_keysym_t sym, - unsigned int keycode); +static int32_t keybinding(uint32_t state, bool locked, uint32_t mods, + xkb_keysym_t sym, uint32_t keycode); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static bool keypressglobal(struct wlr_surface *last_surface, struct wlr_keyboard *keyboard, - struct wlr_keyboard_key_event *event, - unsigned int mods, xkb_keysym_t keysym, - unsigned int keycode); + struct wlr_keyboard_key_event *event, uint32_t mods, + xkb_keysym_t keysym, uint32_t keycode); static void locksession(struct wl_listener *listener, void *data); static void mapnotify(struct wl_listener *listener, void *data); static void maximizenotify(struct wl_listener *listener, void *data); static void minimizenotify(struct wl_listener *listener, void *data); static void motionabsolute(struct wl_listener *listener, void *data); -static void motionnotify(unsigned int time, struct wlr_input_device *device, +static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx, double sy, double sx_unaccel, double sy_unaccel); static void motionrelative(struct wl_listener *listener, void *data); @@ -557,27 +631,28 @@ static void add_foreign_topleve(Client *c); static void exchange_two_client(Client *c1, Client *c2); static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, - int test); + int32_t test); static void outputmgrtest(struct wl_listener *listener, void *data); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, - double sy, unsigned int time); + double sy, uint32_t time); static void printstatus(void); -static void quitsignal(int signo); +static void quitsignal(int32_t signo); static void powermgrsetmode(struct wl_listener *listener, void *data); static void rendermon(struct wl_listener *listener, void *data); static void requestdecorationmode(struct wl_listener *listener, void *data); +static void requestdrmlease(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); -static void resize(Client *c, struct wlr_box geo, int interact); +static void resize(Client *c, struct wlr_box geo, int32_t interact); static void run(char *startup_cmd); static void setcursor(struct wl_listener *listener, void *data); -static void setfloating(Client *c, int floating); -static void setfakefullscreen(Client *c, int fakefullscreen); -static void setfullscreen(Client *c, int fullscreen); -static void setmaxmizescreen(Client *c, int maxmizescreen); -static void reset_maxmizescreen_size(Client *c); -static void setgaps(int oh, int ov, int ih, int iv); +static void setfloating(Client *c, int32_t floating); +static void setfakefullscreen(Client *c, int32_t fakefullscreen); +static void setfullscreen(Client *c, int32_t fullscreen); +static void setmaximizescreen(Client *c, int32_t maximizescreen); +static void reset_maximizescreen_size(Client *c); +static void setgaps(int32_t oh, int32_t ov, int32_t ih, int32_t iv); -static void setmon(Client *c, Monitor *m, unsigned int newtags, bool focus); +static void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus); static void setpsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data); static void setup(void); @@ -591,8 +666,10 @@ static void updatetitle(struct wl_listener *listener, void *data); static void urgent(struct wl_listener *listener, void *data); static void view(const Arg *arg, bool want_animation); -static void handlesig(int signo); - +static void handlesig(int32_t signo); +static void +handle_keyboard_shortcuts_inhibit_new_inhibitor(struct wl_listener *listener, + void *data); static void virtualkeyboard(struct wl_listener *listener, void *data); static void virtualpointer(struct wl_listener *listener, void *data); static void warp_cursor(const Client *c); @@ -601,16 +678,15 @@ static void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny); static void clear_fullscreen_flag(Client *c); static pid_t getparentprocess(pid_t p); -static int isdescprocess(pid_t p, pid_t c); +static int32_t isdescprocess(pid_t p, pid_t c); static Client *termforwin(Client *w); static void swallow(Client *c, Client *w); static void warp_cursor_to_selmon(Monitor *m); -unsigned int want_restore_fullscreen(Client *target_client); +uint32_t want_restore_fullscreen(Client *target_client); static void overview_restore(Client *c, const Arg *arg); static void overview_backup(Client *c); -static int applyrulesgeom(Client *c); -static void set_minized(Client *c); +static void set_minimized(Client *c); static void show_scratchpad(Client *c); static void show_hide_client(Client *c); @@ -618,61 +694,77 @@ static void tag_client(const Arg *arg, Client *target_client); static struct wlr_box setclient_coordinate_center(Client *c, struct wlr_box geom, - int offsetx, int offsety); -static unsigned int get_tags_first_tag(unsigned int tags); + int32_t offsetx, + int32_t offsety); +static uint32_t get_tags_first_tag(uint32_t tags); -static struct wlr_output_mode *get_output_mode(struct wlr_output *output, - int width, int height, - float refresh); +static struct wlr_output_mode * +get_nearest_output_mode(struct wlr_output *output, int32_t width, + int32_t height, float refresh); static void client_commit(Client *c); static void layer_commit(LayerSurface *l); static void apply_border(Client *c); static void client_set_opacity(Client *c, double opacity); static void init_baked_points(void); -static void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, int sx, - int sy, void *data); +static void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, void *data); static Client *direction_select(const Arg *arg); -static void view_in_mon(const Arg *arg, bool want_animation, Monitor *m); +static void view_in_mon(const Arg *arg, bool want_animation, Monitor *m, + bool changefocus); static void buffer_set_effect(Client *c, BufferData buffer_data); static void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, - int sx, int sy, void *data); + int32_t sx, int32_t sy, void *data); static void client_set_pending_state(Client *c); static void layer_set_pending_state(LayerSurface *l); -static void set_rect_size(struct wlr_scene_rect *rect, int width, int height); -static Client *center_select(Monitor *m); +static void set_rect_size(struct wlr_scene_rect *rect, int32_t width, + int32_t height); +static Client *center_tiled_select(Monitor *m); static void handlecursoractivity(void); -static int hidecursor(void *data); +static int32_t hidecursor(void *data); static bool check_hit_no_border(Client *c); static void reset_keyboard_layout(void); static void client_update_oldmonname_record(Client *c, Monitor *m); static void pending_kill_client(Client *c); -static unsigned int get_tags_first_tag_num(unsigned int source_tags); +static uint32_t get_tags_first_tag_num(uint32_t source_tags); static void set_layer_open_animaiton(LayerSurface *l, struct wlr_box geo); static void init_fadeout_layers(LayerSurface *l); -static void layer_actual_size(LayerSurface *l, unsigned int *width, - unsigned int *height); +static void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height); static void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box); -static void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, - int sy, void *data); -static double find_animation_curve_at(double t, int type); +static void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, void *data); +static double find_animation_curve_at(double t, int32_t type); static void apply_opacity_to_rect_nodes(Client *c, struct wlr_scene_node *node, double animation_passed); static enum corner_location set_client_corner_location(Client *c); -static double output_frame_duration_ms(); +static double all_output_frame_duration_ms(); static struct wlr_scene_tree * wlr_scene_tree_snapshot(struct wlr_scene_node *node, struct wlr_scene_tree *parent); static bool is_scroller_layout(Monitor *m); -void create_output(struct wlr_backend *backend, void *data); -char *get_layout_abbr(const char *full_name); -void apply_named_scratchpad(Client *target_client); -Client *get_client_by_id_or_title(const char *arg_id, const char *arg_title); -bool switch_scratchpad_client_state(Client *c); +static void create_output(struct wlr_backend *backend, void *data); +static void get_layout_abbr(char *abbr, const char *full_name); +static void apply_named_scratchpad(Client *target_client); +static Client *get_client_by_id_or_title(const char *arg_id, + const char *arg_title); +static bool switch_scratchpad_client_state(Client *c); +static bool check_trackpad_disabled(struct wlr_pointer *pointer); +static uint32_t get_tag_status(uint32_t tag, Monitor *m); +static void enable_adaptive_sync(Monitor *m, struct wlr_output_state *state); +static Client *get_next_stack_client(Client *c, bool reverse); +static void set_float_malposition(Client *tc); +static void set_size_per(Monitor *m, Client *c); +static void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time); +static void refresh_monitors_workspaces_status(Monitor *m); +static void init_client_properties(Client *c); +static float *get_border_color(Client *c); +static void clear_fullscreen_and_maximized_state(Monitor *m); +static void request_fresh_all_monitors(void); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -681,12 +773,11 @@ bool switch_scratchpad_client_state(Client *c); /* variables */ static const char broken[] = "broken"; static pid_t child_pid = -1; -static int locked; -static unsigned int locked_mods = 0; +static int32_t locked; +static uint32_t locked_mods = 0; static void *exclusive_focus; static struct wl_display *dpy; static struct wl_event_loop *event_loop; -static struct wlr_relative_pointer_manager_v1 *pointer_manager; static struct wlr_backend *backend; static struct wlr_backend *headless_backend; static struct wlr_scene *scene; @@ -707,9 +798,13 @@ static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_output_manager_v1 *output_mgr; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; +static struct wlr_keyboard_shortcuts_inhibit_manager_v1 + *keyboard_shortcuts_inhibit; static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; static struct wlr_output_power_manager_v1 *power_mgr; static struct wlr_pointer_gestures_v1 *pointer_gestures; +static struct wlr_drm_lease_v1_manager *drm_lease_manager; +struct mango_print_status_manager *print_status_manager; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -719,7 +814,7 @@ static struct wlr_scene_rect *root_bg; static struct wlr_session_lock_manager_v1 *session_lock_mgr; static struct wlr_scene_rect *locked_bg; static struct wlr_session_lock_v1 *cur_lock; -static const int layermap[] = {LyrBg, LyrBottom, LyrTop, LyrOverlay}; +static const int32_t layermap[] = {LyrBg, LyrBottom, LyrTop, LyrOverlay}; static struct wlr_scene_tree *drag_icon; static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; static struct wlr_pointer_constraints_v1 *pointer_constraints; @@ -728,53 +823,74 @@ static struct wlr_pointer_constraint_v1 *active_constraint; static struct wlr_seat *seat; static KeyboardGroup *kb_group; -static struct wl_list keyboards; -static unsigned int cursor_mode; +static struct wl_list inputdevices; +static struct wl_list keyboard_shortcut_inhibitors; +static uint32_t cursor_mode; static Client *grabc; -static int grabcx, grabcy; /* client-relative */ +static int32_t grabcx, grabcy; /* client-relative */ +static int32_t drag_begin_cursorx, drag_begin_cursory; /* client-relative */ +static bool start_drag_window = false; +static int32_t last_apply_drap_time = 0; static struct wlr_output_layout *output_layout; static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; -static int enablegaps = 1; /* enables gaps, used by togglegaps */ -static int axis_apply_time = 0; -static int axis_apply_dir = 0; -static int scroller_focus_lock = 0; +static int32_t enablegaps = 1; /* enables gaps, used by togglegaps */ +static int32_t axis_apply_time = 0; +static int32_t axis_apply_dir = 0; +static int32_t scroller_focus_lock = 0; -static unsigned int swipe_fingers = 0; +static uint32_t swipe_fingers = 0; static double swipe_dx = 0; static double swipe_dy = 0; bool render_border = true; +uint32_t chvt_backup_tag = 0; +bool allow_frame_scheduling = true; +char chvt_backup_selmon[32] = {0}; + struct dvec2 *baked_points_move; struct dvec2 *baked_points_open; struct dvec2 *baked_points_tag; struct dvec2 *baked_points_close; +struct dvec2 *baked_points_focus; +struct dvec2 *baked_points_opafadein; +struct dvec2 *baked_points_opafadeout; static struct wl_event_source *hide_source; static bool cursor_hidden = false; +static bool tag_combo = false; +static const char *cli_config_path = NULL; +static KeyMode keymode = { + .mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'}, + .isdefault = true, +}; static struct { enum wp_cursor_shape_device_v1_shape shape; struct wlr_surface *surface; - int hotspot_x; - int hotspot_y; + int32_t hotspot_x; + int32_t hotspot_y; } last_cursor; #include "client/client.h" #include "config/preset.h" struct Pertag { - unsigned int curtag, prevtag; /* current and previous tag */ - int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ - float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ - float smfacts[LENGTH(tags) + 1]; /* smfacts per tag */ + uint32_t curtag, prevtag; /* current and previous tag */ + int32_t nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + bool no_hide[LENGTH(tags) + 1]; /* no_hide per tag */ const Layout *ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */ }; +static struct wl_signal mango_print_status; + +static struct wl_listener print_status_listener = {.notify = + handle_print_status}; static struct wl_listener cursor_axis = {.notify = axisnotify}; static struct wl_listener cursor_button = {.notify = buttonpress}; static struct wl_listener cursor_frame = {.notify = cursorframe}; @@ -805,6 +921,9 @@ static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; static struct wl_listener request_start_drag = {.notify = requeststartdrag}; static struct wl_listener start_drag = {.notify = startdrag}; static struct wl_listener new_session_lock = {.notify = locksession}; +static struct wl_listener drm_lease_request = {.notify = requestdrmlease}; +static struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor = { + .notify = handle_keyboard_shortcuts_inhibit_new_inhibitor}; #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); @@ -823,10 +942,12 @@ static struct wlr_xwayland *xwayland; #include "animation/client.h" #include "animation/common.h" #include "animation/layer.h" +#include "animation/tag.h" #include "config/parse_config.h" #include "dispatch/bind_define.h" #include "ext-protocol/all.h" #include "fetch/fetch.h" +#include "layout/arrange.h" #include "layout/horizontal.h" #include "layout/vertical.h" @@ -834,14 +955,14 @@ void client_change_mon(Client *c, Monitor *m) { setmon(c, m, c->tags, true); reset_foreign_tolevel(c); if (c->isfloating) { - c->oldgeom = c->geom = setclient_coordinate_center(c, c->geom, 0, 0); + c->float_geom = c->geom = setclient_coordinate_center(c, c->geom, 0, 0); } } void applybounds(Client *c, struct wlr_box *bbox) { /* set minimum possible */ - c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); - c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height); + c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width); + c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height); if (c->geom.x >= bbox->x + bbox->width) c->geom.x = bbox->x + bbox->width - c->geom.width; @@ -853,47 +974,60 @@ void applybounds(Client *c, struct wlr_box *bbox) { c->geom.y = bbox->y; } +void clear_fullscreen_and_maximized_state(Monitor *m) { + Client *fc = NULL; + wl_list_for_each(fc, &clients, link) { + if (fc && VISIBLEON(fc, m) && ISFULLSCREEN(fc)) { + clear_fullscreen_flag(fc); + } + } +} + /*清除全屏标志,还原全屏时清0的border*/ void clear_fullscreen_flag(Client *c) { + + if ((c->mon->pertag->ltidxs[get_tags_first_tag_num(c->tags)]->id == + SCROLLER || + c->mon->pertag->ltidxs[get_tags_first_tag_num(c->tags)]->id == + VERTICAL_SCROLLER) && + !c->isfloating) { + return; + } + if (c->isfullscreen) { setfullscreen(c, false); } - if (c->ismaxmizescreen) { - setmaxmizescreen(c, 0); - } -} - -void minized(const Arg *arg) { - if (selmon->sel && !selmon->sel->isminied) { - set_minized(selmon->sel); + if (c->ismaximizescreen) { + setmaximizescreen(c, 0); } } void show_scratchpad(Client *c) { c->is_scratchpad_show = 1; - if (c->isfullscreen || c->ismaxmizescreen) { + if (c->isfullscreen || c->ismaximizescreen) { c->isfullscreen = 0; // 清除窗口全屏标志 - c->ismaxmizescreen = 0; + c->ismaximizescreen = 0; c->bw = c->isnoborder ? 0 : borderpx; } /* return if fullscreen */ if (!c->isfloating) { setfloating(c, 1); - c->geom.width = c->scratchpad_width - ? c->scratchpad_width + c->geom.width = c->iscustomsize + ? c->float_geom.width : c->mon->w.width * scratchpad_width_ratio; - c->geom.height = c->scratchpad_height - ? c->scratchpad_height + c->geom.height = c->iscustomsize + ? c->float_geom.height : c->mon->w.height * scratchpad_height_ratio; // 重新计算居中的坐标 - c->oldgeom = c->geom = c->animainit_geom = c->animation.current = + c->float_geom = c->geom = c->animainit_geom = c->animation.current = setclient_coordinate_center(c, c->geom, 0, 0); + c->iscustomsize = 1; resize(c, c->geom, 0); } - c->oldtags = selmon->tagset[selmon->seltags]; + c->oldtags = c->mon->tagset[c->mon->seltags]; wl_list_remove(&c->link); // 从原来位置移除 wl_list_insert(clients.prev->next, &c->link); // 插入开头 show_hide_client(c); @@ -913,12 +1047,13 @@ void swallow(Client *c, Client *w) { c->isfloating = w->isfloating; c->isurgent = w->isurgent; c->isfullscreen = w->isfullscreen; - c->ismaxmizescreen = w->ismaxmizescreen; - c->isminied = w->isminied; + c->ismaximizescreen = w->ismaximizescreen; + c->isminimized = w->isminimized; c->is_in_scratchpad = w->is_in_scratchpad; c->is_scratchpad_show = w->is_scratchpad_show; c->tags = w->tags; c->geom = w->geom; + c->float_geom = w->float_geom; c->scroller_proportion = w->scroller_proportion; wl_list_insert(&w->link, &c->link); wl_list_insert(&w->flink, &c->flink); @@ -933,7 +1068,7 @@ void swallow(Client *c, Client *w) { if (!c->foreign_toplevel && c->mon) add_foreign_toplevel(c); - if (c->isminied && c->foreign_toplevel) { + if (c->isminimized && c->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, false); wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, true); @@ -941,15 +1076,47 @@ void swallow(Client *c, Client *w) { } bool switch_scratchpad_client_state(Client *c) { + + if (scratchpad_cross_monitor && selmon && c->mon != selmon && + c->is_in_scratchpad) { + // 保存原始monitor用于尺寸计算 + Monitor *oldmon = c->mon; + c->scratchpad_switching_mon = true; + c->mon = selmon; + reset_foreign_tolevel(c); + client_update_oldmonname_record(c, selmon); + + // 根据新monitor调整窗口尺寸 + c->float_geom.width = + (int32_t)(c->float_geom.width * c->mon->w.width / oldmon->w.width); + c->float_geom.height = (int32_t)(c->float_geom.height * + c->mon->w.height / oldmon->w.height); + + c->float_geom = setclient_coordinate_center(c, c->float_geom, 0, 0); + + // 只有显示状态的scratchpad才需要聚焦和返回true + if (c->is_scratchpad_show) { + c->tags = get_tags_first_tag(selmon->tagset[selmon->seltags]); + resize(c, c->float_geom, 0); + arrange(selmon, false, false); + focusclient(c, true); + c->scratchpad_switching_mon = false; + return true; + } else { + resize(c, c->float_geom, 0); + c->scratchpad_switching_mon = false; + } + } + if (c->is_in_scratchpad && c->is_scratchpad_show && - (selmon->tagset[selmon->seltags] & c->tags) == 0) { - unsigned int target = - get_tags_first_tag(selmon->tagset[selmon->seltags]); - tag_client(&(Arg){.ui = target}, c); + (c->mon->tagset[c->mon->seltags] & c->tags) == 0) { + c->tags = c->mon->tagset[c->mon->seltags]; + arrange(c->mon, false, false); + focusclient(c, true); return true; } else if (c->is_in_scratchpad && c->is_scratchpad_show && - (selmon->tagset[selmon->seltags] & c->tags) != 0) { - set_minized(c); + (c->mon->tagset[c->mon->seltags] & c->tags) != 0) { + set_minimized(c); return true; } else if (c && c->is_in_scratchpad && !c->is_scratchpad_show) { show_scratchpad(c); @@ -962,17 +1129,19 @@ bool switch_scratchpad_client_state(Client *c) { void apply_named_scratchpad(Client *target_client) { Client *c = NULL; wl_list_for_each(c, &clients, link) { - if (c->mon != selmon) { + + if (!scratchpad_cross_monitor && c->mon != selmon) { continue; } + if (single_scratchpad && c->is_in_scratchpad && c->is_scratchpad_show && c != target_client) { - set_minized(c); + set_minimized(c); } } if (!target_client->is_in_scratchpad) { - set_minized(target_client); + set_minimized(target_client); switch_scratchpad_client_state(target_client); } else switch_scratchpad_client_state(target_client); @@ -981,7 +1150,7 @@ void apply_named_scratchpad(Client *target_client) { void gpureset(struct wl_listener *listener, void *data) { struct wlr_renderer *old_drw = drw; struct wlr_allocator *old_alloc = alloc; - struct Monitor *m; + struct Monitor *m = NULL; wlr_log(WLR_DEBUG, "gpu reset"); @@ -1004,7 +1173,7 @@ void gpureset(struct wl_listener *listener, void *data) { wlr_renderer_destroy(old_drw); } -void handlesig(int signo) { +void handlesig(int32_t signo) { if (signo == SIGCHLD) while (waitpid(-1, NULL, WNOHANG) > 0) ; @@ -1012,7 +1181,7 @@ void handlesig(int signo) { quit(NULL); } -void toggle_hotarea(int x_root, int y_root) { +void toggle_hotarea(int32_t x_root, int32_t y_root) { // 左下角热区坐标计算,兼容多显示屏 Arg arg = {0}; @@ -1041,13 +1210,20 @@ void toggle_hotarea(int x_root, int y_root) { static void apply_rule_properties(Client *c, const ConfigWinRule *r) { APPLY_INT_PROP(c, r, isterm); + APPLY_INT_PROP(c, r, allow_csd); + APPLY_INT_PROP(c, r, force_maximize); + APPLY_INT_PROP(c, r, force_tearing); APPLY_INT_PROP(c, r, noswallow); + APPLY_INT_PROP(c, r, nofocus); APPLY_INT_PROP(c, r, nofadein); APPLY_INT_PROP(c, r, nofadeout); APPLY_INT_PROP(c, r, no_force_center); APPLY_INT_PROP(c, r, isfloating); APPLY_INT_PROP(c, r, isfullscreen); APPLY_INT_PROP(c, r, isnoborder); + APPLY_INT_PROP(c, r, isnoshadow); + APPLY_INT_PROP(c, r, isnoradius); + APPLY_INT_PROP(c, r, isnoanimation); APPLY_INT_PROP(c, r, isopensilent); APPLY_INT_PROP(c, r, istagsilent); APPLY_INT_PROP(c, r, isnamedscratchpad); @@ -1057,11 +1233,11 @@ static void apply_rule_properties(Client *c, const ConfigWinRule *r) { APPLY_INT_PROP(c, r, ignore_minimize); APPLY_INT_PROP(c, r, isnosizehint); APPLY_INT_PROP(c, r, isunglobal); - APPLY_INT_PROP(c, r, scratchpad_width); - APPLY_INT_PROP(c, r, scratchpad_height); APPLY_INT_PROP(c, r, noblur); + APPLY_INT_PROP(c, r, allow_shortcuts_inhibit); APPLY_FLOAT_PROP(c, r, scroller_proportion); + APPLY_FLOAT_PROP(c, r, scroller_proportion_single); APPLY_FLOAT_PROP(c, r, focused_opacity); APPLY_FLOAT_PROP(c, r, unfocused_opacity); @@ -1069,56 +1245,61 @@ static void apply_rule_properties(Client *c, const ConfigWinRule *r) { APPLY_STRING_PROP(c, r, animation_type_close); } -int applyrulesgeom(Client *c) { - /* rule matching */ - const char *appid, *title; - ConfigWinRule *r; - int hit = 0; - int ji; +void set_float_malposition(Client *tc) { + Client *c = NULL; + int32_t x, y, offset, xreverse, yreverse; + x = tc->geom.x; + y = tc->geom.y; + xreverse = 1; + yreverse = 1; + offset = MIN(tc->mon->w.width / 20, tc->mon->w.height / 20); - if (!(appid = client_get_appid(c))) - appid = broken; - if (!(title = client_get_title(c))) - title = broken; + wl_list_for_each(c, &clients, link) { + if (c->isfloating && c != tc && VISIBLEON(c, tc->mon) && + abs(x - c->geom.x) < offset && abs(y - c->geom.y) < offset) { - for (ji = 0; ji < config.window_rules_count; ji++) { - if (config.window_rules_count < 1) - break; - r = &config.window_rules[ji]; + x = c->geom.x + offset * xreverse; + y = c->geom.y + offset * yreverse; + if (x < tc->mon->w.x) { + x = x + offset; + xreverse = 1; + } - if (!is_window_rule_matches(r, appid, title)) - continue; + if (y < tc->mon->w.y) { + y = y + offset; + yreverse = 1; + } - c->geom.width = r->width > 0 ? r->width : c->geom.width; - c->geom.height = r->height > 0 ? r->height : c->geom.height; + if (x + tc->geom.width > tc->mon->w.x + tc->mon->w.width) { + x = x - offset; + xreverse = -1; + } - if (!c->isnosizehint) - client_set_size_bound(c); - - // 重新计算居中的坐标 - if (r->offsetx != 0 || r->offsety != 0 || r->width > 0 || r->height > 0) - c->geom = - setclient_coordinate_center(c, c->geom, r->offsetx, r->offsety); - if (r->height > 0 || r->width > 0 || r->offsetx != 0 || - r->offsety != 0) { - hit = 1; - } else { - hit = 0; + if (y + tc->geom.height > tc->mon->w.y + tc->mon->w.height) { + y = y - offset; + yreverse = -1; + } } } - return hit; + + tc->float_geom.x = tc->geom.x = x; + tc->float_geom.y = tc->geom.y = y; } void applyrules(Client *c) { /* rule matching */ const char *appid, *title; - unsigned int i, newtags = 0; + uint32_t i, newtags = 0; const ConfigWinRule *r; - Monitor *mon = selmon, *m; - Client *fc; - bool hit_rule_pos = false; + Monitor *m = NULL; + Client *fc = NULL; + Client *parent = NULL; - c->isfloating = client_is_float_type(c); + parent = client_get_parent(c); + + Monitor *mon = parent && parent->mon ? parent->mon : selmon; + + c->isfloating = client_is_float_type(c) || parent; if (!(appid = client_get_appid(c))) appid = broken; if (!(title = client_get_title(c))) @@ -1135,8 +1316,12 @@ void applyrules(Client *c) { // set general properties apply_rule_properties(c, r); - // set tags - newtags |= (r->tags > 0) ? r->tags : 0; + // // set tags + if (r->tags > 0) { + newtags |= r->tags; + } else if (parent) { + newtags = parent->tags; + } // set monitor of client wl_list_for_each(m, &mons, link) { @@ -1145,29 +1330,45 @@ void applyrules(Client *c) { } } - // set geometry of floating client - if (c->isfloating) { - if (r->width > 0) - c->geom.width = r->width; - if (r->height > 0) - c->geom.height = r->height; + if (c->isnamedscratchpad) { + c->isfloating = 1; + } + // set geometry of floating client + + if (r->width > 0) + c->float_geom.width = r->width; + if (r->height > 0) + c->float_geom.height = r->height; + + if (r->width > 0 || r->height > 0) { + c->iscustomsize = 1; + } + + if (r->offsetx || r->offsety) { + c->iscustompos = 1; + c->float_geom = c->geom = setclient_coordinate_center( + c, c->float_geom, r->offsetx, r->offsety); + } + if (c->isfloating) { + c->geom = c->float_geom.width > 0 && c->float_geom.height > 0 + ? c->float_geom + : c->geom; if (!c->isnosizehint) client_set_size_bound(c); - - if (r->offsetx || r->offsety || r->width > 0 || r->height > 0) { - hit_rule_pos = true; - c->oldgeom = c->geom = setclient_coordinate_center( - c, c->geom, r->offsetx, r->offsety); - } } } + if (mon) + set_size_per(mon, c); + // if no geom rule hit and is normal winodw, use the center pos and record // the hit size - if (!hit_rule_pos && - (!client_is_x11(c) || !client_should_ignore_focus(c))) { - c->oldgeom = c->geom = setclient_coordinate_center(c, c->geom, 0, 0); + if (!c->iscustompos && + (!client_is_x11(c) || (c->geom.x == 0 && c->geom.y == 0))) { + c->float_geom = c->geom = setclient_coordinate_center(c, c->geom, 0, 0); + } else { + c->float_geom = c->geom; } /*-----------------------apply rule action-------------------------*/ @@ -1181,7 +1382,7 @@ void applyrules(Client *c) { if (!c->noswallow && !c->isfloating && !client_is_float_type(c) && !c->surface.xdg->initial_commit) { Client *p = termforwin(c); - if (p) { + if (p && !p->isminimized) { c->swallowedby = p; p->swallowing = c; wl_list_remove(&c->link); @@ -1194,24 +1395,19 @@ void applyrules(Client *c) { } } - // set mon and floating, fullscreen and scratchpad state - - if (c->isnamedscratchpad) { - c->isfloating = 1; - } - - int fullscreen_state_backup = c->isfullscreen || client_wants_fullscreen(c); + int32_t fullscreen_state_backup = + c->isfullscreen || client_wants_fullscreen(c); setmon(c, mon, newtags, - !c->isopensilent && (!c->istagsilent || !newtags || - newtags & mon->tagset[mon->seltags])); + !c->isopensilent && + !(client_is_x11_popup(c) && client_should_ignore_focus(c)) && + (!c->istagsilent || !newtags || + newtags & mon->tagset[mon->seltags])); - if (!c->isopensilent && - (!c->istagsilent || c->tags & c->mon->tagset[c->mon->seltags]) && - c->mon && - ((c->mon && c->mon != selmon) || - !(c->tags & (1 << (c->mon->pertag->curtag - 1))))) { + if (c->mon && + !(c->mon == selmon && c->tags & c->mon->tagset[c->mon->seltags]) && + !c->isopensilent && !c->istagsilent) { c->animation.tag_from_rule = true; - view_in_mon(&(Arg){.ui = c->tags}, true, c->mon); + view_in_mon(&(Arg){.ui = c->tags}, true, c->mon, true); } setfullscreen(c, fullscreen_state_backup); @@ -1222,9 +1418,16 @@ void applyrules(Client *c) { */ wl_list_for_each(fc, &clients, link) if (fc && fc != c && c->tags & fc->tags && - ISFULLSCREEN(fc) && !c->isfloating) { + VISIBLEON(fc, c->mon) && ISFULLSCREEN(fc) && + !c->isfloating) { clear_fullscreen_flag(fc); - arrange(c->mon, false); + arrange(c->mon, false, false); + } + + if (c->isfloating && !c->iscustompos && !c->isnamedscratchpad) { + wl_list_remove(&c->link); + wl_list_insert(clients.prev, &c->link); + set_float_malposition(c); } // apply named scratchpad rule @@ -1234,151 +1437,14 @@ void applyrules(Client *c) { // apply overlay rule if (c->isoverlay) { - wlr_scene_node_reparent(&selmon->sel->scene->node, layers[LyrOverlay]); - wlr_scene_node_raise_to_top(&selmon->sel->scene->node); + wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); + wlr_scene_node_raise_to_top(&c->scene->node); } } -void // 17 -arrange(Monitor *m, bool want_animation) { - Client *c; - - if (!m) - return; - - if (!m->wlr_output->enabled) - return; - - m->visible_clients = 0; - m->visible_tiling_clients = 0; - wl_list_for_each(c, &clients, link) { - if (c->iskilling) - continue; - - if (c->mon == m && (c->isglobal || c->isunglobal)) { - c->tags = m->tagset[m->seltags]; - if (selmon->sel == NULL) - focusclient(c, 0); - } - - if (c->mon == m) { - if (VISIBLEON(c, m)) { - - if (!client_is_unmanaged(c) && !client_should_ignore_focus(c)) { - m->visible_clients++; - } - - if (ISTILED(c)) { - m->visible_tiling_clients++; - } - - if (!c->is_clip_to_hide || !ISTILED(c) || - !is_scroller_layout(c->mon)) { - c->is_clip_to_hide = false; - wlr_scene_node_set_enabled(&c->scene->node, true); - wlr_scene_node_set_enabled(&c->scene_surface->node, true); - } - client_set_suspended(c, false); - if (!c->animation.tag_from_rule && want_animation && - m->pertag->prevtag != 0 && m->pertag->curtag != 0 && - animations) { - c->animation.tagining = true; - if (m->pertag->curtag > m->pertag->prevtag) { - if (c->animation.running) { - c->animainit_geom.x = c->animation.current.x; - c->animainit_geom.y = c->animation.current.y; - } else { - c->animainit_geom.x = - tag_animation_direction == VERTICAL - ? c->animation.current.x - : c->mon->m.x + c->mon->m.width; - c->animainit_geom.y = - tag_animation_direction == VERTICAL - ? c->mon->m.y + c->mon->m.height - : c->animation.current.y; - } - - } else { - if (c->animation.running) { - c->animainit_geom.x = c->animation.current.x; - c->animainit_geom.y = c->animation.current.y; - } else { - c->animainit_geom.x = - tag_animation_direction == VERTICAL - ? c->animation.current.x - : m->m.x - c->geom.width; - c->animainit_geom.y = - tag_animation_direction == VERTICAL - ? m->m.y - c->geom.height - : c->animation.current.y; - } - } - } else { - c->animainit_geom.x = c->animation.current.x; - c->animainit_geom.y = c->animation.current.y; - } - - c->animation.tag_from_rule = false; - c->animation.tagouting = false; - c->animation.tagouted = false; - resize(c, c->geom, 0); - - } else { - if ((c->tags & (1 << (m->pertag->prevtag - 1))) && - m->pertag->prevtag != 0 && m->pertag->curtag != 0 && - animations) { - c->animation.tagouting = true; - c->animation.tagining = false; - if (m->pertag->curtag > m->pertag->prevtag) { - c->pending = c->geom; - c->pending.x = tag_animation_direction == VERTICAL - ? c->animation.current.x - : c->mon->m.x - c->geom.width; - c->pending.y = tag_animation_direction == VERTICAL - ? c->mon->m.y - c->geom.height - : c->animation.current.y; - - resize(c, c->geom, 0); - } else { - c->pending = c->geom; - c->pending.x = tag_animation_direction == VERTICAL - ? c->animation.current.x - : c->mon->m.x + c->mon->m.width; - c->pending.y = tag_animation_direction == VERTICAL - ? c->mon->m.y + c->mon->m.height - : c->animation.current.y; - resize(c, c->geom, 0); - } - } else { - wlr_scene_node_set_enabled(&c->scene->node, false); - client_set_suspended(c, true); - } - } - } - - if (c->mon == m && c->ismaxmizescreen && !c->animation.tagouted && - !c->animation.tagouting && VISIBLEON(c, m)) { - reset_maxmizescreen_size(c); - } - } - - if (m->isoverview) { - overviewlayout.arrange(m); - } else if (m && m->pertag->curtag && - m->pertag->ltidxs[m->pertag->curtag]->arrange) { - m->pertag->ltidxs[m->pertag->curtag]->arrange(m); - } else if (m && m->pertag->prevtag && - m->pertag->ltidxs[m->pertag->prevtag]->arrange) { - m->pertag->ltidxs[m->pertag->prevtag]->arrange(m); - } - - motionnotify(0, NULL, 0, 0, 0, 0); - checkidleinhibitor(NULL); -} - void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, - int exclusive) { - LayerSurface *l; + int32_t exclusive) { + LayerSurface *l = NULL; struct wlr_box full_area = m->m; wl_list_for_each(l, list, link) { @@ -1396,24 +1462,24 @@ void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, } void apply_window_snap(Client *c) { - int snap_up = 99999, snap_down = 99999, snap_left = 99999, - snap_right = 99999; - int snap_up_temp = 0, snap_down_temp = 0, snap_left_temp = 0, - snap_right_temp = 0; - int snap_up_screen = 0, snap_down_screen = 0, snap_left_screen = 0, - snap_right_screen = 0; - int snap_up_mon = 0, snap_down_mon = 0, snap_left_mon = 0, - snap_right_mon = 0; + int32_t snap_up = 99999, snap_down = 99999, snap_left = 99999, + snap_right = 99999; + int32_t snap_up_temp = 0, snap_down_temp = 0, snap_left_temp = 0, + snap_right_temp = 0; + int32_t snap_up_screen = 0, snap_down_screen = 0, snap_left_screen = 0, + snap_right_screen = 0; + int32_t snap_up_mon = 0, snap_down_mon = 0, snap_left_mon = 0, + snap_right_mon = 0; - unsigned int cbw = !render_border || c->fake_no_border ? borderpx : 0; - unsigned int tcbw; - unsigned int cx, cy, cw, ch, tcx, tcy, tcw, tch; + uint32_t cbw = !render_border || c->fake_no_border ? borderpx : 0; + uint32_t tcbw; + uint32_t cx, cy, cw, ch, tcx, tcy, tcw, tch; cx = c->geom.x + cbw; cy = c->geom.y + cbw; cw = c->geom.width - 2 * cbw; ch = c->geom.height - 2 * cbw; - Client *tc; + Client *tc = NULL; if (!c || !c->mon || !client_surface(c)->mapped || c->iskilling) return; @@ -1494,40 +1560,55 @@ void apply_window_snap(Client *c) { c->geom.y = c->geom.y + snap_down; } - c->oldgeom = c->geom; + c->float_geom = c->geom; resize(c, c->geom, 0); } +void focuslayer(LayerSurface *l) { + focusclient(NULL, 0); + dwl_im_relay_set_focus(dwl_input_method_relay, l->layer_surface->surface); + client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); +} + void reset_exclusive_layer(Monitor *m) { - LayerSurface *l; - int i; - unsigned int layers_above_shell[] = { + LayerSurface *l = NULL; + int32_t i; + uint32_t layers_above_shell[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, }; if (!m) return; - for (i = 0; i < (int)LENGTH(layers_above_shell); i++) { + for (i = 0; i < (int32_t)LENGTH(layers_above_shell); i++) { wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) { + if (l == exclusive_focus && + l->layer_surface->current.keyboard_interactive != + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) + exclusive_focus = NULL; + if (l->layer_surface->current.keyboard_interactive == + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE && + l->layer_surface->surface == + seat->keyboard_state.focused_surface) + focusclient(focustop(selmon), 1); + if (locked || l->layer_surface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE || !l->mapped || l == exclusive_focus) continue; /* Deactivate the focused client. */ - focusclient(NULL, 0); exclusive_focus = l; - client_notify_enter(l->layer_surface->surface, - wlr_seat_get_keyboard(seat)); + focuslayer(l); return; } } } void arrangelayers(Monitor *m) { - int i; + int32_t i; struct wlr_box usable_area = m->m; if (!m->wlr_output->enabled) @@ -1539,7 +1620,7 @@ void arrangelayers(Monitor *m) { if (!wlr_box_equal(&usable_area, &m->w)) { m->w = usable_area; - arrange(m, false); + arrange(m, false, false); } /* Arrange non-exlusive surfaces from top->bottom */ @@ -1557,14 +1638,18 @@ axisnotify(struct wl_listener *listener, void *data) { * for example when you move the scroll wheel. */ struct wlr_pointer_axis_event *event = data; struct wlr_keyboard *keyboard, *hard_keyboard; - unsigned int mods, hard_mods; + uint32_t mods, hard_mods; AxisBinding *a; - int ji; - unsigned int adir; + int32_t ji; + uint32_t adir; // IDLE_NOTIFY_ACTIVITY; handlecursoractivity(); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + if (check_trackpad_disabled(event->pointer)) { + return; + } + hard_keyboard = &kb_group->wlr_group->keyboard; hard_mods = hard_keyboard ? wlr_keyboard_get_modifiers(hard_keyboard) : 0; @@ -1602,21 +1687,22 @@ axisnotify(struct wl_listener *listener, void *data) { * implemented checking the event's orientation and the delta of the event */ /* Notify the client with pointer focus of the axis event. */ - wlr_seat_pointer_notify_axis(seat, // 滚轮事件发送给客户端也就是窗口 - event->time_msec, event->orientation, - event->delta, event->delta_discrete, - event->source, event->relative_direction); + wlr_seat_pointer_notify_axis( + seat, // 滚轮事件发送给客户端也就是窗口 + event->time_msec, event->orientation, event->delta * axis_scroll_factor, + roundf(event->delta_discrete * axis_scroll_factor), event->source, + event->relative_direction); } -int ongesture(struct wlr_pointer_swipe_end_event *event) { +int32_t ongesture(struct wlr_pointer_swipe_end_event *event) { struct wlr_keyboard *keyboard, *hard_keyboard; - unsigned int mods, hard_mods; + uint32_t mods, hard_mods; const GestureBinding *g; - unsigned int motion; - unsigned int adx = (int)round(fabs(swipe_dx)); - unsigned int ady = (int)round(fabs(swipe_dy)); - int handled = 0; - int ji; + uint32_t motion; + uint32_t adx = (int32_t)round(fabs(swipe_dx)); + uint32_t ady = (int32_t)round(fabs(swipe_dy)); + int32_t handled = 0; + int32_t ji; if (event->cancelled) { return handled; @@ -1732,12 +1818,12 @@ void place_drag_tile_client(Client *c) { Client *closest_client = NULL; long min_distant = LONG_MAX; long temp_distant; - int x, y; + int32_t x, y; wl_list_for_each(tc, &clients, link) { if (tc != c && ISTILED(tc) && VISIBLEON(tc, c->mon)) { - x = tc->geom.x + (int)(tc->geom.width / 2) - cursor->x; - y = tc->geom.y + (int)(tc->geom.height / 2) - cursor->y; + x = tc->geom.x + (int32_t)(tc->geom.width / 2) - cursor->x; + y = tc->geom.y + (int32_t)(tc->geom.height / 2) - cursor->y; temp_distant = x * x + y * y; if (temp_distant < min_distant) { min_distant = temp_distant; @@ -1757,23 +1843,45 @@ void place_drag_tile_client(Client *c) { setfloating(c, 0); } +bool check_trackpad_disabled(struct wlr_pointer *pointer) { + struct libinput_device *device; + + if (!disable_trackpad) + return false; + + if (wlr_input_device_is_libinput(&pointer->base) && + (device = wlr_libinput_get_device_handle(&pointer->base))) { + + // 如果是触摸板且被禁用,忽略事件 + if (libinput_device_config_tap_get_finger_count(device) > 0) { + return true; // 不处理事件 + } + } + + return false; +} + void // 鼠标按键事件 buttonpress(struct wl_listener *listener, void *data) { struct wlr_pointer_button_event *event = data; struct wlr_keyboard *hard_keyboard, *keyboard; - unsigned int hard_mods, mods; - Client *c; - LayerSurface *l; + uint32_t hard_mods, mods; + Client *c = NULL; + LayerSurface *l = NULL; struct wlr_surface *surface; - Client *tmpc; - int ji; - const MouseBinding *b; + Client *tmpc = NULL; + int32_t ji; + const MouseBinding *m; struct wlr_surface *old_pointer_focus_surface = seat->pointer_state.focused_surface; handlecursoractivity(); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + if (check_trackpad_disabled(event->pointer)) { + return; + } + switch (event->state) { case WL_POINTER_BUTTON_STATE_PRESSED: cursor_mode = CurPressed; @@ -1783,7 +1891,8 @@ buttonpress(struct wl_listener *listener, void *data) { xytonode(cursor->x, cursor->y, &surface, NULL, NULL, NULL, NULL); if (toplevel_from_wlr_surface(surface, &c, &l) >= 0) { - if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) + if (c && c->scene->node.enabled && + (!client_is_unmanaged(c) || client_wants_focus(c))) focusclient(c, 1); if (surface != old_pointer_focus_surface) { @@ -1795,9 +1904,7 @@ buttonpress(struct wl_listener *listener, void *data) { if (l && !exclusive_focus && l->layer_surface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) { - focusclient(NULL, 0); - client_notify_enter(l->layer_surface->surface, - wlr_seat_get_keyboard(seat)); + focuslayer(l); } } @@ -1815,16 +1922,16 @@ buttonpress(struct wl_listener *listener, void *data) { for (ji = 0; ji < config.mouse_bindings_count; ji++) { if (config.mouse_bindings_count < 1) break; - b = &config.mouse_bindings[ji]; - if (CLEANMASK(mods) == CLEANMASK(b->mod) && - event->button == b->button && b->func && - (selmon->isoverview == 1 || b->button == BTN_MIDDLE) && c) { - b->func(&b->arg); + m = &config.mouse_bindings[ji]; + if (CLEANMASK(mods) == CLEANMASK(m->mod) && + event->button == m->button && m->func && + (selmon->isoverview == 1 || m->button == BTN_MIDDLE) && c) { + m->func(&m->arg); return; - } else if (CLEANMASK(mods) == CLEANMASK(b->mod) && - event->button == b->button && b->func && - CLEANMASK(b->mod) != 0) { - b->func(&b->arg); + } else if (CLEANMASK(mods) == CLEANMASK(m->mod) && + event->button == m->button && m->func && + CLEANMASK(m->mod) != 0) { + m->func(&m->arg); return; } } @@ -1846,10 +1953,12 @@ buttonpress(struct wl_listener *listener, void *data) { client_update_oldmonname_record(grabc, selmon); setmon(grabc, selmon, 0, true); reset_foreign_tolevel(grabc); - selmon->prevsel = selmon->sel; + selmon->prevsel = ISTILED(selmon->sel) ? selmon->sel : NULL; selmon->sel = grabc; tmpc = grabc; grabc = NULL; + start_drag_window = false; + last_apply_drap_time = 0; if (tmpc->drag_to_tile && drag_tile_to_tile) { place_drag_tile_client(tmpc); } else { @@ -1869,16 +1978,27 @@ buttonpress(struct wl_listener *listener, void *data) { } void checkidleinhibitor(struct wlr_surface *exclude) { - int inhibited = 0, unused_lx, unused_ly; + int32_t inhibited = 0; + Client *c = NULL; + struct wlr_surface *surface = NULL; struct wlr_idle_inhibitor_v1 *inhibitor; + wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) { - struct wlr_surface *surface = - wlr_surface_get_root_surface(inhibitor->surface); + surface = wlr_surface_get_root_surface(inhibitor->surface); + + if (exclude == surface) { + continue; + } + + toplevel_from_wlr_surface(inhibitor->surface, &c, NULL); + + if (idleinhibit_ignore_visible) { + inhibited = 1; + break; + } + struct wlr_scene_tree *tree = surface->data; - if (exclude != surface && - (inhibit_regardless_of_visibility || - (!tree || - wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) { + if (!tree || (tree->node.enabled && (!c || !c->animation.tagouting))) { inhibited = 1; break; } @@ -1904,6 +2024,7 @@ void setcursorshape(struct wl_listener *listener, void *data) { } void cleanuplisteners(void) { + wl_list_remove(&print_status_listener.link); wl_list_remove(&cursor_axis.link); wl_list_remove(&cursor_button.link); wl_list_remove(&cursor_frame.link); @@ -1932,6 +2053,11 @@ void cleanuplisteners(void) { wl_list_remove(&request_start_drag.link); wl_list_remove(&start_drag.link); wl_list_remove(&new_session_lock.link); + wl_list_remove(&tearing_new_object.link); + wl_list_remove(&keyboard_shortcuts_inhibit_new_inhibitor.link); + if (drm_lease_manager) { + wl_list_remove(&drm_lease_request.link); + } #ifdef XWAYLAND wl_list_remove(&new_xwayland_surface.link); wl_list_remove(&xwayland_ready.link); @@ -1966,22 +2092,10 @@ void cleanup(void) { wlr_scene_node_destroy(&scene->tree.node); } -void // 17 -cleanupkeyboard(struct wl_listener *listener, void *data) { - Keyboard *kb = wl_container_of(listener, kb, destroy); - - wl_event_source_remove(kb->key_repeat_source); - wl_list_remove(&kb->link); - wl_list_remove(&kb->modifiers.link); - wl_list_remove(&kb->key.link); - wl_list_remove(&kb->destroy.link); - free(kb); -} - void cleanupmon(struct wl_listener *listener, void *data) { Monitor *m = wl_container_of(listener, m, destroy); - LayerSurface *l, *tmp; - unsigned int i; + LayerSurface *l = NULL, *tmp = NULL; + uint32_t i; /* m->layers[i] are intentionally not unlinked */ for (i = 0; i < LENGTH(m->layers); i++) { @@ -2009,6 +2123,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { wlr_scene_node_destroy(&m->blur->node); m->blur = NULL; } + m->wlr_output->data = NULL; free(m->pertag); free(m); } @@ -2016,8 +2131,8 @@ void cleanupmon(struct wl_listener *listener, void *data) { void closemon(Monitor *m) { /* update selmon if needed and * move closed monitor's clients to the focused one */ - Client *c; - int i = 0, nmons = wl_list_length(&mons); + Client *c = NULL; + int32_t i = 0, nmons = wl_list_length(&mons); if (!nmons) { selmon = NULL; } else if (m == selmon) { @@ -2050,8 +2165,8 @@ void closemon(Monitor *m) { } } -static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx, - int sy, void *user_data) { +static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, + int32_t sx, int32_t sy, void *user_data) { struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer); if (!scene_surface) { @@ -2070,7 +2185,7 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx, void maplayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *l = wl_container_of(listener, l, map); struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; - int ji; + int32_t ji; ConfigLayerRule *r; l->mapped = 1; @@ -2130,9 +2245,7 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { if (!exclusive_focus && l->layer_surface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) { - focusclient(NULL, 0); - client_notify_enter(l->layer_surface->surface, - wlr_seat_get_keyboard(seat)); + focuslayer(l); } } @@ -2227,6 +2340,7 @@ void commitnotify(struct wl_listener *listener, void *data) { if (c->surface.xdg->initial_commit) { // xdg client will first enter this before mapnotify + init_client_properties(c); applyrules(c); if (c->mon) { client_set_scale(client_surface(c), c->mon->wlr_output->scale); @@ -2242,10 +2356,14 @@ void commitnotify(struct wl_listener *listener, void *data) { c->configure_serial = serial; } - uint32_t wm_caps = WLR_XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE; + uint32_t wm_caps = WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN; + + if (!c->ignore_minimize) + wm_caps |= WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE; + + if (!c->ignore_maximize) + wm_caps |= WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE; + wlr_xdg_toplevel_set_wm_capabilities(c->surface.xdg->toplevel, wm_caps); if (c->mon) { @@ -2298,7 +2416,7 @@ void commitpopup(struct wl_listener *listener, void *data) { LayerSurface *l = NULL; Client *c = NULL; struct wlr_box box; - int type = -1; + int32_t type = -1; if (!popup->base->initial_commit) return; @@ -2306,6 +2424,9 @@ void commitpopup(struct wl_listener *listener, void *data) { type = toplevel_from_wlr_surface(popup->base->surface, &c, &l); if (!popup->parent || type < 0) return; + + wlr_scene_node_raise_to_top(popup->parent->data); + popup->base->surface->data = wlr_scene_xdg_surface_create(popup->parent->data, popup->base); if ((l && !l->mon) || (c && !c->mon)) { @@ -2316,6 +2437,7 @@ void commitpopup(struct wl_listener *listener, void *data) { box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x); box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y); wlr_xdg_popup_unconstrain_from_box(popup, &box); + wl_list_remove(&listener->link); free(listener); } @@ -2341,6 +2463,23 @@ void createidleinhibitor(struct wl_listener *listener, void *data) { void createkeyboard(struct wlr_keyboard *keyboard) { + struct libinput_device *device = NULL; + + if (wlr_input_device_is_libinput(&keyboard->base) && + (device = wlr_libinput_get_device_handle(&keyboard->base))) { + + InputDevice *input_dev = calloc(1, sizeof(InputDevice)); + input_dev->wlr_device = &keyboard->base; + input_dev->libinput_device = device; + input_dev->device_data = keyboard; + + input_dev->destroy_listener.notify = destroyinputdevice; + wl_signal_add(&keyboard->base.events.destroy, + &input_dev->destroy_listener); + + wl_list_insert(&inputdevices, &input_dev->link); + } + /* Set the keymap to match the group keymap */ wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap); @@ -2370,14 +2509,14 @@ KeyboardGroup *createkeyboardgroup(void) { xkb_mod_index_t mod_index = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM); if (mod_index != XKB_MOD_INVALID) - locked_mods |= (unsigned int)1 << mod_index; + locked_mods |= (uint32_t)1 << mod_index; } if (capslock) { xkb_mod_index_t mod_index = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS); if (mod_index != XKB_MOD_INVALID) - locked_mods |= (unsigned int)1 << mod_index; + locked_mods |= (uint32_t)1 << mod_index; } if (locked_mods) @@ -2409,7 +2548,7 @@ KeyboardGroup *createkeyboardgroup(void) { void createlayersurface(struct wl_listener *listener, void *data) { struct wlr_layer_surface_v1 *layer_surface = data; - LayerSurface *l; + LayerSurface *l = NULL; struct wlr_surface *surface = layer_surface->surface; struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->pending.layer]]; @@ -2463,17 +2602,37 @@ void createlocksurface(struct wl_listener *listener, void *data) { client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat)); } -struct wlr_output_mode *get_output_mode(struct wlr_output *output, int width, - int height, float refresh) { - struct wlr_output_mode *mode; +struct wlr_output_mode *get_nearest_output_mode(struct wlr_output *output, + int32_t width, int32_t height, + float refresh) { + struct wlr_output_mode *mode, *nearest_mode = NULL; + float min_diff = 99999.0f; + wl_list_for_each(mode, &output->modes, link) { - if (mode->width == width && mode->height == height && - (int)(mode->refresh / 1000) == (int)refresh) { - return mode; + if (mode->width == width && mode->height == height) { + float mode_refresh = mode->refresh / 1000.0f; + float diff = fabsf(mode_refresh - refresh); + + if (diff < min_diff) { + min_diff = diff; + nearest_mode = mode; + } } } - return NULL; + return nearest_mode; +} + +void enable_adaptive_sync(Monitor *m, struct wlr_output_state *state) { + wlr_output_state_set_adaptive_sync_enabled(state, true); + if (!wlr_output_test_state(m->wlr_output, state)) { + wlr_output_state_set_adaptive_sync_enabled(state, false); + wlr_log(WLR_DEBUG, "failed to enable adaptive sync for output %s", + m->wlr_output->name); + } else { + wlr_log(WLR_INFO, "adaptive sync enabled for output %s", + m->wlr_output->name); + } } void createmon(struct wl_listener *listener, void *data) { @@ -2481,18 +2640,27 @@ void createmon(struct wl_listener *listener, void *data) { * monitor) becomes available. */ struct wlr_output *wlr_output = data; const ConfigMonitorRule *r; - unsigned int i; - int ji, jk; + uint32_t i; + int32_t ji, jk; struct wlr_output_state state; - Monitor *m; + Monitor *m = NULL; struct wlr_output_mode *internal_mode = NULL; bool custom_monitor_mode = false; if (!wlr_output_init_render(wlr_output, alloc, drw)) return; + if (wlr_output->non_desktop) { + if (drm_lease_manager) { + wlr_drm_lease_v1_manager_offer_output(drm_lease_manager, + wlr_output); + } + return; + } + m = wlr_output->data = ecalloc(1, sizeof(*m)); m->wlr_output = wlr_output; + m->wlr_output->data = m; wl_list_init(&m->dwl_ipc_outputs); @@ -2508,7 +2676,6 @@ void createmon(struct wl_listener *listener, void *data) { m->isoverview = 0; m->sel = NULL; m->is_in_hotarea = 0; - m->tagset[0] = m->tagset[1] = 1; float scale = 1; m->mfact = default_mfact; m->nmaster = default_nmaster; @@ -2538,11 +2705,16 @@ void createmon(struct wl_listener *listener, void *data) { rr = r->rr; if (r->width > 0 && r->height > 0 && r->refresh > 0) { - custom_monitor_mode = true; - internal_mode = get_output_mode(m->wlr_output, r->width, - r->height, r->refresh); + internal_mode = get_nearest_output_mode(m->wlr_output, r->width, + r->height, r->refresh); if (internal_mode) { + custom_monitor_mode = true; wlr_output_state_set_mode(&state, internal_mode); + } else if (wlr_output_is_headless(m->wlr_output)) { + custom_monitor_mode = true; + wlr_output_state_set_custom_mode( + &state, r->width, r->height, + (int32_t)roundf(r->refresh * 1000)); } } wlr_output_state_set_scale(&state, r->scale); @@ -2559,6 +2731,10 @@ void createmon(struct wl_listener *listener, void *data) { wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output)); + if (adaptive_sync) { + enable_adaptive_sync(m, &state); + } + /* Set up event listeners */ LISTEN(&wlr_output->events.frame, &m->frame, rendermon); LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); @@ -2571,26 +2747,25 @@ void createmon(struct wl_listener *listener, void *data) { wl_list_insert(&mons, &m->link); m->pertag = calloc(1, sizeof(Pertag)); - m->pertag->curtag = m->pertag->prevtag = 1; + if (chvt_backup_tag && + regex_match(chvt_backup_selmon, m->wlr_output->name)) { + m->tagset[0] = m->tagset[1] = (1 << (chvt_backup_tag - 1)) & TAGMASK; + m->pertag->curtag = m->pertag->prevtag = chvt_backup_tag; + chvt_backup_tag = 0; + memset(chvt_backup_selmon, 0, sizeof(chvt_backup_selmon)); + } else { + m->tagset[0] = m->tagset[1] = 1; + m->pertag->curtag = m->pertag->prevtag = 1; + } for (i = 0; i <= LENGTH(tags); i++) { m->pertag->nmasters[i] = m->nmaster; m->pertag->mfacts[i] = m->mfact; - m->pertag->smfacts[i] = default_smfact; m->pertag->ltidxs[i] = m->lt; } // apply tag rule - for (i = 1; i <= config.tag_rules_count; i++) { - for (jk = 0; jk < LENGTH(layouts); jk++) { - if (config.tag_rules_count > 0 && - config.tag_rules[i - 1].layout_name && - strcmp(layouts[jk].name, config.tag_rules[i - 1].layout_name) == - 0) { - m->pertag->ltidxs[config.tag_rules[i - 1].id] = &layouts[jk]; - } - } - } + parse_tagrule(m); /* The xdg-protocol specifies: * @@ -2621,7 +2796,7 @@ void createmon(struct wl_listener *listener, void *data) { // wlr_scene_node_set_enabled(&m->blur->node, 1); } m->ext_group = wlr_ext_workspace_group_handle_v1_create( - ext_manager, WLR_EXT_WORKSPACE_HANDLE_V1_CAP_ACTIVATE); + ext_manager, EXT_WORKSPACE_ENABLE_CAPS); wlr_ext_workspace_group_handle_v1_output_enter(m->ext_group, m->wlr_output); for (i = 1; i <= LENGTH(tags); i++) { @@ -2657,61 +2832,153 @@ createnotify(struct wl_listener *listener, void *data) { LISTEN(&toplevel->events.set_title, &c->set_title, updatetitle); } +void destroyinputdevice(struct wl_listener *listener, void *data) { + InputDevice *input_dev = + wl_container_of(listener, input_dev, destroy_listener); + + // 清理设备特定数据 + if (input_dev->device_data) { + // 根据设备类型进行特定清理 + switch (input_dev->wlr_device->type) { + case WLR_INPUT_DEVICE_SWITCH: { + Switch *sw = (Switch *)input_dev->device_data; + // 移除 toggle 监听器 + wl_list_remove(&sw->toggle.link); + // 释放 Switch 内存 + free(sw); + break; + } + // 可以添加其他设备类型的清理代码 + default: + break; + } + input_dev->device_data = NULL; + } + + // 从设备列表中移除 + wl_list_remove(&input_dev->link); + // 移除 destroy 监听器 + wl_list_remove(&input_dev->destroy_listener.link); + // 释放内存 + free(input_dev); +} + +void configure_pointer(struct libinput_device *device) { + if (libinput_device_config_tap_get_finger_count(device)) { + libinput_device_config_tap_set_enabled(device, tap_to_click); + libinput_device_config_tap_set_drag_enabled(device, tap_and_drag); + libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock); + libinput_device_config_tap_set_button_map(device, button_map); + libinput_device_config_scroll_set_natural_scroll_enabled( + device, trackpad_natural_scrolling); + } else { + libinput_device_config_scroll_set_natural_scroll_enabled( + device, mouse_natural_scrolling); + } + + if (libinput_device_config_dwt_is_available(device)) + libinput_device_config_dwt_set_enabled(device, disable_while_typing); + + if (libinput_device_config_left_handed_is_available(device)) + libinput_device_config_left_handed_set(device, left_handed); + + if (libinput_device_config_middle_emulation_is_available(device)) + libinput_device_config_middle_emulation_set_enabled( + device, middle_button_emulation); + + if (libinput_device_config_scroll_get_methods(device) != + LIBINPUT_CONFIG_SCROLL_NO_SCROLL) + libinput_device_config_scroll_set_method(device, scroll_method); + if (libinput_device_config_scroll_get_methods(device) == + LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) + libinput_device_config_scroll_set_button(device, scroll_button); + + if (libinput_device_config_click_get_methods(device) != + LIBINPUT_CONFIG_CLICK_METHOD_NONE) + libinput_device_config_click_set_method(device, click_method); + + if (libinput_device_config_send_events_get_modes(device)) + libinput_device_config_send_events_set_mode(device, send_events_mode); + + if (libinput_device_config_accel_is_available(device)) { + libinput_device_config_accel_set_profile(device, accel_profile); + libinput_device_config_accel_set_speed(device, accel_speed); + } +} + void createpointer(struct wlr_pointer *pointer) { - struct libinput_device *device; + + struct libinput_device *device = NULL; + if (wlr_input_device_is_libinput(&pointer->base) && (device = wlr_libinput_get_device_handle(&pointer->base))) { - if (libinput_device_config_tap_get_finger_count(device) && - disable_trackpad) { + configure_pointer(device); + + InputDevice *input_dev = calloc(1, sizeof(InputDevice)); + input_dev->wlr_device = &pointer->base; + input_dev->libinput_device = device; + + input_dev->destroy_listener.notify = destroyinputdevice; + wl_signal_add(&pointer->base.events.destroy, + &input_dev->destroy_listener); + + wl_list_insert(&inputdevices, &input_dev->link); + } + wlr_cursor_attach_input_device(cursor, &pointer->base); +} + +void switch_toggle(struct wl_listener *listener, void *data) { + // 获取包含监听器的结构体 + Switch *sw = wl_container_of(listener, sw, toggle); + + // 处理切换事件 + struct wlr_switch_toggle_event *event = data; + SwitchBinding *s; + int32_t ji; + + for (ji = 0; ji < config.switch_bindings_count; ji++) { + if (config.switch_bindings_count < 1) + break; + s = &config.switch_bindings[ji]; + if (event->switch_state == s->fold && s->func) { + s->func(&s->arg); return; } - - if (libinput_device_config_tap_get_finger_count(device)) { - libinput_device_config_tap_set_enabled(device, tap_to_click); - libinput_device_config_tap_set_drag_enabled(device, tap_and_drag); - libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock); - libinput_device_config_tap_set_button_map(device, button_map); - libinput_device_config_scroll_set_natural_scroll_enabled( - device, trackpad_natural_scrolling); - } else { - libinput_device_config_scroll_set_natural_scroll_enabled( - device, mouse_natural_scrolling); - } - - if (libinput_device_config_dwt_is_available(device)) - libinput_device_config_dwt_set_enabled(device, - disable_while_typing); - - if (libinput_device_config_left_handed_is_available(device)) - libinput_device_config_left_handed_set(device, left_handed); - - if (libinput_device_config_middle_emulation_is_available(device)) - libinput_device_config_middle_emulation_set_enabled( - device, middle_button_emulation); - - if (libinput_device_config_scroll_get_methods(device) != - LIBINPUT_CONFIG_SCROLL_NO_SCROLL) - libinput_device_config_scroll_set_method(device, scroll_method); - if (libinput_device_config_scroll_get_methods(device) == - LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) - libinput_device_config_scroll_set_button(device, scroll_button); - - if (libinput_device_config_click_get_methods(device) != - LIBINPUT_CONFIG_CLICK_METHOD_NONE) - libinput_device_config_click_set_method(device, click_method); - - if (libinput_device_config_send_events_get_modes(device)) - libinput_device_config_send_events_set_mode(device, - send_events_mode); - - if (libinput_device_config_accel_is_available(device)) { - libinput_device_config_accel_set_profile(device, accel_profile); - libinput_device_config_accel_set_speed(device, accel_speed); - } } +} - wlr_cursor_attach_input_device(cursor, &pointer->base); +void createswitch(struct wlr_switch *switch_device) { + + struct libinput_device *device = NULL; + + if (wlr_input_device_is_libinput(&switch_device->base) && + (device = wlr_libinput_get_device_handle(&switch_device->base))) { + + InputDevice *input_dev = calloc(1, sizeof(InputDevice)); + input_dev->wlr_device = &switch_device->base; + input_dev->libinput_device = device; + input_dev->device_data = NULL; // 初始化为 NULL + + input_dev->destroy_listener.notify = destroyinputdevice; + wl_signal_add(&switch_device->base.events.destroy, + &input_dev->destroy_listener); + + // 创建 Switch 特定数据 + Switch *sw = calloc(1, sizeof(Switch)); + sw->wlr_switch = switch_device; + sw->toggle.notify = switch_toggle; + sw->input_dev = input_dev; + + // 将 Switch 指针保存到 input_device 中 + input_dev->device_data = sw; + + // 添加 toggle 监听器 + wl_signal_add(&switch_device->events.toggle, &sw->toggle); + + // 添加到全局列表 + wl_list_insert(&inputdevices, &input_dev->link); + } } void createpointerconstraint(struct wl_listener *listener, void *data) { @@ -2743,8 +3010,9 @@ void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) { void cursorframe(struct wl_listener *listener, void *data) { /* This event is forwarded by the cursor when a pointer emits an frame * event. Frame events are sent after regular pointer events to group - * multiple events together. For instance, two axis events may happen at the - * same time, in which case a frame event won't be sent in between. */ + * multiple events together. For instance, two axis events may happen at + * the same time, in which case a frame event won't be sent in between. + */ /* Notify the client with pointer focus of the frame event. */ wlr_seat_pointer_notify_frame(seat); } @@ -2772,7 +3040,8 @@ void destroydragicon(struct wl_listener *listener, void *data) { void destroyidleinhibitor(struct wl_listener *listener, void *data) { /* `data` is the wlr_surface of the idle inhibitor being destroyed, - * at this point the idle inhibitor is still in the list of the manager */ + * at this point the idle inhibitor is still in the list of the manager + */ checkidleinhibitor(wlr_surface_get_root_surface(data)); wl_list_remove(&listener->link); free(listener); @@ -2791,12 +3060,14 @@ void destroylayersurfacenotify(struct wl_listener *listener, void *data) { free(l); } -void destroylock(SessionLock *lock, int unlock) { +void destroylock(SessionLock *lock, int32_t unlock) { wlr_seat_keyboard_notify_clear_focus(seat); if ((locked = !unlock)) goto destroy; - wlr_scene_node_set_enabled(&locked_bg->node, false); + if (locked_bg->node.enabled) { + wlr_scene_node_set_enabled(&locked_bg->node, false); + } focusclient(focustop(selmon), 0); motionnotify(0, NULL, 0, 0, 0, 0); @@ -2893,7 +3164,11 @@ void destroykeyboardgroup(struct wl_listener *listener, void *data) { free(group); } -void focusclient(Client *c, int lift) { +void focusclient(Client *c, int32_t lift) { + + Client *last_focus_client = NULL; + Monitor *um = NULL; + struct wlr_surface *old_keyboard_focus_surface = seat->keyboard_state.focused_surface; @@ -2906,12 +3181,11 @@ void focusclient(Client *c, int lift) { if (c && !client_surface(c)->mapped) return; - if (c && c->animation.tagouting && !c->animation.tagouting) + if (c && client_should_ignore_focus(c) && client_is_x11_popup(c)) return; - if (c && client_should_ignore_focus(c)) { + if (c && c->nofocus) return; - } /* Raise client in stacking order if requested */ if (c && lift) @@ -2929,18 +3203,27 @@ void focusclient(Client *c, int lift) { if (c && !c->iskilling && !client_is_unmanaged(c) && c->mon) { + last_focus_client = selmon->sel; selmon = c->mon; selmon->prevsel = selmon->sel; selmon->sel = c; + c->isfocusing = true; + + if (last_focus_client && !last_focus_client->iskilling && + last_focus_client != c) { + last_focus_client->isfocusing = false; + client_set_unfocused_opacity_animation(last_focus_client); + } + + client_set_focused_opacity_animation(c); // decide whether need to re-arrange - if (c && selmon->prevsel && !selmon->prevsel->isfloating && + + if (c && selmon->prevsel && (selmon->prevsel->tags & selmon->tagset[selmon->seltags]) && (c->tags & selmon->tagset[selmon->seltags]) && !c->isfloating && - !c->isfullscreen && is_scroller_layout(selmon)) { - arrange(selmon, false); - } else if (selmon->prevsel) { - selmon->prevsel = NULL; + is_scroller_layout(selmon)) { + arrange(selmon, false, false); } // change focus link position @@ -2949,7 +3232,15 @@ void focusclient(Client *c, int lift) { // change border color c->isurgent = 0; - setborder_color(c); + } + + // update other monitor focus disappear + wl_list_for_each(um, &mons, link) { + if (um->wlr_output->enabled && um != selmon && um->sel && + !um->sel->iskilling && um->sel->isfocusing) { + um->sel->isfocusing = false; + client_set_unfocused_opacity_animation(um->sel); + } } if (c && !c->iskilling && c->foreign_toplevel) @@ -2958,13 +3249,13 @@ void focusclient(Client *c, int lift) { /* Deactivate old client if focus is changing */ if (old_keyboard_focus_surface && (!c || client_surface(c) != old_keyboard_focus_surface)) { - /* If an exclusive_focus layer is focused, don't focus or activate the - * client, but only update its position in fstack to render its border - * with focuscolor and focus it after the exclusive_focus layer is - * closed. */ + /* If an exclusive_focus layer is focused, don't focus or activate + * the client, but only update its position in fstack to render its + * border with focuscolor and focus it after the exclusive_focus + * layer is closed. */ Client *w = NULL; LayerSurface *l = NULL; - int type = + int32_t type = toplevel_from_wlr_surface(old_keyboard_focus_surface, &w, &l); if (type == LayerShell && l->scene->node.enabled && l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && @@ -2972,23 +3263,22 @@ void focusclient(Client *c, int lift) { return; } else if (w && w == exclusive_focus && client_wants_focus(w)) { return; - /* Don't deactivate old_keyboard_focus_surface client if the new one - * wants focus, as this causes issues with winecfg and probably - * other clients */ + /* Don't deactivate old_keyboard_focus_surface client if the new + * one wants focus, as this causes issues with winecfg and + * probably other clients */ } else if (w && !client_is_unmanaged(w) && (!c || !client_wants_focus(c))) { - setborder_color(w); - client_activate_surface(old_keyboard_focus_surface, 0); } } printstatus(); if (!c) { - /* With no client, all we have left is to clear focus */ - if (selmon && selmon->sel) - selmon->sel = - NULL; // 这个很关键,因为很多地方用到当前窗口做计算,不重置成NULL就会到处有野指针 + + if (selmon && selmon->sel && + (!VISIBLEON(selmon->sel, selmon) || selmon->sel->iskilling || + !client_surface(selmon->sel)->mapped)) + selmon->sel = NULL; // clear text input focus state dwl_im_relay_set_focus(dwl_input_method_relay, NULL); @@ -2999,11 +3289,14 @@ void focusclient(Client *c, int lift) { /* Change cursor surface */ motionnotify(0, NULL, 0, 0, 0, 0); + // set text input focus + // must before client_notify_enter, + // otherwise the position of text_input will be wrong. + dwl_im_relay_set_focus(dwl_input_method_relay, client_surface(c)); + /* Have a client, so focus its top-level wlr_surface */ client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); - // set text input focus - dwl_im_relay_set_focus(dwl_input_method_relay, client_surface(c)); /* Activate the new client */ client_activate_surface(client_surface(c), 1); } @@ -3019,16 +3312,38 @@ fullscreennotify(struct wl_listener *listener, void *data) { } void requestmonstate(struct wl_listener *listener, void *data) { - struct wlr_output_event_request_state *event = data; - wlr_output_commit_state(event->output, event->state); - updatemons(NULL, NULL); + /* This ensures nested backends can be resized */ + Monitor *m = wl_container_of(listener, m, request_state); + const struct wlr_output_event_request_state *event = data; + + if (event->state->committed == WLR_OUTPUT_STATE_MODE) { + switch (event->state->mode_type) { + case WLR_OUTPUT_STATE_MODE_FIXED: + wlr_output_state_set_mode(&m->pending, event->state->mode); + break; + case WLR_OUTPUT_STATE_MODE_CUSTOM: + wlr_output_state_set_custom_mode(&m->pending, + event->state->custom_mode.width, + event->state->custom_mode.height, + event->state->custom_mode.refresh); + break; + } + updatemons(NULL, NULL); + wlr_output_schedule_frame(m->wlr_output); + return; + } + + if (!wlr_output_commit_state(m->wlr_output, event->state)) { + wlr_log(WLR_ERROR, + "Backend requested a new state that could not be applied"); + } } void inputdevice(struct wl_listener *listener, void *data) { /* This event is raised by the backend when a new input device becomes * available. */ struct wlr_input_device *device = data; - unsigned int caps; + uint32_t caps; switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD: @@ -3043,6 +3358,9 @@ void inputdevice(struct wl_listener *listener, void *data) { case WLR_INPUT_DEVICE_POINTER: createpointer(wlr_pointer_from_input_device(device)); break; + case WLR_INPUT_DEVICE_SWITCH: + createswitch(wlr_switch_from_input_device(device)); + break; default: /* TODO handle other input device types */ break; @@ -3050,7 +3368,8 @@ void inputdevice(struct wl_listener *listener, void *data) { /* We need to let the wlr_seat know what our capabilities are, which is * communiciated to the client. In dwl we always have a cursor, even if - * there are no pointer devices, so we always include that capability. */ + * there are no pointer devices, so we always include that capability. + */ /* TODO do we actually require a cursor? */ caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&kb_group->wlr_group->devices)) @@ -3058,9 +3377,9 @@ void inputdevice(struct wl_listener *listener, void *data) { wlr_seat_set_capabilities(seat, caps); } -int keyrepeat(void *data) { +int32_t keyrepeat(void *data) { KeyboardGroup *group = data; - int i; + int32_t i; if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0) return 0; @@ -3069,34 +3388,86 @@ int keyrepeat(void *data) { 1000 / group->wlr_group->keyboard.repeat_info.rate); for (i = 0; i < group->nsyms; i++) - keybinding(group->mods, group->keysyms[i], group->keycode); + keybinding(WL_KEYBOARD_KEY_STATE_PRESSED, false, group->mods, + group->keysyms[i], group->keycode); return 0; } -int // 17 -keybinding(unsigned int mods, xkb_keysym_t sym, unsigned int keycode) { +bool is_keyboard_shortcut_inhibitor(struct wlr_surface *surface) { + KeyboardShortcutsInhibitor *kbsinhibitor; + + wl_list_for_each(kbsinhibitor, &keyboard_shortcut_inhibitors, link) { + if (kbsinhibitor->inhibitor->surface == surface) { + return true; + } + } + return false; +} + +int32_t // 17 +keybinding(uint32_t state, bool locked, uint32_t mods, xkb_keysym_t sym, + uint32_t keycode) { /* * Here we handle compositor keybindings. This is when the compositor is - * processing keys, rather than passing them on to the client for its own - * processing. + * processing keys, rather than passing them on to the client for its + * own processing. */ - int handled = 0; + int32_t handled = 0; const KeyBinding *k; - int ji; + int32_t ji; + int32_t isbreak = 0; + + // not allow modifier keys to be used as a keybinding + if (keycode == 50 || keycode == 37 || keycode == 133 || keycode == 64 || + keycode == 62 || keycode == 108 || keycode == 105 || keycode == 134) + return false; + + if (is_keyboard_shortcut_inhibitor(seat->keyboard_state.focused_surface)) { + return false; + } + for (ji = 0; ji < config.key_bindings_count; ji++) { if (config.key_bindings_count < 1) break; + + if (locked && config.key_bindings[ji].islockapply == false) + continue; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED && + config.key_bindings[ji].isreleaseapply == false) + continue; + + if (state == WL_KEYBOARD_KEY_STATE_PRESSED && + config.key_bindings[ji].isreleaseapply == true) + continue; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED && + state != WL_KEYBOARD_KEY_STATE_RELEASED) + continue; + k = &config.key_bindings[ji]; - if (CLEANMASK(mods) == CLEANMASK(k->mod) && + if ((k->iscommonmode || (k->isdefaultmode && keymode.isdefault) || + (strcmp(keymode.mode, k->mode) == 0)) && + CLEANMASK(mods) == CLEANMASK(k->mod) && ((k->keysymcode.type == KEY_TYPE_SYM && - normalize_keysym(sym) == - normalize_keysym(k->keysymcode.keysym)) || + xkb_keysym_to_lower(sym) == + xkb_keysym_to_lower(k->keysymcode.keysym)) || (k->keysymcode.type == KEY_TYPE_CODE && - keycode == k->keysymcode.keycode)) && + (keycode == k->keysymcode.keycode.keycode1 || + keycode == k->keysymcode.keycode.keycode2 || + keycode == k->keysymcode.keycode.keycode3))) && k->func) { - k->func(&k->arg); - handled = 1; + + if (!k->ispassapply) + handled = 1; + else + handled = 0; + + isbreak = k->func(&k->arg); + + if (isbreak) + break; } } return handled; @@ -3104,14 +3475,14 @@ keybinding(unsigned int mods, xkb_keysym_t sym, unsigned int keycode) { bool keypressglobal(struct wlr_surface *last_surface, struct wlr_keyboard *keyboard, - struct wlr_keyboard_key_event *event, unsigned int mods, - xkb_keysym_t keysym, unsigned int keycode) { + struct wlr_keyboard_key_event *event, uint32_t mods, + xkb_keysym_t keysym, uint32_t keycode) { Client *c = NULL, *lastc = focustop(selmon); - unsigned int keycodes[32] = {0}; - int reset = false; + uint32_t keycodes[32] = {0}; + int32_t reset = false; const char *appid = NULL; const char *title = NULL; - int ji; + int32_t ji; const ConfigWinRule *r; for (ji = 0; ji < config.window_rules_count; ji++) { @@ -3121,14 +3492,18 @@ bool keypressglobal(struct wlr_surface *last_surface, if (!r->globalkeybinding.mod || (!r->globalkeybinding.keysymcode.keysym && - !r->globalkeybinding.keysymcode.keycode)) + !r->globalkeybinding.keysymcode.keycode.keycode1 && + !r->globalkeybinding.keysymcode.keycode.keycode2 && + !r->globalkeybinding.keysymcode.keycode.keycode3)) continue; /* match key only (case insensitive) ignoring mods */ if (((r->globalkeybinding.keysymcode.type == KEY_TYPE_SYM && r->globalkeybinding.keysymcode.keysym == keysym) || (r->globalkeybinding.keysymcode.type == KEY_TYPE_CODE && - r->globalkeybinding.keysymcode.keycode == keycode)) && + (r->globalkeybinding.keysymcode.keycode.keycode1 == keycode || + r->globalkeybinding.keysymcode.keycode.keycode2 == keycode || + r->globalkeybinding.keysymcode.keycode.keycode3 == keycode))) && r->globalkeybinding.mod == mods) { wl_list_for_each(c, &clients, link) { if (c && c != lastc) { @@ -3161,7 +3536,7 @@ done: } void keypress(struct wl_listener *listener, void *data) { - int i; + int32_t i; /* This event is raised when a key is pressed or released. */ KeyboardGroup *group = wl_container_of(listener, group, key); struct wlr_keyboard_key_event *event = data; @@ -3170,7 +3545,7 @@ void keypress(struct wl_listener *listener, void *data) { struct wlr_xdg_surface *xdg_surface = last_surface ? wlr_xdg_surface_try_from_wlr_surface(last_surface) : NULL; - int pass = 0; + int32_t pass = 0; bool hit_global = false; #ifdef XWAYLAND struct wlr_xwayland_surface *xsurface = @@ -3179,33 +3554,36 @@ void keypress(struct wl_listener *listener, void *data) { #endif /* Translate libinput keycode -> xkbcommon */ - unsigned int keycode = event->keycode + 8; + uint32_t keycode = event->keycode + 8; /* Get a list of keysyms based on the keymap for this keyboard */ const xkb_keysym_t *syms; - int nsyms = xkb_state_key_get_syms(group->wlr_group->keyboard.xkb_state, - keycode, &syms); + int32_t nsyms = xkb_state_key_get_syms(group->wlr_group->keyboard.xkb_state, + keycode, &syms); - int handled = 0; - unsigned int mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard); + int32_t handled = 0; + uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); // ov tab mode detect moe key release - if (ov_tab_mode && !locked && + if (ov_tab_mode && !locked && group == kb_group && event->state == WL_KEYBOARD_KEY_STATE_RELEASED && (keycode == 133 || keycode == 37 || keycode == 64 || keycode == 50 || keycode == 134 || keycode == 105 || keycode == 108 || keycode == 62) && selmon && selmon->sel) { if (selmon->isoverview && selmon->sel) { - toggleoverview(&(Arg){.i = -1}); + toggleoverview(&(Arg){.i = 1}); } } /* On _press_ if there is no active screen locker, * attempt to process a compositor keybinding. */ - if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { - for (i = 0; i < nsyms; i++) - handled = keybinding(mods, syms[i], keycode) || handled; + for (i = 0; i < nsyms; i++) + handled = + keybinding(event->state, locked, mods, syms[i], keycode) || handled; + + if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { + tag_combo = false; } if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { @@ -3225,8 +3603,8 @@ void keypress(struct wl_listener *listener, void *data) { return; /* don't pass when popup is focused - * this is better than having popups (like fuzzel or wmenu) closing while - * typing in a passed keybind */ + * this is better than having popups (like fuzzel or wmenu) closing + * while typing in a passed keybind */ pass = (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) || !last_surface #ifdef XWAYLAND @@ -3271,7 +3649,9 @@ void pending_kill_client(Client *c) { void locksession(struct wl_listener *listener, void *data) { struct wlr_session_lock_v1 *session_lock = data; SessionLock *lock; - wlr_scene_node_set_enabled(&locked_bg->node, true); + if (!allow_lock_transparent) { + wlr_scene_node_set_enabled(&locked_bg->node, true); + } if (cur_lock) { wlr_session_lock_v1_destroy(session_lock); return; @@ -3291,8 +3671,8 @@ void locksession(struct wl_listener *listener, void *data) { wlr_session_lock_v1_send_locked(session_lock); } -static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int sx, - int sy, void *user_data) { +static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, + int32_t sy, void *user_data) { Client *c = user_data; struct wlr_scene_surface *scene_surface = @@ -3308,7 +3688,7 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int sx, if (blur && c && !c->noblur) { wlr_scene_buffer_set_backdrop_blur(buffer, true); - wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, true); + wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, false); if (blur_optimized) { wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true); } else { @@ -3320,12 +3700,14 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int sx, } void init_client_properties(Client *c) { - c->ismaxmizescreen = 0; + c->isfocusing = false; + c->ismaximizescreen = 0; c->isfullscreen = 0; c->need_float_size_reduce = 0; c->iskilling = 0; + c->istagswitching = 0; c->isglobal = 0; - c->isminied = 0; + c->isminimized = 0; c->isoverlay = 0; c->isunglobal = 0; c->is_in_scratchpad = 0; @@ -3339,24 +3721,41 @@ void init_client_properties(Client *c) { c->scroller_proportion = scroller_default_proportion; c->is_pending_open_animation = true; c->drag_to_tile = false; + c->scratchpad_switching_mon = false; c->fake_no_border = false; c->focused_opacity = focused_opacity; c->unfocused_opacity = unfocused_opacity; + c->nofocus = 0; c->nofadein = 0; c->nofadeout = 0; c->no_force_center = 0; - c->scratchpad_width = 0; - c->scratchpad_height = 0; c->isnoborder = 0; c->isnosizehint = 0; - c->ignore_maximize = 0; + c->isnoradius = 0; + c->isnoshadow = 0; + c->ignore_maximize = 1; c->ignore_minimize = 1; + c->iscustomsize = 0; + c->iscustompos = 0; + c->master_mfact_per = 0.0f; + c->master_inner_per = 0.0f; + c->stack_inner_per = 0.0f; + c->isterm = 0; + c->allow_csd = 0; + c->force_maximize = 0; + c->force_tearing = 0; + c->allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; + c->scroller_proportion_single = 0.0f; + c->float_geom.width = 0; + c->float_geom.height = 0; + c->float_geom.x = 0; + c->float_geom.y = 0; } void // old fix to 0.5 mapnotify(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ - Client *p = NULL; + Client *at_client = NULL; Client *c = wl_container_of(listener, c, map); /* Create scene tree for this client and its border */ c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]); @@ -3369,10 +3768,11 @@ mapnotify(struct wl_listener *listener, void *data) { client_get_geometry(c, &c->geom); - init_client_properties(c); + if (client_is_x11(c)) + init_client_properties(c); // set special window properties - if (client_is_unmanaged(c) || client_should_ignore_focus(c)) { + if (client_is_unmanaged(c) || client_is_x11_popup(c)) { c->bw = 0; c->isnoborder = 1; } else { @@ -3387,7 +3787,8 @@ mapnotify(struct wl_listener *listener, void *data) { c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; - /* Handle unmanaged clients first so we can return prior create borders */ + /* Handle unmanaged clients first so we can return prior create borders + */ if (client_is_unmanaged(c)) { /* Unmanaged clients always are floating */ wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); @@ -3423,26 +3824,29 @@ mapnotify(struct wl_listener *listener, void *data) { if (new_is_master && selmon && !is_scroller_layout(selmon)) // tile at the top wl_list_insert(&clients, &c->link); // 新窗口是master,头部入栈 - else if (selmon && is_scroller_layout(selmon) && center_select(selmon)) { - Client *at_client = center_select(selmon); - at_client->link.next->prev = &c->link; - c->link.prev = &at_client->link; - c->link.next = at_client->link.next; - at_client->link.next = &c->link; + else if (selmon && is_scroller_layout(selmon) && + selmon->visible_scroll_tiling_clients > 0) { + + if (selmon->sel && ISSCROLLTILED(selmon->sel) && + VISIBLEON(selmon->sel, selmon)) { + at_client = selmon->sel; + } else { + at_client = center_tiled_select(selmon); + } + + if (at_client) { + at_client->link.next->prev = &c->link; + c->link.prev = &at_client->link; + c->link.next = at_client->link.next; + at_client->link.next = &c->link; + } else { + wl_list_insert(clients.prev, &c->link); // 尾部入栈 + } } else wl_list_insert(clients.prev, &c->link); // 尾部入栈 wl_list_insert(&fstack, &c->flink); - /* Set initial monitor, tags, floating status, and focus: - * we always consider floating, clients that have parent and thus - * we set the same tags and monitor than its parent, if not - * try to apply rules for them */ - if ((p = client_get_parent(c))) { - c->isfloating = 1; - setmon(c, p->mon, p->tags, true); - } else { - applyrules(c); - } + applyrules(c); client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); @@ -3460,33 +3864,46 @@ mapnotify(struct wl_listener *listener, void *data) { printstatus(); } -void // 0.5 custom -maximizenotify(struct wl_listener *listener, void *data) { - /* This event is raised when a client would like to maximize itself, - * typically because the user clicked on the maximize button on - * client-side decorations. dwl doesn't support maximization, but - * to conform to xdg-shell protocol we still must send a configure. - * Since xdg-shell protocol v5 we should ignore request of unsupported - * capabilities, just schedule a empty configure when the client uses <5 - * protocol version - * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ - // Client *c = wl_container_of(listener, c, maximize); - // if (wl_resource_get_version(c->surface.xdg->toplevel->resource) - // < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) - // wlr_xdg_surface_schedule_configure(c->surface.xdg); - // togglemaxmizescreen(&(Arg){0}); +void maximizenotify(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, maximize); if (!c || !c->mon || c->iskilling || c->ignore_maximize) return; - if (c->ismaxmizescreen || c->isfullscreen) - setmaxmizescreen(c, 0); - else - setmaxmizescreen(c, 1); + if (!client_is_x11(c) && !c->surface.xdg->initialized) { + return; + } + + if (client_request_maximize(c, data)) { + setmaximizescreen(c, 1); + } else { + setmaximizescreen(c, 0); + } } -void set_minized(Client *c) { +void unminimize(Client *c) { + if (c && c->is_in_scratchpad && c->is_scratchpad_show) { + c->isminimized = 0; + c->is_scratchpad_show = 0; + c->is_in_scratchpad = 0; + c->isnamedscratchpad = 0; + setborder_color(c); + return; + } + + if (c && c->isminimized) { + show_hide_client(c); + c->is_scratchpad_show = 0; + c->is_in_scratchpad = 0; + c->isnamedscratchpad = 0; + setborder_color(c); + arrange(c->mon, false, false); + return; + } +} + +void set_minimized(Client *c) { if (!c || !c->mon) return; @@ -3495,58 +3912,50 @@ void set_minized(Client *c) { c->oldtags = c->mon->tagset[c->mon->seltags]; c->mini_restore_tag = c->tags; c->tags = 0; - c->isminied = 1; + c->isminimized = 1; c->is_in_scratchpad = 1; c->is_scratchpad_show = 0; focusclient(focustop(selmon), 1); - arrange(c->mon, false); + arrange(c->mon, false, false); wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, false); wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, true); wl_list_remove(&c->link); // 从原来位置移除 wl_list_insert(clients.prev, &c->link); // 插入尾部 } -void // 0.5 custom -minimizenotify(struct wl_listener *listener, void *data) { - /* This event is raised when a client would like to maximize itself, - * typically because the user clicked on the maximize button on - * client-side decorations. dwl doesn't support maximization, but - * to conform to xdg-shell protocol we still must send a configure. - * Since xdg-shell protocol v5 we should ignore request of unsupported - * capabilities, just schedule a empty configure when the client uses <5 - * protocol version - * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ - // Client *c = wl_container_of(listener, c, maximize); - // if (wl_resource_get_version(c->surface.xdg->toplevel->resource) - // < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) - // wlr_xdg_surface_schedule_configure(c->surface.xdg); - // togglemaxmizescreen(&(Arg){0}); +void minimizenotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, minimize); - if (!c || !c->mon || c->iskilling || c->isminied) + if (!c || !c->mon || c->iskilling || c->isminimized) return; - if (client_request_minimize(c, data)) { - if (!c->ignore_minimize) - set_minized(c); + if (client_request_minimize(c, data) && !c->ignore_minimize) { + if (!c->isminimized) + set_minimized(c); client_set_minimized(c, true); } else { + if (c->isminimized) + unminimize(c); client_set_minimized(c, false); } } void motionabsolute(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits an _absolute_ - * motion event, from 0..1 on each axis. This happens, for example, when - * wlroots is running under a Wayland window rather than KMS+DRM, and you - * move the mouse over the window. You could enter the window from any edge, - * so we have to warp the mouse there. There is also some hardware which - * emits these events. */ + /* This event is forwarded by the cursor when a pointer emits an + * _absolute_ motion event, from 0..1 on each axis. This happens, for + * example, when wlroots is running under a Wayland window rather than + * KMS+DRM, and you move the mouse over the window. You could enter the + * window from any edge, so we have to warp the mouse there. There is + * also some hardware which emits these events. */ struct wlr_pointer_motion_absolute_event *event = data; double lx, ly, dx, dy; - if (!event->time_msec) /* this is 0 with virtual pointers */ + if (check_trackpad_disabled(event->pointer)) { + return; + } + + if (!event->time_msec) /* this is 0 with virtual pointer */ wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y); @@ -3557,7 +3966,7 @@ void motionabsolute(struct wl_listener *listener, void *data) { motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); } -void motionnotify(unsigned int time, struct wlr_input_device *device, double dx, +void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel) { double sx = 0, sy = 0, sx_confined, sy_confined; Client *c = NULL, *w = NULL; @@ -3617,58 +4026,58 @@ void motionnotify(unsigned int time, struct wlr_input_device *device, double dx, } /* Update drag icon's position */ - wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), - (int)round(cursor->y)); + wlr_scene_node_set_position(&drag_icon->node, (int32_t)round(cursor->x), + (int32_t)round(cursor->y)); /* If we are currently grabbing the mouse, handle and return */ if (cursor_mode == CurMove) { /* Move the grabbed client to the new position. */ - grabc->oldgeom = (struct wlr_box){.x = (int)round(cursor->x) - grabcx, - .y = (int)round(cursor->y) - grabcy, - .width = grabc->geom.width, - .height = grabc->geom.height}; - resize(grabc, grabc->oldgeom, 1); + grabc->iscustomsize = 1; + grabc->float_geom = + (struct wlr_box){.x = (int32_t)round(cursor->x) - grabcx, + .y = (int32_t)round(cursor->y) - grabcy, + .width = grabc->geom.width, + .height = grabc->geom.height}; + resize(grabc, grabc->float_geom, 1); return; } else if (cursor_mode == CurResize) { - grabc->oldgeom = - (struct wlr_box){.x = grabc->geom.x, - .y = grabc->geom.y, - .width = (int)round(cursor->x) - grabc->geom.x, - .height = (int)round(cursor->y) - grabc->geom.y}; - resize(grabc, grabc->oldgeom, 1); - return; + if (grabc->isfloating) { + grabc->iscustomsize = 1; + grabc->float_geom = (struct wlr_box){ + .x = grabc->geom.x, + .y = grabc->geom.y, + .width = (int32_t)round(cursor->x) - grabc->geom.x, + .height = (int32_t)round(cursor->y) - grabc->geom.y}; + if (last_apply_drap_time == 0 || + time - last_apply_drap_time > drag_refresh_interval) { + resize(grabc, grabc->float_geom, 1); + last_apply_drap_time = time; + } + return; + } else { + resize_tile_client(grabc, true, 0, 0, time); + } } - /* If there's no client surface under the cursor, set the cursor image to a - * default. This is what makes the cursor image appear when you move it - * off of a client or over its border. */ + /* If there's no client surface under the cursor, set the cursor image + * to a default. This is what makes the cursor image appear when you + * move it off of a client or over its border. */ if (!surface && !seat->drag && !cursor_hidden) wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); - if (c && c->mon && !c->animation.running && - (!(c->geom.x + c->geom.width > c->mon->m.x + c->mon->m.width || - c->geom.x < c->mon->m.x || - c->geom.y + c->geom.height > c->mon->m.y + c->mon->m.height || - c->geom.y < c->mon->m.y) || - !ISTILED(c))) { + if (c && c->mon && !c->animation.running && (INSIDEMON(c) || !ISTILED(c))) { scroller_focus_lock = 0; } should_lock = false; - if (!scroller_focus_lock || - !(c && c->mon && - (c->geom.x + c->geom.width > c->mon->m.x + c->mon->m.width || - c->geom.x < c->mon->m.x || - c->geom.y + c->geom.height > c->mon->m.y + c->mon->m.height || - c->geom.y < c->mon->m.y))) { - if (c && c->mon && is_scroller_layout(c->mon) && - (c->geom.x + c->geom.width > c->mon->m.x + c->mon->m.width || - c->geom.x < c->mon->m.x || - c->geom.y + c->geom.height > c->mon->m.y + c->mon->m.height || - c->geom.y < c->mon->m.y)) { + if (!scroller_focus_lock || !(c && c->mon && !INSIDEMON(c))) { + if (c && c->mon && is_scroller_layout(c->mon) && !INSIDEMON(c)) { should_lock = true; } - pointerfocus(c, surface, sx, sy, time); + + if (!(!edge_scroller_pointer_focus && c && c->mon && + is_scroller_layout(c->mon) && !INSIDEMON(c))) + pointerfocus(c, surface, sx, sy, time); if (should_lock && c && c->mon && ISTILED(c) && c == c->mon->sel) { scroller_focus_lock = 1; @@ -3677,14 +4086,19 @@ void motionnotify(unsigned int time, struct wlr_input_device *device, double dx, } void motionrelative(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits a _relative_ - * pointer motion event (i.e. a delta) */ + /* This event is forwarded by the cursor when a pointer emits a + * _relative_ pointer motion event (i.e. a delta) */ struct wlr_pointer_motion_event *event = data; - /* The cursor doesn't move unless we tell it to. The cursor automatically - * handles constraining the motion to the output layout, as well as any - * special configuration applied for the specific input device which - * generated the event. You can pass NULL for the device if you want to move - * the cursor around without any input. */ + /* The cursor doesn't move unless we tell it to. The cursor + * automatically handles constraining the motion to the output layout, + * as well as any special configuration applied for the specific input + * device which generated the event. You can pass NULL for the device if + * you want to move the cursor around without any input. */ + + if (check_trackpad_disabled(event->pointer)) { + return; + } + motionnotify(event->time_msec, &event->pointer->base, event->delta_x, event->delta_y, event->unaccel_dx, event->unaccel_dy); toggle_hotarea(cursor->x, cursor->y); @@ -3696,7 +4110,7 @@ void outputmgrapply(struct wl_listener *listener, void *data) { } void // 0.7 custom -outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) { +outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int32_t test) { /* * Called when a client such as wlr-randr requests a change in output * configuration. This is only one way that the layout can be changed, @@ -3704,15 +4118,15 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) { * output_layout.change event, not here. */ struct wlr_output_configuration_head_v1 *config_head; - int ok = 1; + int32_t ok = 1; wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; Monitor *m = wlr_output->data; struct wlr_output_state state; - /* Ensure displays previously disabled by wlr-output-power-management-v1 - * are properly handled*/ + /* Ensure displays previously disabled by + * wlr-output-power-management-v1 are properly handled*/ m->asleep = 0; wlr_output_state_init(&state); @@ -3764,11 +4178,11 @@ void outputmgrtest(struct wl_listener *listener, void *data) { } void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, - unsigned int time) { + uint32_t time) { struct timespec now; if (surface != seat->pointer_state.focused_surface && sloppyfocus && time && - c && !client_is_unmanaged(c)) + c && c->scene->node.enabled && !client_is_unmanaged(c)) focusclient(c, 0); /* If surface is NULL, clear pointer focus */ @@ -3789,20 +4203,8 @@ void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, wlr_seat_pointer_notify_motion(seat, time, sx, sy); } -void // 17 -printstatus(void) { - Monitor *m = NULL; - wl_list_for_each(m, &mons, link) { - if (!m->wlr_output->enabled) { - continue; - } - // Update workspace active states - dwl_ext_workspace_printstatus(m); - - // Update IPC output status - dwl_ipc_output_printstatus(m); - } -} +// 修改printstatus函数,接受掩码参数 +void printstatus(void) { wl_signal_emit(&mango_print_status, NULL); } void powermgrsetmode(struct wl_listener *listener, void *data) { struct wlr_output_power_v1_set_mode_event *event = data; @@ -3812,7 +4214,6 @@ void powermgrsetmode(struct wl_listener *listener, void *data) { if (!m) return; - m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the ouput */ wlr_output_state_set_enabled(&state, event->mode); wlr_output_commit_state(m->wlr_output, &state); @@ -3820,10 +4221,10 @@ void powermgrsetmode(struct wl_listener *listener, void *data) { updatemons(NULL, NULL); } -void quitsignal(int signo) { quit(NULL); } +void quitsignal(int32_t signo) { quit(NULL); } -void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, int sx, int sy, - void *data) { +void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, int32_t sx, + int32_t sy, void *data) { wlr_scene_buffer_set_opacity(buffer, *(double *)data); } @@ -3834,18 +4235,27 @@ void client_set_opacity(Client *c, double opacity) { void rendermon(struct wl_listener *listener, void *data) { Monitor *m = wl_container_of(listener, m, frame); - Client *c, *tmp; + Client *c = NULL, *tmp = NULL; struct wlr_output_state pending = {0}; - LayerSurface *l, *tmpl; - int i; + LayerSurface *l = NULL, *tmpl = NULL; + int32_t i; struct wl_list *layer_list; - + bool frame_allow_tearing = false; struct timespec now; bool need_more_frames = false; + if (session && !session->active) { + return; + } + + if (!m->wlr_output->enabled || !allow_frame_scheduling) + return; + + frame_allow_tearing = check_tearing_frame_allow(m); + + // 绘制层和淡出效果 for (i = 0; i < LENGTH(m->layers); i++) { layer_list = &m->layers[i]; - // Draw frames for all layer wl_list_for_each_safe(l, tmpl, layer_list, link) { need_more_frames = layer_draw_frame(l) || need_more_frames; } @@ -3859,35 +4269,65 @@ void rendermon(struct wl_listener *listener, void *data) { need_more_frames = layer_draw_fadeout_frame(l) || need_more_frames; } - // Draw frames for all clients + // 绘制客户端 wl_list_for_each(c, &clients, link) { need_more_frames = client_draw_frame(c) || need_more_frames; - if (!animations && c->configure_serial && !c->isfloating && - client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) + if (!animations && !(allow_tearing && frame_allow_tearing) && + c->configure_serial && !c->isfloating && + client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) { goto skip; + } } - wlr_scene_output_commit(m->scene_output, NULL); + // 只有在需要帧时才构建和提交状态 + if (allow_tearing && frame_allow_tearing) { + apply_tear_state(m); + } else { + wlr_scene_output_commit(m->scene_output, NULL); + } skip: - - // Send frame done notification + // 发送帧完成通知 clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_send_frame_done(m->scene_output, &now); + if (allow_tearing && frame_allow_tearing) { + wlr_scene_output_send_frame_done(m->scene_output, &now); + } else { + wlr_scene_output_send_frame_done(m->scene_output, &now); + wlr_output_state_finish(&pending); + } - // // Clean up pending state - wlr_output_state_finish(&pending); - - if (need_more_frames) { - wlr_output_schedule_frame(m->wlr_output); + // 如果需要更多帧,确保安排下一帧 + if (need_more_frames && allow_frame_scheduling) { + request_fresh_all_monitors(); } } void requestdecorationmode(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_decoration_mode); - if (c->surface.xdg->initialized) - wlr_xdg_toplevel_decoration_v1_set_mode( - c->decoration, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + struct wlr_xdg_toplevel_decoration_v1 *deco = data; + + if (c->surface.xdg->initialized) { + // 获取客户端请求的模式 + enum wlr_xdg_toplevel_decoration_v1_mode requested_mode = + deco->requested_mode; + + // 如果客户端没有指定,使用默认模式 + if (!c->allow_csd) { + requested_mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; + } + + wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration, requested_mode); + } +} + +static void requestdrmlease(struct wl_listener *listener, void *data) { + struct wlr_drm_lease_request_v1 *req = data; + struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); + + if (!lease) { + wlr_log(WLR_ERROR, "Failed to grant lease request"); + wlr_drm_lease_request_v1_reject(req); + } } void requeststartdrag(struct wl_listener *listener, void *data) { @@ -3903,30 +4343,38 @@ void requeststartdrag(struct wl_listener *listener, void *data) { void setborder_color(Client *c) { if (!c || !c->mon) return; - if (c->isurgent) { - client_set_border_color(c, urgentcolor); - return; - } - if (c->is_in_scratchpad && selmon && c == selmon->sel) { - client_set_border_color(c, scratchpadcolor); - } else if (c->isglobal && selmon && c == selmon->sel) { - client_set_border_color(c, globalcolor); - } else if (c->isoverlay && selmon && c == selmon->sel) { - client_set_border_color(c, overlaycolor); - } else if (c->ismaxmizescreen && selmon && c == selmon->sel) { - client_set_border_color(c, maxmizescreencolor); - } else if (selmon && c == selmon->sel) { - client_set_border_color(c, focuscolor); - } else { - client_set_border_color(c, bordercolor); - } + + float *border_color = get_border_color(c); + memcpy(c->opacity_animation.target_border_color, border_color, + sizeof(c->opacity_animation.target_border_color)); + client_set_border_color(c, border_color); } void exchange_two_client(Client *c1, Client *c2) { - if (c1 == NULL || c2 == NULL || c1->mon != c2->mon) { + + Monitor *tmp_mon = NULL; + uint32_t tmp_tags; + double master_inner_per = 0.0f; + double master_mfact_per = 0.0f; + double stack_inner_per = 0.0f; + + if (c1 == NULL || c2 == NULL || + (!exchange_cross_monitor && c1->mon != c2->mon)) { return; } + master_inner_per = c1->master_inner_per; + master_mfact_per = c1->master_mfact_per; + 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; + struct wl_list *tmp1_prev = c1->link.prev; struct wl_list *tmp2_prev = c2->link.prev; struct wl_list *tmp1_next = c1->link.next; @@ -3964,31 +4412,41 @@ void exchange_two_client(Client *c1, Client *c2) { tmp2_next->prev = &c1->link; } - arrange(c1->mon, false); - focusclient(c1, 0); + if (exchange_cross_monitor) { + tmp_mon = c2->mon; + tmp_tags = c2->tags; + setmon(c2, c1->mon, c1->tags, false); + setmon(c1, tmp_mon, tmp_tags, false); + arrange(c1->mon, false, false); + arrange(c2->mon, false, false); + focusclient(c1, 0); + } else { + arrange(c1->mon, false, false); + focusclient(c1, 0); + } } void // 17 run(char *startup_cmd) { - char autostart_temp_path[1024]; + + set_env(); + /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(dpy); if (!socket) die("startup: display_add_socket_auto"); setenv("WAYLAND_DISPLAY", socket, 1); - /* Start the backend. This will enumerate outputs and inputs, become the DRM - * master, etc */ + /* Start the backend. This will enumerate outputs and inputs, become the + * DRM master, etc */ if (!wlr_backend_start(backend)) die("startup: backend_start"); - /* Now that the socket exists and the backend is started, run the startup - * command */ - if (!startup_cmd) - startup_cmd = get_autostart_path(autostart_temp_path, - sizeof(autostart_temp_path)); + /* Now that the socket exists and the backend is started, run the + * startup command */ + if (startup_cmd) { - int piperw[2]; + int32_t piperw[2]; if (pipe(piperw) < 0) die("startup: pipe:"); if ((child_pid = fork()) < 0) @@ -4013,8 +4471,8 @@ run(char *startup_cmd) { printstatus(); - /* At this point the outputs are initialized, choose initial selmon based on - * cursor position, and set default cursor image */ + /* At this point the outputs are initialized, choose initial selmon + * based on cursor position, and set default cursor image */ selmon = xytomon(cursor->x, cursor->y); /* TODO hack to get cursor to display in its initial location (100, 100) @@ -4037,11 +4495,13 @@ run(char *startup_cmd) { } void setcursor(struct wl_listener *listener, void *data) { - /* This event is raised by the seat when a client provides a cursor image */ + /* This event is raised by the seat when a client provides a cursor + * image */ struct wlr_seat_pointer_request_set_cursor_event *event = data; /* If we're "grabbing" the cursor, don't use the client's image, we will - * restore it after "grabbing" sending a leave event, followed by a enter - * event, which will result in the client requesting set the cursor surface + * restore it after "grabbing" sending a leave event, followed by a + * enter event, which will result in the client requesting set the + * cursor surface */ if (cursor_mode != CurNormal && cursor_mode != CurPressed) return; @@ -4062,11 +4522,10 @@ void setcursor(struct wl_listener *listener, void *data) { } void // 0.5 -setfloating(Client *c, int floating) { +setfloating(Client *c, int32_t floating) { - Client *fc; - int hit; - struct wlr_box target_box, backup_box; + Client *fc = NULL; + struct wlr_box target_box; c->isfloating = floating; bool window_size_outofrange = false; @@ -4077,34 +4536,33 @@ setfloating(Client *c, int floating) { if (floating == 1 && c != grabc) { - if (c->isfullscreen || c->ismaxmizescreen) { + if (c->isfullscreen || c->ismaximizescreen) { c->isfullscreen = 0; // 清除窗口全屏标志 - c->ismaxmizescreen = 0; + c->ismaximizescreen = 0; c->bw = c->isnoborder ? 0 : borderpx; } // 重新计算居中的坐标 - if (!client_is_x11(c) || !client_should_ignore_focus(c)) + if (!client_is_x11(c) && !c->iscustompos) target_box = setclient_coordinate_center(c, target_box, 0, 0); - backup_box = c->geom; - hit = applyrulesgeom(c); - target_box = hit == 1 ? c->geom : target_box; - c->geom = backup_box; + else + target_box = c->geom; // restore to the memeroy geom - if (c->oldgeom.width > 0 && c->oldgeom.height > 0) { - if (c->mon && c->oldgeom.width >= c->mon->w.width - gappoh) { - c->oldgeom.width = c->mon->w.width * 0.9; + if (c->float_geom.width > 0 && c->float_geom.height > 0) { + if (c->mon && c->float_geom.width >= c->mon->w.width - gappoh) { + c->float_geom.width = c->mon->w.width * 0.9; window_size_outofrange = true; } - if (c->mon && c->oldgeom.height >= c->mon->w.height - gappov) { - c->oldgeom.height = c->mon->w.height * 0.9; + if (c->mon && c->float_geom.height >= c->mon->w.height - gappov) { + c->float_geom.height = c->mon->w.height * 0.9; window_size_outofrange = true; } if (window_size_outofrange) { - c->oldgeom = setclient_coordinate_center(c, c->oldgeom, 0, 0); + c->float_geom = + setclient_coordinate_center(c, c->float_geom, 0, 0); } - resize(c, c->oldgeom, 0); + resize(c, c->float_geom, 0); } else { resize(c, target_box, 0); } @@ -4118,9 +4576,9 @@ setfloating(Client *c, int floating) { c->is_in_scratchpad = 0; c->isnamedscratchpad = 0; // 让当前tag中的全屏窗口退出全屏参与平铺 - wl_list_for_each(fc, &clients, link) if (fc && fc != c && - c->tags & fc->tags && - ISFULLSCREEN(fc)) { + wl_list_for_each(fc, &clients, + link) if (fc && fc != c && VISIBLEON(fc, c->mon) && + c->tags & fc->tags && ISFULLSCREEN(fc)) { clear_fullscreen_flag(fc); } } @@ -4128,18 +4586,25 @@ setfloating(Client *c, int floating) { if (c->isoverlay) { wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); } else if (client_should_overtop(c) && c->isfloating) { - wlr_scene_node_reparent(&c->scene->node, layers[LyrFSorOverTop]); + wlr_scene_node_reparent(&c->scene->node, layers[LyrTop]); } else { wlr_scene_node_reparent(&c->scene->node, - layers[c->isfloating ? LyrFloat : LyrTile]); + layers[c->isfloating ? LyrTop : LyrTile]); } - arrange(c->mon, false); + if (!c->isfloating) { + set_size_per(c->mon, c); + } + + if (!c->force_maximize) + client_set_maximized(c, false); + + arrange(c->mon, false, false); setborder_color(c); printstatus(); } -void reset_maxmizescreen_size(Client *c) { +void reset_maximizescreen_size(Client *c) { c->geom.x = c->mon->w.x + gappoh; c->geom.y = c->mon->w.y + gappov; c->geom.width = c->mon->w.width - 2 * gappoh; @@ -4147,46 +4612,55 @@ void reset_maxmizescreen_size(Client *c) { resize(c, c->geom, 0); } -void setmaxmizescreen(Client *c, int maxmizescreen) { - struct wlr_box maxmizescreen_box; +void setmaximizescreen(Client *c, int32_t maximizescreen) { + struct wlr_box maximizescreen_box; if (!c || !c->mon || !client_surface(c)->mapped || c->iskilling) return; - c->ismaxmizescreen = maxmizescreen; + if (c->mon->isoverview) + return; - if (maxmizescreen) { + c->ismaximizescreen = maximizescreen; + + if (maximizescreen) { if (c->isfullscreen) setfullscreen(c, 0); if (c->isfloating) - c->oldgeom = c->geom; - if (selmon->isoverview) { - Arg arg = {0}; - toggleoverview(&arg); - } + c->float_geom = c->geom; - maxmizescreen_box.x = c->mon->w.x + gappoh; - maxmizescreen_box.y = c->mon->w.y + gappov; - maxmizescreen_box.width = c->mon->w.width - 2 * gappoh; - maxmizescreen_box.height = c->mon->w.height - 2 * gappov; + maximizescreen_box.x = c->mon->w.x + gappoh; + maximizescreen_box.y = c->mon->w.y + gappov; + maximizescreen_box.width = c->mon->w.width - 2 * gappoh; + maximizescreen_box.height = c->mon->w.height - 2 * gappov; wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层 - resize(c, maxmizescreen_box, 0); - c->ismaxmizescreen = 1; + if (!is_scroller_layout(c->mon) || c->isfloating) + resize(c, maximizescreen_box, 0); + c->ismaximizescreen = 1; } else { c->bw = c->isnoborder ? 0 : borderpx; - c->ismaxmizescreen = 0; + c->ismaximizescreen = 0; if (c->isfloating) setfloating(c, 1); - arrange(c->mon, false); } - wlr_scene_node_reparent(&c->scene->node, layers[maxmizescreen ? LyrTile - : c->isfloating ? LyrFloat - : LyrTile]); + wlr_scene_node_reparent(&c->scene->node, + layers[c->isfloating ? LyrTop : LyrTile]); + if (!c->ismaximizescreen) { + set_size_per(c->mon, c); + } + + if (!c->force_maximize && !c->ismaximizescreen) { + client_set_maximized(c, false); + } else if (!c->force_maximize && c->ismaximizescreen) { + client_set_maximized(c, true); + } + + arrange(c->mon, false, false); } -void setfakefullscreen(Client *c, int fakefullscreen) { +void setfakefullscreen(Client *c, int32_t fakefullscreen) { c->isfakefullscreen = fakefullscreen; if (!c->mon) return; @@ -4196,31 +4670,31 @@ void setfakefullscreen(Client *c, int fakefullscreen) { client_set_fullscreen(c, fakefullscreen); } -void setfullscreen(Client *c, int fullscreen) // 用自定义全屏代理自带全屏 +void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自带全屏 { - c->isfullscreen = fullscreen; if (!c || !c->mon || !client_surface(c)->mapped || c->iskilling) return; + if (c->mon->isoverview) + return; + + c->isfullscreen = fullscreen; + client_set_fullscreen(c, fullscreen); if (fullscreen) { - if (c->ismaxmizescreen) - setmaxmizescreen(c, 0); + if (c->ismaximizescreen) + setmaximizescreen(c, 0); if (c->isfloating) - c->oldgeom = c->geom; - if (selmon->isoverview) { - Arg arg = {0}; - toggleoverview(&arg); - } + c->float_geom = c->geom; c->bw = 0; wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层 - resize(c, c->mon->m, 1); + if (!is_scroller_layout(c->mon) || c->isfloating) + resize(c, c->mon->m, 1); c->isfullscreen = 1; - // c->isfloating = 0; } else { c->bw = c->isnoborder ? 0 : borderpx; c->isfullscreen = 0; @@ -4232,23 +4706,26 @@ void setfullscreen(Client *c, int fullscreen) // 用自定义全屏代理自带 if (c->isoverlay) { wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); } else if (client_should_overtop(c) && c->isfloating) { - wlr_scene_node_reparent(&c->scene->node, layers[LyrFSorOverTop]); + wlr_scene_node_reparent(&c->scene->node, layers[LyrTop]); } else { - wlr_scene_node_reparent(&c->scene->node, - layers[fullscreen ? LyrFSorOverTop - : c->isfloating ? LyrFloat - : LyrTile]); + wlr_scene_node_reparent( + &c->scene->node, + layers[fullscreen || c->isfloating ? LyrTop : LyrTile]); } - arrange(c->mon, false); + if (!c->isfullscreen) { + set_size_per(c->mon, c); + } + + arrange(c->mon, false, false); } -void setgaps(int oh, int ov, int ih, int iv) { +void setgaps(int32_t oh, int32_t ov, int32_t ih, int32_t iv) { selmon->gappoh = MAX(oh, 0); selmon->gappov = MAX(ov, 0); selmon->gappih = MAX(ih, 0); selmon->gappiv = MAX(iv, 0); - arrange(selmon, false); + arrange(selmon, false, false); } void reset_keyboard_layout(void) { @@ -4266,7 +4743,7 @@ void reset_keyboard_layout(void) { // Get current layout xkb_layout_index_t current = xkb_state_serialize_layout( keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); - const int num_layouts = xkb_keymap_num_layouts(keyboard->keymap); + const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap); if (num_layouts < 1) { wlr_log(WLR_INFO, "No layouts available"); return; @@ -4279,38 +4756,37 @@ void reset_keyboard_layout(void) { return; } - // Get layout abbreviations - char **layout_ids = calloc(num_layouts, sizeof(char *)); - if (!layout_ids) { - wlr_log(WLR_ERROR, "Failed to allocate layout IDs"); + // 现在安全地创建真正的keymap + struct xkb_keymap *new_keymap = xkb_keymap_new_from_names( + context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!new_keymap) { + // 理论上这里不应该失败,因为前面已经验证过了 + wlr_log(WLR_ERROR, + "Unexpected failure to create keymap after validation"); goto cleanup_context; } - for (int i = 0; i < num_layouts; i++) { - layout_ids[i] = - get_layout_abbr(xkb_keymap_layout_get_name(keyboard->keymap, i)); - if (!layout_ids[i]) { - wlr_log(WLR_ERROR, "Failed to get layout abbreviation"); - goto cleanup_layouts; - } + // 验证新keymap是否有布局 + const int32_t new_num_layouts = xkb_keymap_num_layouts(new_keymap); + if (new_num_layouts < 1) { + wlr_log(WLR_ERROR, "New keymap has no layouts"); + xkb_keymap_unref(new_keymap); + goto cleanup_context; } - // Keep the same rules but just reapply them - struct xkb_rule_names rules = xkb_rules; - - // Create new keymap with current rules - struct xkb_keymap *new_keymap = - xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (!new_keymap) { - wlr_log(WLR_ERROR, "Failed to create keymap for layouts: %s", - rules.layout); - goto cleanup_layouts; + // 确保当前布局索引在新keymap中有效 + if (current >= new_num_layouts) { + wlr_log(WLR_INFO, + "Current layout index %u out of range for new keymap, " + "resetting to 0", + current); + current = 0; } - // Apply the same keymap (this will reset the layout state) - unsigned int depressed = keyboard->modifiers.depressed; - unsigned int latched = keyboard->modifiers.latched; - unsigned int locked = keyboard->modifiers.locked; + // Apply the new keymap + uint32_t depressed = keyboard->modifiers.depressed; + uint32_t latched = keyboard->modifiers.latched; + uint32_t locked = keyboard->modifiers.locked; wlr_keyboard_set_keymap(keyboard, new_keymap); @@ -4321,20 +4797,31 @@ void reset_keyboard_layout(void) { wlr_seat_set_keyboard(seat, keyboard); wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers); + InputDevice *id; + wl_list_for_each(id, &inputdevices, link) { + if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { + continue; + } + + struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data; + + wlr_keyboard_set_keymap(tkb, keyboard->keymap); + wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, 0); + tkb->modifiers.group = 0; + + // 7. 更新 seat + wlr_seat_set_keyboard(seat, tkb); + wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers); + } + // Cleanup xkb_keymap_unref(new_keymap); -cleanup_layouts: - for (int i = 0; i < num_layouts; i++) { - free(layout_ids[i]); - } - free(layout_ids); - cleanup_context: xkb_context_unref(context); } -void setmon(Client *c, Monitor *m, unsigned int newtags, bool focus) { +void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) { Monitor *oldmon = c->mon; if (oldmon == m) @@ -4352,7 +4839,7 @@ void setmon(Client *c, Monitor *m, unsigned int newtags, bool focus) { /* Scene graph sends surface leave/enter events on move and resize */ if (oldmon) - arrange(oldmon, false); + arrange(oldmon, false, false); if (m) { /* Make sure window actually overlaps with the monitor */ resize(c, c->geom, 0); @@ -4362,10 +4849,17 @@ void setmon(Client *c, Monitor *m, unsigned int newtags, bool focus) { setfloating(c, c->isfloating); setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */ } - if (m && focus) - focusclient(focustop(m), 1); - if (!c->foreign_toplevel && m) { + if (focus && !client_is_x11_popup(c)) { + focusclient(focustop(selmon), 1); + } + + if (m) { + + if (c->foreign_toplevel) { + remove_foreign_topleve(c); + } + add_foreign_toplevel(c); if (m->sel && m->sel->foreign_toplevel) wlr_foreign_toplevel_handle_v1_set_activated( @@ -4397,10 +4891,18 @@ void setsel(struct wl_listener *listener, void *data) { } void show_hide_client(Client *c) { - unsigned int target = get_tags_first_tag(c->oldtags); - tag_client(&(Arg){.ui = target}, c); - // c->tags = c->oldtags; - c->isminied = 0; + uint32_t target = 1; + + set_size_per(c->mon, c); + target = get_tags_first_tag(c->oldtags); + + if (!c->is_in_scratchpad) { + tag_client(&(Arg){.ui = target}, c); + } else { + c->tags = c->oldtags; + arrange(c->mon, false, false); + } + c->isminimized = 0; wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, false); focusclient(c, 1); wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, true); @@ -4427,15 +4929,28 @@ void create_output(struct wlr_backend *backend, void *data) { #endif } +// 修改信号处理函数,接收掩码参数 +void handle_print_status(struct wl_listener *listener, void *data) { + + Monitor *m = NULL; + wl_list_for_each(m, &mons, link) { + if (!m->wlr_output->enabled) { + continue; + } + dwl_ext_workspace_printstatus(m); + + dwl_ipc_output_printstatus(m); + } +} + void setup(void) { setenv("XCURSOR_SIZE", "24", 1); setenv("XDG_CURRENT_DESKTOP", "mango", 1); - parse_config(); init_baked_points(); - int drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE}; + int32_t drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE}; struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig}; sigemptyset(&sa.sa_mask); @@ -4458,6 +4973,14 @@ void setup(void) { * backend uses the renderer, for example, to fall back to software cursors * if the backend does not support hardware cursors (some older GPUs * don't). */ + /* The backend is a wlroots feature which abstracts the underlying input + * and output hardware. The autocreate option will choose the most + * suitable backend based on the current environment, such as opening an + * X11 window if an X11 server is running. The NULL argument here + * optionally allows you to pass in a custom renderer if wlr_renderer + * doesn't meet your needs. The backend uses the renderer, for example, + * to fall back to software cursors if the backend does not support + * hardware cursors (some older GPUs don't). */ if (!(backend = wlr_backend_autocreate(event_loop, &session))) die("couldn't create backend"); @@ -4485,8 +5008,8 @@ void setup(void) { /* Create shm, drm and linux_dmabuf interfaces by ourselves. * The simplest way is call: * wlr_renderer_init_wl_display(drw); - * but we need to create manually the linux_dmabuf interface to integrate it - * with wlr_scene. */ + * but we need to create manually the linux_dmabuf interface to + * integrate it with wlr_scene. */ wlr_renderer_init_wl_shm(drw, dpy); if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) { @@ -4504,11 +5027,11 @@ void setup(void) { die("couldn't create allocator"); /* This creates some hands-off wlroots interfaces. The compositor is - * necessary for clients to allocate surfaces and the data device manager - * handles the clipboard. Each of these wlroots interfaces has room for you - * to dig your fingers in and play with their behavior if you want. Note - * that the clients cannot set the selection directly without compositor - * approval, see the setsel() function. */ + * necessary for clients to allocate surfaces and the data device + * manager handles the clipboard. Each of these wlroots interfaces has + * room for you to dig your fingers in and play with their behavior if + * you want. Note that the clients cannot set the selection directly + * without compositor approval, see the setsel() function. */ compositor = wlr_compositor_create(dpy, 6, drw); wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); @@ -4525,6 +5048,10 @@ void setup(void) { wlr_alpha_modifier_v1_create(dpy); wlr_ext_data_control_manager_v1_create(dpy, 1); + // 在 setup 函数中 + wl_signal_init(&mango_print_status); + wl_signal_add(&mango_print_status, &print_status_listener); + /* Initializes the interface used to implement urgency hints */ activation = wlr_xdg_activation_v1_create(dpy); wl_signal_add(&activation->events.request_activate, &request_activate); @@ -4535,14 +5062,18 @@ void setup(void) { power_mgr = wlr_output_power_manager_v1_create(dpy); wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode); + tearing_control = wlr_tearing_control_manager_v1_create(dpy, 1); + tearing_new_object.notify = handle_tearing_new_object; + wl_signal_add(&tearing_control->events.new_object, &tearing_new_object); + /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ output_layout = wlr_output_layout_create(dpy); wl_signal_add(&output_layout->events.change, &layout_change); wlr_xdg_output_manager_v1_create(dpy, output_layout); - /* Configure a listener to be notified when new outputs are available on the - * backend. */ + /* Configure a listener to be notified when new outputs are available on + * the backend. */ wl_list_init(&mons); wl_signal_add(&backend->events.new_output, &new_output); @@ -4599,23 +5130,25 @@ void setup(void) { wlr_cursor_attach_output_layout(cursor, output_layout); /* Creates an xcursor manager, another wlroots utility which loads up - * Xcursor themes to source cursor images from and makes sure that cursor - * images are available at all scale factors on the screen (necessary for - * HiDPI support). Scaled cursors will be loaded with each output. */ + * Xcursor themes to source cursor images from and makes sure that + * cursor images are available at all scale factors on the screen + * (necessary for HiDPI support). Scaled cursors will be loaded with + * each output. */ // cursor_mgr = wlr_xcursor_manager_create(cursor_theme, 24); cursor_mgr = wlr_xcursor_manager_create(config.cursor_theme, cursor_size); /* - * wlr_cursor *only* displays an image on screen. It does not move around - * when the pointer moves. However, we can attach input devices to it, and - * it will generate aggregate events for all of them. In these events, we - * can choose how we want to process them, forwarding them to clients and - * moving the cursor around. More detail on this process is described in my - * input handling blog post: + * wlr_cursor *only* displays an image on screen. It does not move + * around when the pointer moves. However, we can attach input devices + * to it, and it will generate aggregate events for all of them. In + * these events, we can choose how we want to process them, forwarding + * them to clients and moving the cursor around. More detail on this + * process is described in my input handling blog post: * * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html * - * And more comments are sprinkled throughout the notify functions above. + * And more comments are sprinkled throughout the notify functions + * above. */ wl_signal_add(&cursor->events.motion, &cursor_motion); wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute); @@ -4638,10 +5171,11 @@ void setup(void) { /* * Configures a seat, which is a single "seat" at which a user sits and * operates the computer. This conceptually includes up to one keyboard, - * pointer, touch, and drawing tablet device. We also rig up a listener to - * let us know when new input devices are available on the backend. + * pointer, touch, and drawing tablet device. We also rig up a listener + * to let us know when new input devices are available on the backend. */ - wl_list_init(&keyboards); + wl_list_init(&inputdevices); + wl_list_init(&keyboard_shortcut_inhibitors); wl_signal_add(&backend->events.new_input, &new_input_device); virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, @@ -4671,6 +5205,10 @@ void setup(void) { kb_group = createkeyboardgroup(); wl_list_init(&kb_group->destroy.link); + keyboard_shortcuts_inhibit = wlr_keyboard_shortcuts_inhibit_v1_create(dpy); + wl_signal_add(&keyboard_shortcuts_inhibit->events.new_inhibitor, + &keyboard_shortcuts_inhibit_new_inhibitor); + output_mgr = wlr_output_manager_v1_create(dpy); wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); wl_signal_add(&output_mgr->events.test, &output_mgr_test); @@ -4686,6 +5224,14 @@ void setup(void) { dwl_input_method_relay = dwl_im_relay_create(); + drm_lease_manager = wlr_drm_lease_v1_manager_create(dpy, backend); + if (drm_lease_manager) { + wl_signal_add(&drm_lease_manager->events.request, &drm_lease_request); + } else { + wlr_log(WLR_DEBUG, "Failed to create wlr_drm_lease_device_v1."); + wlr_log(WLR_INFO, "VR will not be available."); + } + wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); @@ -4726,19 +5272,22 @@ void startdrag(struct wl_listener *listener, void *data) { } void tag_client(const Arg *arg, Client *target_client) { - Client *fc; + Client *fc = NULL; if (target_client && arg->ui & TAGMASK) { + target_client->tags = arg->ui & TAGMASK; + target_client->istagswitching = 1; + wl_list_for_each(fc, &clients, link) { if (fc && fc != target_client && target_client->tags & fc->tags && ISFULLSCREEN(fc) && !target_client->isfloating) { clear_fullscreen_flag(fc); } } - view(&(Arg){.ui = arg->ui}, false); + view(&(Arg){.ui = arg->ui, .i = arg->i}, true); } else { - view(arg, false); + view(arg, true); } focusclient(target_client, 1); @@ -4748,11 +5297,15 @@ void tag_client(const Arg *arg, Client *target_client) { void overview(Monitor *m) { grid(m); } // 目标窗口有其他窗口和它同个tag就返回0 -unsigned int want_restore_fullscreen(Client *target_client) { +uint32_t want_restore_fullscreen(Client *target_client) { Client *c = NULL; wl_list_for_each(c, &clients, link) { if (c && c != target_client && c->tags == target_client->tags && - c == selmon->sel) { + c == selmon->sel && + c->mon->pertag->ltidxs[get_tags_first_tag_num(c->tags)]->id != + SCROLLER && + c->mon->pertag->ltidxs[get_tags_first_tag_num(c->tags)]->id != + VERTICAL_SCROLLER) { return 0; } } @@ -4764,7 +5317,7 @@ unsigned int want_restore_fullscreen(Client *target_client) { void overview_backup(Client *c) { c->overview_isfloatingbak = c->isfloating; c->overview_isfullscreenbak = c->isfullscreen; - c->overview_ismaxmizescreenbak = c->ismaxmizescreen; + c->overview_ismaximizescreenbak = c->ismaximizescreen; c->overview_isfullscreenbak = c->isfullscreen; c->animation.tagining = false; c->animation.tagouted = false; @@ -4774,9 +5327,9 @@ void overview_backup(Client *c) { if (c->isfloating) { c->isfloating = 0; } - if (c->isfullscreen || c->ismaxmizescreen) { + if (c->isfullscreen || c->ismaximizescreen) { c->isfullscreen = 0; // 清除窗口全屏标志 - c->ismaxmizescreen = 0; + c->ismaximizescreen = 0; } c->bw = c->isnoborder ? 0 : borderpx; } @@ -4785,10 +5338,10 @@ void overview_backup(Client *c) { void overview_restore(Client *c, const Arg *arg) { c->isfloating = c->overview_isfloatingbak; c->isfullscreen = c->overview_isfullscreenbak; - c->ismaxmizescreen = c->overview_ismaxmizescreenbak; + c->ismaximizescreen = c->overview_ismaximizescreenbak; c->overview_isfloatingbak = 0; c->overview_isfullscreenbak = 0; - c->overview_ismaxmizescreenbak = 0; + c->overview_ismaximizescreenbak = 0; c->geom = c->overview_backup_geom; c->bw = c->overview_backup_bw; c->animation.tagining = false; @@ -4797,14 +5350,14 @@ void overview_restore(Client *c, const Arg *arg) { if (c->isfloating) { // XRaiseWindow(dpy, c->win); // 提升悬浮窗口到顶层 resize(c, c->overview_backup_geom, 0); - } else if (c->isfullscreen || c->ismaxmizescreen) { - if (want_restore_fullscreen(c) && c->ismaxmizescreen) { - setmaxmizescreen(c, 1); + } else if (c->isfullscreen || c->ismaximizescreen) { + if (want_restore_fullscreen(c) && c->ismaximizescreen) { + setmaximizescreen(c, 1); } else if (want_restore_fullscreen(c) && c->isfullscreen) { setfullscreen(c, 1); } else { c->isfullscreen = 0; - c->ismaxmizescreen = 0; + c->ismaximizescreen = 0; setfullscreen(c, false); } } else { @@ -4836,74 +5389,12 @@ void handlecursoractivity(void) { last_cursor.hotspot_x, last_cursor.hotspot_y); } -int hidecursor(void *data) { +int32_t hidecursor(void *data) { wlr_cursor_unset_image(cursor); cursor_hidden = true; return 1; } -// 显示所有tag 或 跳转到聚焦窗口的tag -void toggleoverview(const Arg *arg) { - Client *c; - - if (selmon->isoverview && ov_tab_mode && arg->i != -1 && selmon->sel) { - focusstack(&(Arg){.i = 1}); - return; - } - - selmon->isoverview ^= 1; - unsigned int target; - unsigned int visible_client_number = 0; - - if (selmon->isoverview) { - wl_list_for_each(c, &clients, - link) if (c && c->mon == selmon && - !client_is_unmanaged(c) && - !client_should_ignore_focus(c) && - !c->isminied && !c->isunglobal) { - visible_client_number++; - } - if (visible_client_number > 0) { - target = ~0; - } else { - selmon->isoverview ^= 1; - return; - } - } else if (!selmon->isoverview && selmon->sel) { - target = get_tags_first_tag(selmon->sel->tags); - } else if (!selmon->isoverview && !selmon->sel) { - target = (1 << (selmon->pertag->prevtag - 1)); - view(&(Arg){.ui = target}, false); - refresh_monitors_workspaces_status(selmon); - return; - } - - // 正常视图到overview,退出所有窗口的浮动和全屏状态参与平铺, - // overview到正常视图,还原之前退出的浮动和全屏窗口状态 - if (selmon->isoverview) { - wl_list_for_each(c, &clients, link) { - if (c && !client_is_unmanaged(c) && - !client_should_ignore_focus(c) && !c->isunglobal) - overview_backup(c); - } - } else { - wl_list_for_each(c, &clients, link) { - if (c && !c->iskilling && !client_is_unmanaged(c) && - !c->isunglobal && !client_should_ignore_focus(c) && - client_surface(c)->mapped) - overview_restore(c, &(Arg){.ui = target}); - } - } - - view(&(Arg){.ui = target}, false); - - if (ov_tab_mode && selmon->isoverview && selmon->sel) { - focusstack(&(Arg){.i = 1}); - } - - refresh_monitors_workspaces_status(selmon); -} - void unlocksession(struct wl_listener *listener, void *data) { SessionLock *lock = wl_container_of(listener, lock, unlock); destroylock(lock, 1); @@ -4927,12 +5418,13 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) { } void unmapnotify(struct wl_listener *listener, void *data) { - /* Called when the surface is unmapped, and should no longer be shown. */ + /* Called when the surface is unmapped, and should no longer be shown. + */ Client *c = wl_container_of(listener, c, unmap); - Monitor *m; + Monitor *m = NULL; c->iskilling = 1; - if (animations && !c->is_clip_to_hide && !c->isminied && + if (animations && !c->is_clip_to_hide && !c->isminimized && (!c->mon || VISIBLEON(c, c->mon))) init_fadeout_client(c); @@ -4995,8 +5487,8 @@ void unmapnotify(struct wl_listener *listener, void *data) { } if (c->swallowedby) { + setmaximizescreen(c->swallowedby, c->ismaximizescreen); setfullscreen(c->swallowedby, c->isfullscreen); - setmaxmizescreen(c->swallowedby, c->ismaxmizescreen); c->swallowedby->swallowing = NULL; c->swallowedby = NULL; } @@ -5021,10 +5513,10 @@ void updatemons(struct wl_listener *listener, void *data) { */ struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); - Client *c; + Client *c = NULL; struct wlr_output_configuration_head_v1 *config_head; - Monitor *m; - int mon_pos_offsetx, mon_pos_offsety, oldx, oldy; + Monitor *m = NULL; + int32_t mon_pos_offsetx, mon_pos_offsety, oldx, oldy; /* First remove from the layout the disabled monitors */ wl_list_for_each(m, &mons, link) { @@ -5033,7 +5525,8 @@ void updatemons(struct wl_listener *listener, void *data) { config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); config_head->state.enabled = 0; - /* Remove this output from the layout to avoid cursor enter inside it */ + /* Remove this output from the layout to avoid cursor enter inside + * it */ wlr_output_layout_remove(output_layout, m->wlr_output); closemon(m); m->m = m->w = (struct wlr_box){0}; @@ -5075,7 +5568,7 @@ void updatemons(struct wl_listener *listener, void *data) { if (c->isfloating && c->mon == m) { c->geom.x += mon_pos_offsetx; c->geom.y += mon_pos_offsety; - c->oldgeom = c->geom; + c->float_geom = c->geom; resize(c, c->geom, 1); } @@ -5107,15 +5600,11 @@ void updatemons(struct wl_listener *listener, void *data) { /* Calculate the effective monitor geometry to use for clients */ arrangelayers(m); /* Don't move clients to the left output when plugging monitors */ - arrange(m, false); + arrange(m, false, false); /* make sure fullscreen clients have the right size */ if ((c = focustop(m)) && c->isfullscreen) resize(c, m->m, 0); - /* Try to re-set the gamma LUT when updating monitors, - * it's only really needed when enabling a disabled output, but meh. */ - m->gamma_lut_changed = 1; - config_head->state.x = m->m.x; config_head->state.y = m->m.y; @@ -5140,8 +5629,8 @@ void updatemons(struct wl_listener *listener, void *data) { /* FIXME: figure out why the cursor image is at 0,0 after turning all * the monitors on. * Move the cursor image where it used to be. It does not generate a - * wl_pointer.motion event for the clients, it's only the image what it's - * at the wrong position after all. */ + * wl_pointer.motion event for the clients, it's only the image what + * it's at the wrong position after all. */ wlr_cursor_move(cursor, NULL, 0, 0); wlr_output_manager_v1_set_configuration(output_mgr, config); @@ -5171,36 +5660,47 @@ urgent(struct wl_listener *listener, void *data) { return; if (focus_on_activate && !c->istagsilent && c != selmon->sel) { - view(&(Arg){.ui = c->tags}, true); + if (!(c->mon == selmon && c->tags & c->mon->tagset[c->mon->seltags])) + view_in_mon(&(Arg){.ui = c->tags}, true, c->mon, true); focusclient(c, 1); } else if (c != focustop(selmon)) { - if (client_surface(c)->mapped) - client_set_border_color(c, urgentcolor); c->isurgent = 1; + if (client_surface(c)->mapped) + setborder_color(c); printstatus(); } } -void view_in_mon(const Arg *arg, bool want_animation, Monitor *m) { - unsigned int i, tmptag; +void view_in_mon(const Arg *arg, bool want_animation, Monitor *m, + bool changefocus) { + uint32_t i, tmptag; - if (!m || (arg->ui != ~0 && m->isoverview)) { + if (!m || (arg->ui != (~0 & TAGMASK) && m->isoverview)) { return; } - if (arg->ui == 0) + if (arg->ui == 0) { return; + } + + if (arg->ui == UINT32_MAX) { + m->pertag->prevtag = get_tags_first_tag_num(m->tagset[m->seltags]); + m->seltags ^= 1; /* toggle sel tagset */ + m->pertag->curtag = get_tags_first_tag_num(m->tagset[m->seltags]); + goto toggleseltags; + } if ((m->tagset[m->seltags] & arg->ui & TAGMASK) != 0) { want_animation = false; } m->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) { m->tagset[m->seltags] = arg->ui & TAGMASK; tmptag = m->pertag->curtag; - if (arg->ui == ~0) + if (arg->ui == (~0 & TAGMASK)) m->pertag->curtag = 0; else { for (i = 0; !(arg->ui & 1 << i) && i < LENGTH(tags) && arg->ui != 0; @@ -5217,13 +5717,77 @@ void view_in_mon(const Arg *arg, bool want_animation, Monitor *m) { m->pertag->curtag = tmptag; } - focusclient(focustop(m), 1); - arrange(m, want_animation); +toggleseltags: + + if (changefocus) + focusclient(focustop(m), 1); + arrange(m, want_animation, true); printstatus(); } void view(const Arg *arg, bool want_animation) { - view_in_mon(arg, want_animation, selmon); + Monitor *m = NULL; + if (arg->i) { + view_in_mon(arg, want_animation, selmon, true); + wl_list_for_each(m, &mons, link) { + if (!m->wlr_output->enabled || m == selmon) + continue; + // only arrange, not change monitor focus + view_in_mon(arg, want_animation, m, false); + } + } else { + view_in_mon(arg, want_animation, selmon, true); + } +} + +static void +handle_keyboard_shortcuts_inhibitor_destroy(struct wl_listener *listener, + void *data) { + KeyboardShortcutsInhibitor *inhibitor = + wl_container_of(listener, inhibitor, destroy); + + wlr_log(WLR_DEBUG, "Removing keyboard shortcuts inhibitor"); + + wl_list_remove(&inhibitor->link); + wl_list_remove(&inhibitor->destroy.link); + free(inhibitor); +} + +void handle_keyboard_shortcuts_inhibit_new_inhibitor( + struct wl_listener *listener, void *data) { + + struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = data; + + if (allow_shortcuts_inhibit == SHORTCUTS_INHIBIT_DISABLE) { + return; + } + + // per-view, seat-agnostic config via criteria + Client *c = NULL; + LayerSurface *l = NULL; + + int32_t type = toplevel_from_wlr_surface(inhibitor->surface, &c, &l); + + if (type < 0) + return; + + if (type != LayerShell && c && !c->allow_shortcuts_inhibit) { + return; + } + + wlr_log(WLR_DEBUG, "Adding keyboard shortcuts inhibitor"); + + KeyboardShortcutsInhibitor *kbsinhibitor = + calloc(1, sizeof(KeyboardShortcutsInhibitor)); + + kbsinhibitor->inhibitor = inhibitor; + + kbsinhibitor->destroy.notify = handle_keyboard_shortcuts_inhibitor_destroy; + wl_signal_add(&inhibitor->events.destroy, &kbsinhibitor->destroy); + + wl_list_insert(&keyboard_shortcut_inhibitors, &kbsinhibitor->link); + + wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor); } void virtualkeyboard(struct wl_listener *listener, void *data) { @@ -5278,8 +5842,8 @@ void activatex11(struct wl_listener *listener, void *data) { if (c && c->swallowing) return; - if (c->isminied) { - c->isminied = 0; + if (c->isminimized) { + c->isminimized = 0; c->tags = c->mini_restore_tag; c->is_scratchpad_show = 0; c->is_in_scratchpad = 0; @@ -5293,18 +5857,19 @@ void activatex11(struct wl_listener *listener, void *data) { } if (focus_on_activate && !c->istagsilent && c != selmon->sel) { - view(&(Arg){.ui = c->tags}, true); + if (!(c->mon == selmon && c->tags & c->mon->tagset[c->mon->seltags])) + view_in_mon(&(Arg){.ui = c->tags}, true, c->mon, true); wlr_xwayland_surface_activate(c->surface.xwayland, 1); focusclient(c, 1); - need_arrange = false; + need_arrange = true; } else if (c != focustop(selmon)) { c->isurgent = 1; if (client_surface(c)->mapped) - client_set_border_color(c, urgentcolor); + setborder_color(c); } if (need_arrange) { - arrange(c->mon, false); + arrange(c->mon, false, false); } printstatus(); @@ -5333,13 +5898,13 @@ void configurex11(struct wl_listener *listener, void *data) { .height = event->height + c->bw * 2}, 0); } else { - arrange(c->mon, false); + arrange(c->mon, false, false); } } void createnotifyx11(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = data; - Client *c; + Client *c = NULL; /* Allocate a Client for this surface */ c = xsurface->data = ecalloc(1, sizeof(*c)); @@ -5382,7 +5947,7 @@ void sethints(struct wl_listener *listener, void *data) { printstatus(); if (c->isurgent && surface && surface->mapped) - client_set_border_color(c, urgentcolor); + setborder_color(c); } void xwaylandready(struct wl_listener *listener, void *data) { @@ -5408,24 +5973,27 @@ static void setgeometrynotify(struct wl_listener *listener, void *data) { } #endif -int main(int argc, char *argv[]) { +int32_t main(int32_t argc, char *argv[]) { char *startup_cmd = NULL; - int c; + int32_t c; - while ((c = getopt(argc, argv, "s:hdv")) != -1) { + while ((c = getopt(argc, argv, "s:c:hdv")) != -1) { if (c == 's') startup_cmd = optarg; else if (c == 'd') log_level = WLR_DEBUG; else if (c == 'v') die("mango " VERSION); + else if (c == 'c') + cli_config_path = optarg; else goto usage; } if (optind < argc) goto usage; - /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket + /* Wayland requires XDG_RUNTIME_DIR for creating its communications + * socket */ if (!getenv("XDG_RUNTIME_DIR")) die("XDG_RUNTIME_DIR must be set"); @@ -5435,5 +6003,5 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; usage: - die("Usage: %s [-v] [-d] [-s startup command]", argv[0]); + die("Usage: %s [-v] [-d] [-c config file] [-s startup command]", argv[0]); }