Compare commits

..

49 commits
0.14.0 ... main

Author SHA1 Message Date
DreamMaoMao
a515ad9b91 feat: add overview jump mode
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
2026-06-16 23:08:01 +08:00
DreamMaoMao
24fb167ae6 bump version to 0.14.4 2026-06-16 14:37:58 +08:00
DreamMaoMao
1ae6adabc3 opt: ensure sel client exist in all monitor 2026-06-16 13:40:32 +08:00
DreamMaoMao
87f00ab5e9 fix: dwindle refuse arrange when restore from tty in non-selmon
dwindle relies on sel window cutting, but the sel window has been
cleared when the display is destroyed and was not set when the display
is created
2026-06-16 13:36:21 +08:00
DreamMaoMao
405e8dbc1a opt: format code 2026-06-16 08:20:55 +08:00
DreamMaoMao
71689f6ef4 opt: warp cursor to focusing client when exchange 2026-06-16 08:19:50 +08:00
DreamMaoMao
84a7d3d1e9 opt: better direction algorithm for client find 2026-06-16 08:08:43 +08:00
DreamMaoMao
ce1879d417 fix: scroller proportion option miss apply in windowrule 2026-06-16 08:07:54 +08:00
DreamMaoMao
d81ca73ea1 opt: optimzie dir find logic
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
2026-06-16 00:06:49 +08:00
DreamMaoMao
55d366daf2 bump version to 0.14.3 2026-06-15 20:44:31 +08:00
DreamMaoMao
ba014d5059 update docs 2026-06-15 16:56:03 +08:00
DreamMaoMao
b0fb99b95e feat: add scroller property to tagrule 2026-06-15 16:49:42 +08:00
DreamMaoMao
fa24c606a0 opt: optimize tag animaiton when tagout from overview 2026-06-15 13:43:56 +08:00
DreamMaoMao
8ca013e6c3 opt: avoid arrange when restore from overview 2026-06-15 13:33:46 +08:00
DreamMaoMao
8a3d065cc1 opt: add ov workspace before remove all tag workspace 2026-06-15 10:43:54 +08:00
DreamMaoMao
2a9c38df1f fix: cursor not auto hide in overview 2026-06-15 09:53:34 +08:00
DreamMaoMao
4daab9e4d5 opt: optimzie client layer judge in overveiw
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
2026-06-15 00:02:14 +08:00
DreamMaoMao
83adb33ad2 fix: overview cursor jump miss apply when no mousebind 2026-06-15 00:00:05 +08:00
DreamMaoMao
15fb37f1c6 update docs 2026-06-14 13:14:38 +08:00
DreamMaoMao
7e178369ff opt: optimize drop tile client when cross monitor 2026-06-14 07:53:57 +08:00
DreamMaoMao
792bfac475 fix: can't resize tile scroller window when only two tiled client 2026-06-11 23:14:50 +08:00
DreamMaoMao
03e68ba069 opt: optimize marco use in client tile resize 2026-06-11 12:39:17 +08:00
DreamMaoMao
33cda5afea opt:optimize edge focus judge 2026-06-11 12:37:48 +08:00
DreamMaoMao
94db68ef88 fix: floating window can't get pointer focus when cross monitor 2026-06-11 11:08:44 +08:00
DreamMaoMao
cd6e2883db
Merge pull request #1027 from xtheeq/docs-update
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
docs: fix wayfreeze link page
2026-06-10 11:45:58 +08:00
DreamMaoMao
ef59224cdb fix: deck layout caculate error when multi master 2026-06-09 21:56:48 +08:00
DreamMaoMao
52732c928b fix: grid layout cant fullscreen 2026-06-09 21:50:16 +08:00
xtheeq
47f30454e6 docs: fix wayfreeze link page 2026-06-08 20:33:21 +05:30
DreamMaoMao
7faf9b6239 Merge pull request #1025 from osamamragab/main
feat: add hide cursor on keypress option (#871)
2026-06-08 21:44:13 +08:00
DreamMaoMao
36398a1af2 opt: optimize unmanaged client init 2026-06-08 19:29:00 +08:00
Osama Ragab
a429420b73 feat: add hide cursor on keypress option (#871) 2026-06-08 13:28:34 +08:00
DreamMaoMao
b8fa9a3043 Merge pull request #1025 from osamamragab/main
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
feat: add hide cursor on keypress option (#871)
2026-06-08 13:28:34 +08:00
DreamMaoMao
009e2d2111 opt: optimzie xwayland position set 2026-06-08 13:05:04 +08:00
DreamMaoMao
27f4f64173 opt: not need send output enter when layer map
scene scene-graph API will auto do this
2026-06-08 11:40:51 +08:00
DreamMaoMao
ccb58a4f1a docs: fix some error im sample 2026-06-07 18:08:55 +08:00
DreamMaoMao
db8d22ae8e bump version to 0.14.2 2026-06-06 21:21:34 +08:00
DreamMaoMao
3ab2780b84 fix: excrescent gap in fullscreen 2026-06-06 18:16:08 +08:00
DreamMaoMao
9459fe51fd bump version to 0.14.1 2026-06-06 12:30:44 +08:00
DreamMaoMao
4e87e3b80d opt: fix scan out support for fullscreen 2026-06-06 11:55:38 +08:00
DreamMaoMao
8a92184667
Merge pull request #762 from grecodavide/main
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
Add force kill option
2026-06-04 10:47:59 +08:00
Davide Greco
cc20d5cff0 feat: add force option in killclient 2026-06-04 10:46:36 +08:00
DreamMaoMao
13e9cfb237 fix: crash when use error device pointer in tablet create 2026-06-04 09:50:13 +08:00
DreamMaoMao
bea689ca0c fix: exchange not work in vertical scroller 2026-06-03 08:47:23 +08:00
DreamMaoMao
a11cf12f28 mmsg: add man page and usage message 2026-05-31 22:58:36 +08:00
DreamMaoMao
6849e5b4db opt: more reasonable method to set scoket flag
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
2026-05-30 11:53:39 +08:00
DreamMaoMao
8cb3a40ac7
Merge pull request #994 from TheXnlyGhxst/patch-1
update AerynOS install instructions
2026-05-30 10:23:49 +08:00
DreamMaoMao
c2cfac97b1
Merge pull request #995 from Yappaholic/main
guix: fix cjson import module
2026-05-30 10:22:55 +08:00
Yappaholic
0aec251028 guix: fix cjson import module 2026-05-29 23:39:07 +03:00
Justan
404d664fca
update AerynOS install instructions 2026-05-29 13:20:19 -04:00
33 changed files with 1418 additions and 557 deletions

View file

@ -88,7 +88,7 @@ bindr=Super,Super_L,spawn,rofi -show run
| Command | Param | Description |
| :--- | :--- | :--- |
| `killclient` | - | Close the focused window. |
| `killclient` | `force` | Close the focused window. If `force` is specified, sends `SIGKILL`. |
| `togglefloating` | - | Toggle floating state. |
| `toggle_all_floating` | - | Toggle all visible clients floating state. |
| `togglefullscreen` | - | Toggle fullscreen. |
@ -161,6 +161,7 @@ bindr=Super,Super_L,spawn,rofi -show run
| `reload_config` | - | Hot-reload configuration. |
| `quit` | - | Exit mangowm. |
| `toggleoverview` | - | Toggle overview mode. |
| `togglejump` | - | Toggle overview with jump mode. |
| `create_virtual_output` | - | Create a headless monitor (for VNC/Sunshine). |
| `destroy_all_virtual_output` | - | Destroy all virtual monitors. |
| `toggleoverlay` | - | Toggle overlay state for the focused window. |

View file

@ -21,6 +21,7 @@ description: Advanced settings for XWayland, focus behavior, and system integrat
| `sloppyfocus` | `1` | Focus follows the mouse cursor. |
| `warpcursor` | `1` | Warp the cursor to the center of the window when focus changes via keyboard. |
| `cursor_hide_timeout` | `0` | Hide the cursor after `N` seconds of inactivity (`0` to disable). |
| `cursor_hide_on_keypress` | `0` | Hide the cursor on keypress. |
| `drag_tile_to_tile` | `0` | Allow dragging a tiled window onto another to swap their positions. |
| `drag_tile_small` | `1` | Allow dragging a tiled window temporarily to small size.|
| `drag_corner` | `3` | Corner for drag-to-tile detection (0: none, 13: corners, 4: auto-detect). |

View file

@ -163,6 +163,8 @@ Some GPUs have compatibility issues with `syncobj_enable=1` — it may crash app
## Power Management
You can control monitor power using the `mmsg` IPC tool.
> Notice: This command does not remove the monitor, it only turns it off.
> if you want completely remove monitor, just use `wlr-randr`
```bash
# Turn off
@ -178,13 +180,13 @@ mmsg dispatch toggle_monitor,eDP-1
You can also use `wlr-randr` for monitor management:
```bash
# Turn off monitor
# remove a monitor
wlr-randr --output eDP-1 --off
# Turn on monitor
# add a monitor
wlr-randr --output eDP-1 --on
# Show all monitors
# Show all monitors spec
wlr-randr
```

View file

@ -18,6 +18,7 @@ You can install it using the `moss` package manager:
```bash
sudo moss install mangowm
```
* The Default config will be located at `/usr/share/defaults/mango`.
---
@ -273,6 +274,7 @@ If your distribution isn't listed above, or you want the latest unreleased chang
> - `hwdata`
> - `seatd`
> - `pcre2`
> - `pango`
> - `cjson`
> - `pixman`
> - `xorg-xwayland`

View file

@ -14,7 +14,7 @@ Instead, compose your own workflow from small Wayland utilities and bind them to
| [`slurp`](https://github.com/emersion/slurp) | Interactively select a region for `grim` |
| [`wl-copy`](https://github.com/bugaevc/wl-clipboard) | Copy screenshots directly to the clipboard |
| [`satty`](https://github.com/gabm/Satty) | Annotate screenshots before saving |
| [`wayfreeze`](https://github.com/nicbk/wayfreeze) | Freeze the screen before capture |
| [`wayfreeze`](https://github.com/Jappie3/wayfreeze) | Freeze the screen before capture |
Install the required with your package manager or from source.

View file

@ -52,6 +52,18 @@ You can also color-code windows based on their state:
> **Tip:** For scratchpad window sizing, see [Scratchpad](/docs/window-management/scratchpad) configuration.
### Overview Jump Mode
| Setting | Default | Description |
| :--- | :--- | :--- |
| `jump_hit_fg_color` | `0xc4939dff` | label text color. |
| `jump_hit_bg_color` | `0x201b14ff` | label background color.
| `jump_hit_border_color` | `0x8BAA9Bff` | label border color.
| `jump_hit_border_width` | `4` | label border width.
| `jump_hit_corner_radius` | `5` | label corner radius.
| `jump_hit_padding_x` | `10` | label horizontal padding.
| `jump_hit_padding_y` | `10` | label vertical padding.
| `jump_hit_font_desc` | `monospace Bold 24` | label font set.|
## Cursor Theme
Set the size and theme of your mouse cursor.

View file

@ -115,7 +115,7 @@ windowrule=width:1000,height:900,appid:yesplaymusic,title:Demons
# Global keybindings for OBS Studio
windowrule=globalkeybinding:ctrl+alt-o,appid:com.obsproject.Studio
windowrule=globalkeybinding:ctrl+alt+n,appid:com.obsproject.Studio
windowrule=globalkeybinding:ctrl+alt-n,appid:com.obsproject.Studio
windowrule=isopensilent:1,appid:com.obsproject.Studio
# Force tearing for games
@ -183,6 +183,9 @@ tagrule=id:Values,monitor_make:xxx,monitor_model:xxx,Parameter:Values
| `no_hide` | integer | `0` / `1` | Not hide even if the tag is empty |
| `nmaster` | integer | 0, 99 | Number of master windows |
| `mfact` | float | 0.10.9 | Master area factor |
| `scroller_default_proportion` | float | 0.1-1.0 | Set scroller default proportion. |
| `scroller_default_proportion_single` | float | 0.1-1.0 | Set scroller auto adjust proportion when it is single window(only apply when set `scroller_ignore_proportion_single` to `0`) |
| `scroller_ignore_proportion_single` | integer | `0` / `1` | Ignore scroller single proportion setting. |
### Examples
@ -204,6 +207,10 @@ tagrule=id:4,monitor_name:eDP-1,no_hide:1,layout_name:scroller
# Advanced tag configuration with master layout settings
tagrule=id:5,layout_name:tile,nmaster:2,mfact:0.6
tagrule=id:6,monitor_name:HDMI-A-1,layout_name:monocle,no_render_border:1
# set scroller proportion for specific tag
tagrule=id:1,layout_name:scroller,scroller_default_proportion_single:0.5,scroller_ignore_proportion_single:0,scroller_default_proportion:0.9,monitor_name:HDMI-A-1
```
> **Tip:** For Waybar configuration with persistent tags, see [Status Bar](/docs/visuals/status-bar) documentation.

View file

@ -10,7 +10,7 @@
#:use-module (gnu packages pciutils)
#:use-module (gnu packages admin)
#:use-module (gnu packages pcre)
#:use-module (gnu packages cjson)
#:use-module (gnu packages javascript)
#:use-module (gnu packages xorg)
#:use-module (gnu packages build-tools)
#:use-module (gnu packages ninja)
@ -54,6 +54,7 @@
hwdata
seatd
pcre2
pango
cjson
libxcb
pixman

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'],
version : '0.14.0',
project('mango', ['c'],
version : '0.14.4',
)
subdir('protocols')
@ -24,10 +24,6 @@ if sysconfdir.startswith(prefix) and not is_nixos
endif
endif
# 打印调试信息,确认 sysconfdir 的值
# message('prefix: ' + prefix)
# message('sysconfdir: ' + sysconfdir)
cc = meson.get_compiler('c')
libm = cc.find_library('m')
xcb = dependency('xcb', required : get_option('xwayland'))
@ -41,13 +37,13 @@ pcre2_dep = dependency('libpcre2-8')
libscenefx_dep = dependency('scenefx-0.4',version: '>=0.4.1')
pixman_dep = dependency('pixman-1')
cjson_dep = dependency('libcjson')
pangocairo_dep = dependency('pangocairo')
# 获取版本信息
# version info
git = find_program('git', required : false)
is_git_repo = false
# 检查当前目录是否是 Git 仓库
# check if current directory is a git repo
if git.found()
git_status = run_command(git, 'rev-parse', '--is-inside-work-tree', check : false)
if git_status.returncode() == 0 and git_status.stdout().strip() == 'true'
@ -56,18 +52,18 @@ if git.found()
endif
if is_git_repo
# 如果是 Git 目录,获取 Commit Hash 和最新的 tag
# if current directory is a git repo, get commit hash and latest tag
commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip()
latest_tag = meson.project_version()
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
else
# 如果不是 Git 目录,使用项目版本号和 "release" 字符串
# if not a git repo, use project version and "release" string
commit_hash = 'release'
latest_tag = meson.project_version()
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
endif
# 定义编译参数
# define compilation args
c_args = [
'-g',
'-Wno-unused-function',
@ -77,7 +73,7 @@ c_args = [
'-DSYSCONFDIR="@0@"'.format('/etc'),
]
# 仅在 debug 选项启用时添加调试参数
# add debug args only when debug option is enabled
if get_option('asan')
c_args += [
'-fsanitize=address',
@ -90,7 +86,7 @@ if xcb.found() and xlibs.found()
c_args += '-DXWAYLAND'
endif
# 链接参数(根据 debug 状态添加 ASAN
# define link args
link_args = []
if get_option('asan')
link_args += '-fsanitize=address'
@ -99,6 +95,7 @@ endif
executable('mango',
'src/mango.c',
'src/common/util.c',
'src/draw/text-node.c',
'src/ext-protocol/wlr_ext_workspace_v1.c',
wayland_sources,
dependencies : [
@ -114,6 +111,7 @@ executable('mango',
pcre2_dep,
pixman_dep,
cjson_dep,
pangocairo_dep,
],
install : true,
c_args : c_args,
@ -135,7 +133,7 @@ wayland_scanner_private_code = generator(
arguments: ['private-code', '@INPUT@', '@OUTPUT@']
)
# 在 mmsg 目标中使用生成器
# use generator in mmsg target
executable('mmsg',
'mmsg/mmsg.c',
wayland_scanner_private_code.process(dwl_ipc_protocol),
@ -150,8 +148,10 @@ executable('mmsg',
],
)
mandir = get_option('mandir')
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
portal_install_dir = join_paths(prefix, 'share/xdg-desktop-portal')
install_data('assets/mango.desktop', install_dir : desktop_install_dir)
install_data('assets/mango-portals.conf', install_dir : portal_install_dir)
install_data('assets/config.conf', install_dir : join_paths(sysconfdir, 'mango'))
install_data('mmsg/mmsg.1', install_dir : join_paths(mandir, 'man1'))

122
mmsg/mmsg.1 Normal file
View file

@ -0,0 +1,122 @@
.TH "mmsg" "1" "2026-05-31" "mmsg 0.14.0" "mmsg manual"
.SH NAME
mmsg \- send commands to the mango Wayland compositor and receive JSON responses
.SH SYNOPSIS
\fBmmsg\fR <command> [\fIargs\fR...]
.br
\fBmmsg\fR \-\-help | \-h | help
.SH DESCRIPTION
\fBmmsg\fR connects to a running \fBmango\fR(1) compositor via the UNIX domain socket
specified by the \fIMANGO_INSTANCE_SIGNATURE\fR environment variable. It sends
the given command and prints the JSON reply to standard output. In \fBwatch\fR
mode it keeps the connection open and prints a stream of updates as they
occur.
.PP
If \fB\-\-help\fR, \fB\-h\fR or \fBhelp\fR is given, a complete usage summary is printed and
the program exits with success.
.SH COMMANDS
.SS "One\-shot queries (get)"
All \fBget\fR commands print a single JSON object and then close the connection.
.TP
\fBget version\fR
Return compositor version.
.TP
\fBget keymode\fR
Return current keymode.
.TP
\fBget keyboardlayout\fR
Return current keyboard layout.
.TP
\fBget last_open_surface\fR [\fImonitor\fR]
Return the last open surface (application) for the given monitor.
If \fImonitor\fR is omitted the focused monitor is used.
.TP
\fBget monitor\fR <name>
Return details of the named monitor.
.TP
\fBget focusing\-client\fR
Return details of the currently focused client.
.TP
\fBget client\fR <id>
Return details of the client with the given numeric ID.
.TP
\fBget tag\fR <monitor> <index>
Return details of a specific tag (1\-based index) on the named monitor.
.TP
\fBget all\-clients\fR
List all clients.
.TP
\fBget all\-monitors\fR
List all monitors.
.TP
\fBget all\-tags\fR
List tags for all monitors.
.TP
\fBget tags\fR <monitor>
List tags for a specific monitor.
.TP
\fBdispatch\fR <func>[,arg...] [client,<id>]
Invoke an internal compositor function.
The function name and its arguments are separated by commas.
Optionally add \fBclient,<id>\fR (before or after the function) to target a
specific client.
.br
Examples:
.RS
.IP \(bu 2
\fBmmsg dispatch togglefloating\fR
.IP \(bu 2
\fBmmsg dispatch movewin,10,100\fR
.IP \(bu 2
\fBmmsg dispatch movewin,10,100 client,4\fR
.RE
.SS "Persistent streams (watch)"
\fBWatch\fR commands keep the connection open and continuously output JSON
updates. The initial state is sent immediately, followed by updates whenever
relevant changes occur.
.TP
\fBwatch monitor\fR <name>
Stream updates for the named monitor.
.TP
\fBwatch focusing\-client\fR
Stream updates for the focused client.
.TP
\fBwatch client\fR <id>
Stream updates for the client with the given ID.
.TP
\fBwatch tags\fR <monitor>
Stream tag updates for the named monitor.
.TP
\fBwatch all\-monitors\fR
Stream updates for all monitors.
.TP
\fBwatch all\-tags\fR
Stream updates for all tags.
.TP
\fBwatch all\-clients\fR
Stream updates for all clients.
.TP
\fBwatch keymode\fR
Stream keymode changes.
.TP
\fBwatch keyboardlayout\fR
Stream keyboard layout changes.
.TP
\fBwatch last_open_surface\fR [\fImonitor\fR]
Stream last open surface changes for a specific monitor (or the focused one).
.SH ENVIRONMENT
.TP
\fIMANGO_INSTANCE_SIGNATURE\fR
Path to the compositor's IPC socket. Set automatically by \fBmango\fR(1).
If unset, \fBmmsg\fR prints an error and exits.
.SH EXIT STATUS
.TP
\fB0\fR
Success (or help message printed).
.TP
\fBEXIT_FAILURE\fR
An error occurred (connection refused, send/receive error, etc.).
.SH SEE ALSO
\fBmango\fR(1)
.SH BUGS
Report issues at the project's issue tracker.

View file

@ -7,7 +7,73 @@
#include <sys/un.h>
#include <unistd.h>
static void usage(void) {
printf("Usage: mmsg <command> [args...]\n\n");
printf("One-shot queries (get):\n");
printf(
" get version Show compositor version\n");
printf(" get keymode Show current keymode\n");
printf(" get keyboardlayout Show current keyboard "
"layout\n");
printf(" get last_open_surface [monitor] Show last open surface "
"(default focused monitor)\n");
printf(" get monitor <name> Show monitor details\n");
printf(" get focusing-client Show focused client "
"details\n");
printf(" get client <id> Show client details by "
"ID\n");
printf(" get tag <monitor> <index> Show tag details "
"(1based index)\n");
printf(" get all-clients List all clients\n");
printf(" get all-monitors List all monitors\n");
printf(" get all-tags List all tags (all "
"monitors)\n");
printf(
" get tags <monitor> List tags for a monitor\n");
printf(" dispatch <func>[,arg...] [client,<id>] Call a compositor "
"function\n");
printf(" <func> and arguments are separated by commas.\n");
printf(" Add 'client,<id>' at the beginning or end to target a "
"specific client.\n");
printf(" Examples:\n");
printf(" dispatch togglefloating\n");
printf(" dispatch movewin,10,100\n");
printf(" dispatch movewin,10,100 client,4\n");
printf("Persistent streams (watch):\n");
printf(
" watch monitor <name> Stream monitor changes\n");
printf(" watch focusing-client Stream focused client "
"changes\n");
printf(
" watch client <id> Stream client changes\n");
printf(" watch tags <monitor> Stream tag changes for "
"a monitor\n");
printf(" watch all-monitors Stream all monitors "
"changes\n");
printf(
" watch all-tags Stream all tags changes\n");
printf(" watch all-clients Stream all clients "
"changes\n");
printf(
" watch keymode Stream keymode changes\n");
printf(" watch keyboardlayout Stream keyboard layout "
"changes\n");
printf(" watch last_open_surface [monitor] Stream last open "
"surface changes\n\n");
printf("Environment:\n");
printf(" MANGO_INSTANCE_SIGNATURE IPC socket path (set by the "
"compositor)\n\n");
printf("Run 'mmsg --help', '-h' or 'help' to see this message.\n");
}
int main(int argc, char *argv[]) {
if (argc >= 2 &&
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
strcmp(argv[1], "help") == 0)) {
usage();
return EXIT_SUCCESS;
}
if (argc < 2) {
fprintf(stderr, "Usage: mmsg <command> [args...]\n");
fprintf(stderr, " get <type> ... one-shot request\n");
@ -37,7 +103,6 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
// 拼接命令,缓冲区大小 4096 以容纳较长参数
char cmd[4096] = {0};
int offset = 0;
for (int i = 1; i < argc; i++) {
@ -51,7 +116,6 @@ int main(int argc, char *argv[]) {
offset += n;
}
// 添加换行符
int n = snprintf(cmd + offset, sizeof(cmd) - offset, "\n");
if (n < 0 || n >= (int)(sizeof(cmd) - offset)) {
fprintf(stderr, "Error: command too long to append newline.\n");
@ -59,14 +123,12 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
// 发送命令,使用 MSG_NOSIGNAL 避免 SIGPIPE
if (send(sock, cmd, strlen(cmd), MSG_NOSIGNAL) < 0) {
perror("send");
close(sock);
return EXIT_FAILURE;
}
// 将 socket 封装为行缓冲文件流,自动处理 TCP 拆包,按完整行读取
FILE *stream = fdopen(sock, "r");
if (!stream) {
perror("fdopen");
@ -74,7 +136,6 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
// 按行读取并输出直到连接关闭get 模式服务端主动 close或出错
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, stream) != -1) {
@ -82,11 +143,10 @@ int main(int argc, char *argv[]) {
fflush(stdout);
}
// 检查是否因读取错误退出(而非正常 EOF
if (ferror(stream)) {
perror("recv");
free(line);
fclose(stream); // 关闭 stream 同时关闭 socket
fclose(stream);
return EXIT_FAILURE;
}

View file

@ -5,6 +5,7 @@
libxcb,
libxkbcommon,
pcre2,
pango,
cjson,
pixman,
pkg-config,
@ -49,6 +50,7 @@ stdenv.mkDerivation {
libxcb
libxkbcommon
pcre2
pango
cjson
pixman
wayland

View file

@ -48,10 +48,13 @@ static void finish_exchange_arrange_and_focus(Client *c1, Client *c2,
}
wl_list_remove(&c2->flink);
wl_list_insert(&c1->flink, &c2->flink);
if (config.warpcursor)
warp_cursor(c1);
}
void client_tile_resize(Client *c, struct wlr_box geo, int32_t interact) {
if (!ISSCROLLTILED(c))
if (!ISFAKETILED(c))
return;
if (!c->isfullscreen && !c->ismaximizescreen) {
@ -87,3 +90,9 @@ void client_active(Client *c) {
view_in_mon(&(Arg){.ui = target}, true, c->mon, true);
focusclient(c, 1);
}
void client_pending_force_kill(Client *c) {
if (!c)
return;
kill(c->pid, SIGKILL);
}

View file

@ -305,7 +305,8 @@ void client_draw_shadow(Client *c) {
if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow)
return;
if (!config.shadows || (!c->isfloating && config.shadow_only_floating)) {
if (!config.shadows || c->isfullscreen ||
(!c->isfloating && config.shadow_only_floating)) {
if (c->shadow->node.enabled)
wlr_scene_node_set_enabled(&c->shadow->node, false);
return;
@ -405,7 +406,7 @@ void apply_split_border(Client *c, bool hit_no_border) {
const Layout *layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag];
if (hit_no_border || !ISTILED(c) || layout->id != DWINDLE ||
!config.dwindle_manual_split) {
!config.dwindle_manual_split || c->isfullscreen) {
if (c->splitindicator[0]->node.enabled) {
wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false);
}
@ -492,6 +493,18 @@ void apply_border(Client *c) {
if (!c || c->iskilling || !client_surface(c)->mapped)
return;
if (c->isfullscreen) {
if (c->border->node.enabled) {
wlr_scene_node_set_position(&c->scene_surface->node, 0, 0);
wlr_scene_node_set_enabled(&c->border->node, false);
}
return;
} else {
if (!c->border->node.enabled) {
wlr_scene_node_set_enabled(&c->border->node, true);
}
}
bool hit_no_border = check_hit_no_border(c);
apply_split_border(c, hit_no_border);
@ -686,17 +699,6 @@ void client_set_drop_area(Client *c) {
bool dwindle_familiar =
cur_layout->id == DWINDLE && config.dwindle_drop_simple_split;
uint32_t nmaster = c->mon->pertag->nmasters[c->mon->pertag->curtag];
bool should_swap =
(cur_layout->id == DECK || cur_layout->id == VERTICAL_DECK ||
cur_layout->id == MONOCLE || cur_layout->id == GRID ||
cur_layout->id == FAIR || cur_layout->id == VERTICAL_FAIR ||
cur_layout->id == VERTICAL_GRID) ||
((cur_layout->id == TILE || cur_layout->id == VERTICAL_TILE ||
cur_layout->id == CENTER_TILE || cur_layout->id == RIGHT_TILE) &&
nmaster == 1 && c->ismaster);
if (dwindle_familiar) {
bool split_h = c->geom.width >= c->geom.height;
float ratio = config.dwindle_split_ratio;
@ -730,29 +732,11 @@ void client_set_drop_area(Client *c) {
client_height - (int32_t)(client_height * ratio);
}
}
} else if (should_swap) {
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height;
drop_direction = UNDIR;
} else if (cur_layout->id == TILE || cur_layout->id == DECK ||
cur_layout->id == CENTER_TILE || cur_layout->id == RIGHT_TILE) {
if (rel_y < client_height * 0.5) {
drop_direction = UP;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height / 2;
} else {
drop_direction = DOWN;
drop_box.x = bw;
drop_box.y = bw + client_height / 2;
drop_box.width = client_width;
drop_box.height = client_height / 2;
}
} else if (cur_layout->id == VERTICAL_TILE ||
cur_layout->id == VERTICAL_DECK) {
if (c->ismaster) {
if (c->mon->visible_tiling_clients == 1) {
if (rel_x < client_width * 0.5) {
drop_direction = LEFT;
drop_box.x = bw;
@ -766,6 +750,69 @@ void client_set_drop_area(Client *c) {
drop_box.width = client_width / 2;
drop_box.height = client_height;
}
} else {
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height;
drop_direction = UNDIR;
}
} else {
if (rel_y < client_height * 0.5) {
drop_direction = UP;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height / 2;
} else {
drop_direction = DOWN;
drop_box.x = bw;
drop_box.y = bw + client_height / 2;
drop_box.width = client_width;
drop_box.height = client_height / 2;
}
}
} else if (cur_layout->id == VERTICAL_TILE ||
cur_layout->id == VERTICAL_DECK) {
if (c->ismaster) {
if (c->mon->visible_tiling_clients == 1) {
if (rel_y < client_height * 0.5) {
drop_direction = UP;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height / 2;
} else {
drop_direction = DOWN;
drop_box.x = bw;
drop_box.y = bw + client_height / 2;
drop_box.width = client_width;
drop_box.height = client_height / 2;
}
} else {
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height;
drop_direction = UNDIR;
}
} else {
if (rel_x < client_width * 0.5) {
drop_direction = LEFT;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width / 2;
drop_box.height = client_height;
} else {
drop_direction = RIGHT;
drop_box.x = bw + client_width / 2;
drop_box.y = bw;
drop_box.width = client_width / 2;
drop_box.height = client_height;
}
}
} else {
double dist_left = rel_x;
double dist_right = client_width - rel_x;

View file

@ -117,6 +117,9 @@ void set_arrange_hidden(Monitor *m, Client *c, bool want_animation) {
c->animation.tagining = false;
set_tagout_animation(m, c);
} else {
c->animation.running = false;
wlr_scene_node_set_enabled(&c->scene->node, false);
c->animainit_geom = c->current = c->pending = c->animation.current =
c->geom;
}
}

View file

@ -167,6 +167,9 @@ typedef struct {
char *monitor_serial;
float mfact;
int32_t nmaster;
float scroller_default_proportion;
float scroller_default_proportion_single;
int32_t scroller_ignore_proportion_single;
int32_t no_render_border;
int32_t open_as_floating;
int32_t no_hide;
@ -258,9 +261,11 @@ typedef struct {
int32_t enable_hotarea;
int32_t ov_tab_mode;
int32_t ov_no_resize;
int32_t overviewgappi;
int32_t overviewgappo;
uint32_t cursor_hide_timeout;
uint32_t cursor_hide_on_keypress;
uint32_t axis_bind_apply_timeout;
uint32_t focus_on_activate;
@ -403,6 +408,7 @@ typedef struct {
struct xkb_context *ctx;
struct xkb_keymap *keymap;
JumphitData jumhitdata;
} Config;
typedef int32_t (*FuncType)(const Arg *);
@ -559,6 +565,26 @@ int32_t parse_direction(const char *str) {
}
}
int32_t parse_force(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, "unforce") == 0) {
return UNFORCE;
} else if (strcmp(lowerStr, "force") == 0) {
return FORCE;
} else {
return UNFORCE;
}
}
int32_t parse_fold_state(const char *str) {
// 将输入字符串转换为小写
char lowerStr[10];
@ -1006,6 +1032,9 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
} else if (strcmp(func_name, "toggleoverview") == 0) {
func = toggleoverview;
(*arg).i = atoi(arg_value);
} else if (strcmp(func_name, "togglejump") == 0) {
func = togglejump;
(*arg).i = atoi(arg_value);
} else if (strcmp(func_name, "set_proportion") == 0) {
func = set_proportion;
(*arg).f = atof(arg_value);
@ -1029,6 +1058,7 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
(*arg).i = atoi(arg_value);
} else if (strcmp(func_name, "killclient") == 0) {
func = killclient;
(*arg).i = parse_force(arg_value);
} else if (strcmp(func_name, "centerwin") == 0) {
func = centerwin;
} else if (strcmp(func_name, "focuslast") == 0) {
@ -1693,6 +1723,8 @@ bool parse_option(Config *config, char *key, char *value) {
config->overviewgappo = atoi(value);
} else if (strcmp(key, "cursor_hide_timeout") == 0) {
config->cursor_hide_timeout = atoi(value);
} else if (strcmp(key, "cursor_hide_on_keypress") == 0) {
config->cursor_hide_on_keypress = atoi(value);
} else if (strcmp(key, "axis_bind_apply_timeout") == 0) {
config->axis_bind_apply_timeout = atoi(value);
} else if (strcmp(key, "focus_on_activate") == 0) {
@ -1731,6 +1763,50 @@ bool parse_option(Config *config, char *key, char *value) {
config->cursor_size = atoi(value);
} else if (strcmp(key, "cursor_theme") == 0) {
config->cursor_theme = strdup(value);
} else if (strcmp(key, "jump_hit_fg_color") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_fg_color "
"format: %s\n",
value);
return false;
} else {
convert_hex_to_rgba(config->jumhitdata.fg_color, color);
}
} else if (strcmp(key, "jump_hit_font_desc") == 0) {
config->jumhitdata.font_desc = strdup(value);
} else if (strcmp(key, "jump_hit_bg_color") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_bg_color "
"format: %s\n",
value);
return false;
} else {
convert_hex_to_rgba(config->jumhitdata.bg_color, color);
}
} else if (strcmp(key, "jump_hit_border_color") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
fprintf(
stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_border_color "
"format: %s\n",
value);
return false;
} else {
convert_hex_to_rgba(config->jumhitdata.border_color, color);
}
} else if (strcmp(key, "jump_hit_border_width") == 0) {
config->jumhitdata.border_width = CLAMP_INT(atoi(value), 0, 100);
} else if (strcmp(key, "jump_hit_corner_radius") == 0) {
config->jumhitdata.corner_radius = CLAMP_INT(atoi(value), 0, 100);
} else if (strcmp(key, "jump_hit_padding_x") == 0) {
config->jumhitdata.padding_x = CLAMP_INT(atoi(value), 0, 100);
} else if (strcmp(key, "jump_hit_padding_y") == 0) {
config->jumhitdata.padding_y = CLAMP_INT(atoi(value), 0, 100);
} else if (strcmp(key, "disable_while_typing") == 0) {
config->disable_while_typing = atoi(value);
} else if (strcmp(key, "left_handed") == 0) {
@ -2017,6 +2093,9 @@ bool parse_option(Config *config, char *key, char *value) {
rule->no_render_border = 0;
rule->open_as_floating = 0;
rule->no_hide = 0;
rule->scroller_default_proportion = 0.0f;
rule->scroller_default_proportion_single = 0.0f;
rule->scroller_ignore_proportion_single = -1;
bool parse_error = false;
char *token = strtok(value, ",");
@ -2052,6 +2131,17 @@ bool parse_option(Config *config, char *key, char *value) {
rule->nmaster = CLAMP_INT(atoi(val), 1, 99);
} else if (strcmp(key, "mfact") == 0) {
rule->mfact = CLAMP_FLOAT(atof(val), 0.1f, 0.9f);
} else if (strcmp(key, "scroller_default_proportion") == 0) {
rule->scroller_default_proportion =
CLAMP_FLOAT(atof(val), 0.0f, 1.0f);
} else if (strcmp(key, "scroller_default_proportion_single") ==
0) {
rule->scroller_default_proportion_single =
CLAMP_FLOAT(atof(val), 0.0f, 1.0f);
} else if (strcmp(key, "scroller_ignore_proportion_single") ==
0) {
rule->scroller_ignore_proportion_single =
CLAMP_INT(atoi(val), 0, 1);
} else {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Unknown "
@ -3206,6 +3296,11 @@ void free_config(void) {
config.cursor_theme = NULL;
}
if (config.jumhitdata.font_desc) {
free((void *)config.jumhitdata.font_desc);
config.jumhitdata.font_desc = NULL;
}
if (config.tablet_map_to_mon) {
free(config.tablet_map_to_mon);
config.tablet_map_to_mon = NULL;
@ -3325,6 +3420,8 @@ void override_config(void) {
CLAMP_INT(config.no_radius_when_single, 0, 1);
config.cursor_hide_timeout =
CLAMP_INT(config.cursor_hide_timeout, 0, 36000);
config.cursor_hide_on_keypress =
CLAMP_INT(config.cursor_hide_on_keypress, 0, 1);
config.single_scratchpad = CLAMP_INT(config.single_scratchpad, 0, 1);
config.repeat_rate = CLAMP_INT(config.repeat_rate, 1, 1000);
config.repeat_delay = CLAMP_INT(config.repeat_delay, 1, 20000);
@ -3394,6 +3491,15 @@ void override_config(void) {
config.focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f);
config.unfocused_opacity =
CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f);
config.jumhitdata.border_width =
CLAMP_INT(config.jumhitdata.border_width, 0, 100);
config.jumhitdata.corner_radius =
CLAMP_INT(config.jumhitdata.corner_radius, 0, 100);
config.jumhitdata.padding_x =
CLAMP_INT(config.jumhitdata.padding_x, 0, 100);
config.jumhitdata.padding_y =
CLAMP_INT(config.jumhitdata.padding_y, 0, 100);
}
void set_value_default() {
@ -3432,7 +3538,6 @@ void set_value_default() {
config.log_level = WLR_ERROR;
config.numlockon = 0;
config.capslock = 0;
config.ov_tab_mode = 1;
config.ov_no_resize = 1;
config.hotarea_size = 10;
@ -3486,6 +3591,7 @@ void set_value_default() {
config.overviewgappi = 5;
config.overviewgappo = 30;
config.cursor_hide_timeout = 0;
config.cursor_hide_on_keypress = 0;
config.warpcursor = 1;
config.drag_corner = 3;
@ -3569,6 +3675,23 @@ void set_value_default() {
config.animation_curve_opafadeout[2] = 0.5;
config.animation_curve_opafadeout[3] = 0.5;
config.jumhitdata.fg_color[0] = 0xc4 / 255.0f;
config.jumhitdata.fg_color[1] = 0x93 / 255.0f;
config.jumhitdata.fg_color[2] = 0x9d / 255.0f;
config.jumhitdata.fg_color[3] = 1.0f;
config.jumhitdata.bg_color[0] = 0x32 / 255.0f;
config.jumhitdata.bg_color[1] = 0x32 / 255.0f;
config.jumhitdata.bg_color[2] = 0x32 / 255.0f;
config.jumhitdata.bg_color[3] = 1.0f;
config.jumhitdata.border_color[0] = 0x8b / 255.0f;
config.jumhitdata.border_color[1] = 0xaa / 255.0f;
config.jumhitdata.border_color[2] = 0x9b / 255.0f;
config.jumhitdata.border_color[3] = 1.0f;
config.jumhitdata.border_width = 4;
config.jumhitdata.corner_radius = 5;
config.jumhitdata.padding_x = 10;
config.jumhitdata.padding_y = 10;
config.rootcolor[0] = 0x32 / 255.0f;
config.rootcolor[1] = 0x32 / 255.0f;
config.rootcolor[2] = 0x32 / 255.0f;
@ -3681,6 +3804,7 @@ bool parse_config(void) {
config.tag_rules = NULL;
config.tag_rules_count = 0;
config.cursor_theme = NULL;
config.jumhitdata.font_desc = NULL;
config.tablet_map_to_mon = NULL;
strcpy(config.keymode, "default");
@ -3907,6 +4031,12 @@ void parse_tagrule(Monitor *m) {
for (i = 0; i <= LENGTH(tags); i++) {
m->pertag->nmasters[i] = config.default_nmaster;
m->pertag->mfacts[i] = config.default_mfact;
m->pertag->scroller_default_proportion[i] =
config.scroller_default_proportion;
m->pertag->scroller_default_proportion_single[i] =
config.scroller_default_proportion_single;
m->pertag->scroller_ignore_proportion_single[i] =
config.scroller_ignore_proportion_single;
}
for (i = 0; i < config.tag_rules_count; i++) {
@ -3961,6 +4091,15 @@ void parse_tagrule(Monitor *m) {
m->pertag->no_render_border[tr.id] = tr.no_render_border;
if (tr.open_as_floating >= 0)
m->pertag->open_as_floating[tr.id] = tr.open_as_floating;
if (tr.scroller_default_proportion > 0.0f)
m->pertag->scroller_default_proportion[tr.id] =
tr.scroller_default_proportion;
if (tr.scroller_default_proportion_single > 0.0f)
m->pertag->scroller_default_proportion_single[tr.id] =
tr.scroller_default_proportion_single;
if (tr.scroller_ignore_proportion_single >= 0)
m->pertag->scroller_ignore_proportion_single[tr.id] =
tr.scroller_ignore_proportion_single;
}
}

View file

@ -3,6 +3,7 @@ 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 togglejump(const Arg *arg);
int32_t set_proportion(const Arg *arg);
int32_t switch_proportion_preset(const Arg *arg);
int32_t zoom(const Arg *arg);

View file

@ -130,8 +130,13 @@ int32_t exchange_stack_client(const Arg *arg) {
}
int32_t focusdir(const Arg *arg) {
if (!selmon)
return 0;
Client *c = NULL;
c = direction_select(arg);
if (!selmon->isoverview)
c = get_focused_stack_client(c, arg->tc);
if (c) {
focusclient(c, 1);
@ -345,8 +350,12 @@ int32_t setmfact(const Arg *arg) {
int32_t killclient(const Arg *arg) {
Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL);
if (c) {
if (arg->i == FORCE) {
client_pending_force_kill(c);
} else {
pending_kill_client(c);
}
}
return 0;
}
@ -1351,9 +1360,9 @@ int32_t togglefullscreen(const Arg *arg) {
sel->isnamedscratchpad = 0;
if (sel->isfullscreen)
setfullscreen(sel, 0);
setfullscreen(sel, 0, true);
else
setfullscreen(sel, 1);
setfullscreen(sel, 1, true);
return 0;
}
@ -1397,9 +1406,9 @@ int32_t togglemaximizescreen(const Arg *arg) {
sel->isnamedscratchpad = 0;
if (sel->ismaximizescreen)
setmaximizescreen(sel, 0);
setmaximizescreen(sel, 0, true);
else
setmaximizescreen(sel, 1);
setmaximizescreen(sel, 1, true);
setborder_color(sel);
return 0;
@ -1728,6 +1737,10 @@ int32_t toggleoverview(const Arg *arg) {
uint32_t target;
uint32_t visible_client_number = 0;
if (!selmon->isoverview && selmon->is_jump_mode) {
finish_jump_mode(selmon);
}
if (selmon->isoverview) {
wl_list_for_each(c, &clients, link) if (c && c->mon == selmon &&
!client_is_unmanaged(c) &&
@ -1756,7 +1769,12 @@ int32_t toggleoverview(const Arg *arg) {
if (selmon->isoverview) {
wlr_seat_pointer_clear_focus(seat);
if (cursor_hidden) {
handlecursoractivity();
} else {
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
}
wl_list_for_each(c, &clients, link) {
if (c && c->mon == selmon && !client_is_unmanaged(c) &&
@ -1767,6 +1785,7 @@ int32_t toggleoverview(const Arg *arg) {
}
}
} else {
selmon->tagset[selmon->seltags] = target;
wl_list_for_each(c, &clients, link) {
if (c && c->mon == selmon && !c->iskilling &&
@ -1780,6 +1799,24 @@ int32_t toggleoverview(const Arg *arg) {
view(&(Arg){.ui = target}, false);
fix_mon_tagset_from_overview(selmon);
refresh_monitors_workspaces_status(selmon);
return 0;
}
int32_t togglejump(const Arg *arg) {
if (!selmon)
return 0;
if (!selmon->isoverview) {
begin_jump_mode(selmon);
toggleoverview(arg);
return 0;
}
if (selmon->isoverview) {
toggleoverview(arg);
}
return 0;
}
@ -1909,7 +1946,7 @@ int32_t scroller_stack(const Arg *arg) {
if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon))
return 0;
Client *target_client = find_client_by_direction(c, arg, false, true);
Client *target_client = find_client_by_direction(c, arg, false);
return scroller_apply_stack(c, target_client, arg->i);
}

308
src/draw/text-node.c Normal file
View file

@ -0,0 +1,308 @@
#include "text-node.h"
#include <cairo.h>
#include <drm_fourcc.h>
#include <glib.h>
#include <pango/pangocairo.h>
#include <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/interfaces/wlr_buffer.h>
// 自定义 wlr_buffer用于将 Cairo Surface 包装并注入 wlr_scene
struct mango_text_buffer {
struct wlr_buffer base;
cairo_surface_t *surface;
};
// 全局字体描述缓存
static GHashTable *font_desc_cache = NULL;
static PangoFontDescription *get_cached_font_desc(const char *font_desc) {
if (!font_desc_cache) {
font_desc_cache =
g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify)pango_font_description_free);
}
PangoFontDescription *desc =
g_hash_table_lookup(font_desc_cache, font_desc);
if (!desc) {
desc = pango_font_description_from_string(font_desc);
g_hash_table_insert(font_desc_cache, g_strdup(font_desc), desc);
}
return desc;
}
void mango_text_global_finish(void) {
if (font_desc_cache) {
g_hash_table_destroy(font_desc_cache);
font_desc_cache = NULL;
}
}
// wlr_buffer 实现
static void text_buffer_destroy(struct wlr_buffer *wlr_buffer) {
struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base);
cairo_surface_destroy(buf->surface);
free(buf);
}
static bool text_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer,
uint32_t flags, void **data,
uint32_t *format,
size_t *stride) {
(void)flags;
struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base);
*data = cairo_image_surface_get_data(buf->surface);
*format = DRM_FORMAT_ARGB8888;
*stride = cairo_image_surface_get_stride(buf->surface);
return true;
}
static void text_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {}
static const struct wlr_buffer_impl text_buffer_impl = {
.destroy = text_buffer_destroy,
.begin_data_ptr_access = text_buffer_begin_data_ptr_access,
.end_data_ptr_access = text_buffer_end_data_ptr_access,
};
struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent,
JumphitData data) {
struct mango_text_node *node = calloc(1, sizeof(*node));
if (!node)
return NULL;
node->scene_buffer = wlr_scene_buffer_create(parent, NULL);
if (!node->scene_buffer) {
free(node);
return NULL;
}
node->fg_color[0] = data.fg_color[0];
node->fg_color[1] = data.fg_color[1];
node->fg_color[2] = data.fg_color[2];
node->fg_color[3] = data.fg_color[3];
node->bg_color[0] = data.bg_color[0];
node->bg_color[1] = data.bg_color[1];
node->bg_color[2] = data.bg_color[2];
node->bg_color[3] = data.bg_color[3];
node->border_color[0] = data.border_color[0];
node->border_color[1] = data.border_color[1];
node->border_color[2] = data.border_color[2];
node->border_color[3] = data.border_color[3];
node->border_width = data.border_width;
node->corner_radius = data.corner_radius;
node->padding_x = data.padding_x;
node->padding_y = data.padding_y;
node->font_desc = data.font_desc ? data.font_desc : "monospace Bold 24";
return node;
}
void mango_text_node_destroy(struct mango_text_node *node) {
if (!node)
return;
wlr_scene_node_destroy(&node->scene_buffer->node);
free(node);
}
void mango_text_node_set_background(struct mango_text_node *node, float r,
float g, float b, float a) {
if (!node)
return;
node->bg_color[0] = r;
node->bg_color[1] = g;
node->bg_color[2] = b;
node->bg_color[3] = a;
}
void mango_text_node_set_border(struct mango_text_node *node, float r, float g,
float b, float a, float width, float radius) {
if (!node)
return;
node->border_color[0] = r;
node->border_color[1] = g;
node->border_color[2] = b;
node->border_color[3] = a;
node->border_width = width > 0.0f ? width : 0.0f;
node->corner_radius = radius >= 0.0f ? radius : 0.0f;
}
void mango_text_node_set_padding(struct mango_text_node *node, float pad_x,
float pad_y) {
if (!node)
return;
node->padding_x = pad_x >= 0.0f ? pad_x : 0.0f;
node->padding_y = pad_y >= 0.0f ? pad_y : 0.0f;
}
static void draw_rounded_rect(cairo_t *cr, double x, double y, double w,
double h, double r) {
// 使用 Cairo 的标准圆角矩形路径
double degrees = G_PI / 180.0;
cairo_new_sub_path(cr);
cairo_arc(cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees);
cairo_arc(cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees);
cairo_arc(cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees);
cairo_arc(cr, x + r, y + r, r, 180 * degrees, 270 * degrees);
cairo_close_path(cr);
}
void mango_text_node_update(struct mango_text_node *node, const char *text,
float scale) {
if (!node || !text)
return;
if (scale <= 0.0f)
scale = 1.0f;
const char *font_desc = node->font_desc;
PangoFontDescription *desc = get_cached_font_desc(font_desc);
// 测量文字像素尺寸(无缩放的实际像素)
cairo_surface_t *dummy_surface =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
cairo_t *dummy_cr = cairo_create(dummy_surface);
PangoContext *dummy_ctx = pango_cairo_create_context(dummy_cr);
pango_cairo_context_set_resolution(dummy_ctx, 96.0 * scale);
PangoLayout *dummy_layout = pango_layout_new(dummy_ctx);
pango_layout_set_font_description(dummy_layout, desc);
pango_layout_set_text(dummy_layout, text, -1);
int text_pixel_w, text_pixel_h;
pango_layout_get_pixel_size(dummy_layout, &text_pixel_w, &text_pixel_h);
g_object_unref(dummy_layout);
g_object_unref(dummy_ctx);
cairo_destroy(dummy_cr);
cairo_surface_destroy(dummy_surface);
if (text_pixel_w <= 0 || text_pixel_h <= 0) {
wlr_scene_buffer_set_buffer(node->scene_buffer, NULL);
node->logical_width = 0;
node->logical_height = 0;
return;
}
float logical_text_w = text_pixel_w / scale;
float logical_text_h = text_pixel_h / scale;
float pad_x = node->padding_x;
float pad_y = node->padding_y;
float box_logical_w = logical_text_w + 2.0f * pad_x;
float box_logical_h = logical_text_h + 2.0f * pad_y;
float border = node->border_width;
bool draw_border = (border > 0.0f) && (node->border_color[3] > 0.0f);
bool draw_bg = (node->bg_color[3] > 0.0f);
int surface_pixel_w = (int)((box_logical_w + 2.0f * border) * scale + 0.5f);
int surface_pixel_h = (int)((box_logical_h + 2.0f * border) * scale + 0.5f);
if (surface_pixel_w < 1)
surface_pixel_w = 1;
if (surface_pixel_h < 1)
surface_pixel_h = 1;
cairo_surface_t *surface = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, surface_pixel_w, surface_pixel_h);
cairo_t *cr = cairo_create(surface);
// 清空为全透明
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
double bg_x = border * scale;
double bg_y = border * scale;
double bg_w = box_logical_w * scale;
double bg_h = box_logical_h * scale;
double radius = node->corner_radius * scale;
if (radius < 0) {
radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0;
}
if (radius > bg_w / 2.0)
radius = bg_w / 2.0;
if (radius > bg_h / 2.0)
radius = bg_h / 2.0;
if (draw_bg) {
cairo_set_source_rgba(cr, node->bg_color[0], node->bg_color[1],
node->bg_color[2], node->bg_color[3]);
if (radius > 0.0) {
draw_rounded_rect(cr, bg_x, bg_y, bg_w, bg_h, radius);
cairo_fill(cr);
} else {
cairo_rectangle(cr, bg_x, bg_y, bg_w, bg_h);
cairo_fill(cr);
}
}
cairo_save(cr);
cairo_translate(cr, (border + pad_x) * scale, (border + pad_y) * scale);
PangoContext *ctx = pango_cairo_create_context(cr);
pango_cairo_context_set_resolution(ctx, 96.0 * scale);
PangoLayout *layout = pango_layout_new(ctx);
pango_layout_set_font_description(layout, desc);
pango_layout_set_text(layout, text, -1);
cairo_set_source_rgba(cr, node->fg_color[0], node->fg_color[1],
node->fg_color[2], node->fg_color[3]);
pango_cairo_show_layout(cr, layout);
g_object_unref(layout);
g_object_unref(ctx);
cairo_restore(cr);
if (draw_border) {
cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1],
node->border_color[2], node->border_color[3]);
cairo_set_line_width(cr, border * scale);
double half_lw = border * scale * 0.5;
double bx = bg_x - half_lw;
double by = bg_y - half_lw;
double bw = bg_w + border * scale;
double bh = bg_h + border * scale;
if (radius > 0.0) {
double outer_radius = radius + half_lw;
if (outer_radius < 0.0)
outer_radius = 0.0;
draw_rounded_rect(cr, bx, by, bw, bh, outer_radius);
cairo_stroke(cr);
} else {
cairo_rectangle(cr, bx, by, bw, bh);
cairo_stroke(cr);
}
}
cairo_surface_flush(surface);
// 包装成 wlr_buffer
struct mango_text_buffer *buf = calloc(1, sizeof(*buf));
if (!buf) {
cairo_surface_destroy(surface);
cairo_destroy(cr);
return;
}
wlr_buffer_init(&buf->base, &text_buffer_impl, surface_pixel_w,
surface_pixel_h);
buf->surface = surface;
wlr_scene_buffer_set_buffer(node->scene_buffer, &buf->base);
wlr_buffer_drop(&buf->base);
// 最终逻辑大小 = 背景框逻辑尺寸 + 边框逻辑尺寸
node->logical_width = (int)(box_logical_w + 2.0f * border);
node->logical_height = (int)(box_logical_h + 2.0f * border);
wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width,
node->logical_height);
cairo_destroy(cr);
}

52
src/draw/text-node.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef MANGO_TEXT_NODE_H
#define MANGO_TEXT_NODE_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_scene.h>
struct mango_text_node {
struct wlr_scene_buffer *scene_buffer;
int logical_width;
int logical_height;
const char *font_desc;
// 外观属性
float fg_color[4]; // 文本颜色 RGBA默认白色
float bg_color[4]; // 背景色 RGBA默认透明
float border_color[4]; // 边框色 RGBA默认透明
int32_t border_width; // 边框宽度(逻辑像素),<=0 则不绘制
int32_t corner_radius; // 圆角半径(逻辑像素),<0 时自动取
// min(width,height)/2 形成胶囊
int32_t padding_x; // 文本左右内边距(逻辑像素)
int32_t padding_y; // 文本上下内边距(逻辑像素)
};
typedef struct {
float fg_color[4];
float bg_color[4];
float border_color[4];
int32_t border_width;
int32_t corner_radius;
int32_t padding_x;
int32_t padding_y;
const char *font_desc;
} JumphitData;
struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent,
JumphitData data);
void mango_text_node_destroy(struct mango_text_node *node);
void mango_text_node_update(struct mango_text_node *node, const char *text,
float scale);
// 外观设置接口
void mango_text_node_set_background(struct mango_text_node *node, float r,
float g, float b, float a);
void mango_text_node_set_border(struct mango_text_node *node, float r, float g,
float b, float a, float width, float radius);
void mango_text_node_set_padding(struct mango_text_node *node, float pad_x,
float pad_y);
void mango_text_node_destroy(struct mango_text_node *node);
void mango_text_global_finish(void);
#endif

View file

@ -188,10 +188,10 @@ void refresh_monitors_workspaces_status(Monitor *m) {
int32_t i;
if (m->isoverview) {
add_workspace_by_tag(0, m);
for (i = 1; i <= LENGTH(tags); i++) {
remove_workspace_by_tag(i, m);
}
add_workspace_by_tag(0, m);
} else {
remove_workspace_by_tag(0, m);
for (i = 1; i <= LENGTH(tags); i++) {

View file

@ -16,12 +16,12 @@ void handle_foreign_maximize_request(struct wl_listener *listener, void *data) {
return;
if (c->ismaximizescreen && !event->maximized) {
setmaximizescreen(c, 0);
setmaximizescreen(c, 0, true);
return;
}
if (!c->ismaximizescreen && event->maximized) {
setmaximizescreen(c, 1);
setmaximizescreen(c, 1, true);
return;
}
}
@ -59,12 +59,12 @@ void handle_foreign_fullscreen_request(struct wl_listener *listener,
return;
if (c->isfullscreen && !event->fullscreen) {
setfullscreen(c, 0);
setfullscreen(c, 0, true);
return;
}
if (!c->isfullscreen && event->fullscreen) {
setfullscreen(c, 1);
setfullscreen(c, 1, true);
return;
}
}

View file

@ -22,6 +22,7 @@ static struct wlr_tablet_manager_v2 *tablet_mgr;
struct Tablet {
struct wlr_tablet_v2_tablet *tablet_v2;
struct wl_listener destroy;
struct wlr_input_device *device;
struct wl_list link;
};
static struct wl_list tablets;
@ -38,6 +39,7 @@ struct TabletTool {
struct TabletPad {
struct wlr_tablet_v2_tablet_pad *pad_v2;
struct wlr_input_device *device;
struct Tablet *tablet;
struct wl_listener tablet_destroy;
struct wl_listener attach;
@ -72,11 +74,18 @@ void createtablet(struct wlr_input_device *device) {
return;
}
tablet->device = device;
tablet->tablet_v2 = wlr_tablet_create(tablet_mgr, seat, device);
if (!tablet->tablet_v2) {
free(tablet);
return;
}
tablet->tablet_v2->wlr_tablet->data = tablet;
tablet->destroy.notify = destroytablet;
wl_signal_add(&tablet->tablet_v2->wlr_device->events.destroy,
&tablet->destroy);
wl_signal_add(&tablet->device->events.destroy, &tablet->destroy);
if (libinput_device_config_send_events_get_modes(device_handle)) {
libinput_device_config_send_events_set_mode(device_handle,
config.send_events_mode);
@ -90,7 +99,7 @@ void createtablet(struct wlr_input_device *device) {
wlr_libinput_get_device_handle(device));
struct TabletPad *tablet_pad;
wl_list_for_each(tablet_pad, &tablet_pads, link) {
struct wlr_input_device *pad_device = tablet_pad->pad_v2->wlr_device;
struct wlr_input_device *pad_device = tablet_pad->device;
if (!wlr_input_device_is_libinput(pad_device)) {
continue;
}
@ -129,8 +138,7 @@ void attach_tablet_pad(struct TabletPad *tablet_pad, struct Tablet *tablet) {
wl_list_remove(&tablet_pad->tablet_destroy.link);
tablet_pad->tablet_destroy.notify = tabletpadtabletdestroy;
wl_signal_add(&tablet->tablet_v2->wlr_device->events.destroy,
&tablet_pad->tablet_destroy);
wl_signal_add(&tablet->device->events.destroy, &tablet_pad->tablet_destroy);
}
void tabletpadattach(struct wl_listener *listener, void *data) {
@ -152,27 +160,38 @@ void createtabletpad(struct wlr_input_device *device) {
wlr_log(WLR_ERROR, "could not allocate tablet_pad");
return;
}
tablet_pad->device = device;
tablet_pad->pad_v2 = wlr_tablet_pad_create(tablet_mgr, seat, device);
if (!tablet_pad->pad_v2) {
wlr_log(WLR_ERROR, "could not create tablet_pad_v2 wrapper");
free(tablet_pad);
return;
}
tablet_pad->destroy.notify = destroytabletpad;
tablet_pad->attach.notify = tabletpadattach;
wl_list_init(&tablet_pad->tablet_destroy.link);
wl_signal_add(&tablet_pad->pad_v2->wlr_device->events.destroy,
&tablet_pad->destroy);
wl_signal_add(&device->events.destroy, &tablet_pad->destroy);
wl_signal_add(&tablet_pad->pad_v2->wlr_pad->events.attach_tablet,
&tablet_pad->attach);
wl_list_insert(&tablet_pads, &tablet_pad->link);
/* Search for a sibling tablet */
if (!wlr_input_device_is_libinput(tablet_pad->pad_v2->wlr_device)) {
if (!wlr_input_device_is_libinput(device)) {
/* We can only do this on libinput devices */
return;
}
struct libinput_device_group *group = libinput_device_get_device_group(
wlr_libinput_get_device_handle(tablet_pad->pad_v2->wlr_device));
wlr_libinput_get_device_handle(device));
struct Tablet *tablet;
wl_list_for_each(tablet, &tablets, link) {
struct wlr_input_device *tablet_device = tablet->tablet_v2->wlr_device;
struct wlr_input_device *tablet_device = tablet->device;
if (!wlr_input_device_is_libinput(tablet_device)) {
continue;
}
@ -249,11 +268,11 @@ void tablettoolmotion(struct TabletTool *tool, bool change_x, bool change_y,
switch (tool->tool_v2->wlr_tool->type) {
case WLR_TABLET_TOOL_TYPE_LENS:
case WLR_TABLET_TOOL_TYPE_MOUSE:
wlr_cursor_move(cursor, tablet->tablet_v2->wlr_device, dx, dy);
wlr_cursor_move(cursor, tablet->device, dx, dy);
break;
default:
wlr_cursor_warp_absolute(cursor, tablet->tablet_v2->wlr_device,
change_x ? x : NAN, change_y ? y : NAN);
wlr_cursor_warp_absolute(cursor, tablet->device, change_x ? x : NAN,
change_y ? y : NAN);
break;
}

View file

@ -91,9 +91,9 @@ setclient_coordinate_center(Client *c, Monitor *tm, struct wlr_box geom,
if (!m)
return geom;
uint32_t cbw = check_hit_no_border(c) ? c->bw : 0;
uint32_t cbw = c && check_hit_no_border(c) ? c->bw : 0;
if (!c->no_force_center && m) {
if ((!c || !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 {
@ -163,291 +163,130 @@ Client *center_tiled_select(Monitor *m) {
}
return target_c;
}
Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
bool ignore_align) {
Client *find_client_by_direction(Client *tc, const Arg *arg,
bool findfloating) {
Client *c = NULL;
Client **tempClients = NULL; // 初始化为 NULL
int32_t last = -1;
// 第一次遍历,计算客户端数量
wl_list_for_each(c, &clients, link) {
if (c && (findfloating || !c->isfloating) && !c->isunglobal &&
(config.focus_cross_monitor || c->mon == tc->mon) &&
(c->tags & c->mon->tagset[c->mon->seltags])) {
last++;
}
}
if (last < 0) {
return NULL; // 没有符合条件的客户端
}
// 动态分配内存
tempClients = malloc((last + 1) * sizeof(Client *));
if (!tempClients) {
// 处理内存分配失败的情况
return NULL;
}
// 第二次遍历,填充 tempClients
last = -1;
wl_list_for_each(c, &clients, link) {
if (c && (findfloating || !c->isfloating) && !c->isunglobal &&
(config.focus_cross_monitor || c->mon == tc->mon) &&
(c->tags & c->mon->tagset[c->mon->seltags])) {
last++;
tempClients[last] = c;
}
}
int32_t sel_x = tc->geom.x;
int32_t sel_y = tc->geom.y;
int64_t distance = LLONG_MAX;
int64_t same_monitor_distance = LLONG_MAX;
Client *tempFocusClients = NULL;
Client *tempSameMonitorFocusClients = NULL;
int64_t distance = LLONG_MAX;
int64_t same_monitor_distance = LLONG_MAX;
int32_t tc_l = tc->geom.x;
int32_t tc_r = tc->geom.x + tc->geom.width;
int32_t tc_t = tc->geom.y;
int32_t tc_b = tc->geom.y + tc->geom.height;
int32_t tc_cx = tc_l + tc->geom.width / 2;
int32_t tc_cy = tc_t + tc->geom.height / 2;
for (int32_t step = 0; step < 2; step++) {
if (step == 1 && tempFocusClients)
break;
wl_list_for_each(c, &clients, link) {
if (!c || c == tc)
continue;
if (!findfloating && c->isfloating)
continue;
if (c->isunglobal)
continue;
if (!config.focus_cross_monitor && c->mon != tc->mon)
continue;
if (!(c->tags & c->mon->tagset[c->mon->seltags]))
continue;
if (step == 0 && ((!tc->mon->isoverview &&
!client_is_in_same_stack(tc, c, NULL)) ||
c->mon != tc->mon))
continue;
int32_t c_l = c->geom.x;
int32_t c_r = c->geom.x + c->geom.width;
int32_t c_t = c->geom.y;
int32_t c_b = c->geom.y + c->geom.height;
int32_t c_cx = c_l + c->geom.width / 2;
int32_t c_cy = c_t + c->geom.height / 2;
int64_t main_dist = 0;
int64_t orth_dist = 0;
bool match_dir = false;
switch (arg->i) {
case UP:
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;
tempFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
break;
case DOWN:
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;
tempFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
break;
case LEFT:
if (!ignore_align) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x &&
tempClients[_i]->geom.y == sel_y &&
tempClients[_i]->mon == tc->mon) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
if (c_cx < tc_cx || (c_cx == tc_cx && c_l < tc_l)) {
match_dir = true;
main_dist = tc_l - c_r;
orth_dist = (c_b < tc_t)
? (tc_t - c_b)
: ((c_t > tc_b) ? (c_t - tc_b) : 0);
}
break;
case RIGHT:
if (!ignore_align) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x &&
tempClients[_i]->geom.y == sel_y &&
tempClients[_i]->mon == tc->mon) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
if (c_cx > tc_cx || (c_cx == tc_cx && c_l > tc_l)) {
match_dir = true;
main_dist = c_l - tc_r;
orth_dist = (c_b < tc_t)
? (tc_t - c_b)
: ((c_t > tc_b) ? (c_t - tc_b) : 0);
}
break;
case UP:
if (c_cy < tc_cy || (c_cy == tc_cy && c_t < tc_t)) {
match_dir = true;
main_dist = tc_t - c_b;
orth_dist = (c_r < tc_l)
? (tc_l - c_r)
: ((c_l > tc_r) ? (c_l - tc_r) : 0);
}
break;
case DOWN:
if (c_cy > tc_cy || (c_cy == tc_cy && c_t > tc_t)) {
match_dir = true;
main_dist = c_t - tc_b;
orth_dist = (c_r < tc_l)
? (tc_l - c_r)
: ((c_l > tc_r) ? (c_l - tc_r) : 0);
}
break;
default:
continue;
}
free(tempClients); // 释放内存
if (tempSameMonitorFocusClients) {
return tempSameMonitorFocusClients;
} else {
return tempFocusClients;
if (!match_dir)
continue;
int64_t penalty = 0;
if (main_dist < 0) {
penalty = 10000000000LL; // 主方向重叠(反方向)的极大惩罚
main_dist = -main_dist;
}
// 正交方向无重叠惩罚,优先选择在同一行/列的窗口
int64_t no_overlap_penalty = 0;
if (orth_dist > 0) {
// LEFT/RIGHT 时 orth_dist 是垂直间距,>0 表示垂直无重叠
// UP/DOWN 时 orth_dist 是水平间距,>0 表示水平无重叠
no_overlap_penalty = 10000000LL;
}
int64_t tmp_distance = penalty + no_overlap_penalty +
(main_dist * main_dist) +
(orth_dist * orth_dist);
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = c;
}
if (c->mon == tc->mon && tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = c;
}
}
}
if (tempSameMonitorFocusClients)
return tempSameMonitorFocusClients;
return tempFocusClients;
}
Client *direction_select(const Arg *arg) {
@ -462,10 +301,7 @@ Client *direction_select(const Arg *arg) {
return NULL;
}
return find_client_by_direction(
tc, arg, true,
(is_scroller_layout(selmon) || is_centertile_layout(selmon)) &&
!selmon->isoverview);
return find_client_by_direction(tc, arg, true);
}
/* We probably should change the name of this, it sounds like

View file

@ -160,11 +160,26 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
if (pl)
*pl = l;
if (selmon && selmon->isoverview && (!l || layer_ignores_focus(l))) {
if (selmon && selmon->isoverview && config.ov_no_resize) {
ovc = xytoclient(x, y);
bool is_below = false;
if (l && l->layer_surface) {
is_below = (l->layer_surface->current.layer ==
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ||
l->layer_surface->current.layer ==
ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM);
}
if (ovc && (!l || layer_ignores_focus(l) || is_below)) {
if (pc)
*pc = ovc;
if (psurface && ovc)
*psurface = client_surface(ovc);
if (psurface)
*psurface = ovc ? client_surface(ovc) : NULL;
if (pl && ovc)
*pl = NULL;
}
}
}

View file

@ -217,7 +217,7 @@ static cJSON *build_monitor_tags_response(Monitor *m) {
static void send_static_json(int fd, const char *json_str) {
size_t len = strlen(json_str);
send(fd, json_str, len, MSG_NOSIGNAL);
send(fd, json_str, len, 0);
}
/* ---------- 一次性命令处理 ---------- */
@ -413,7 +413,7 @@ static void handle_command(int client_fd, const char *cmd_raw) {
char *msg = malloc(len + 2);
if (msg) {
snprintf(msg, len + 2, "%s\n", json_str);
send(client_fd, msg, len + 1, MSG_NOSIGNAL);
send(client_fd, msg, len + 1, 0);
free(msg);
}
free(json_str);
@ -434,7 +434,7 @@ static void ipc_notify_json_to_fd(int fd, cJSON *json) {
return;
}
snprintf(msg, len + 2, "%s\n", str);
if (send(fd, msg, len + 1, MSG_NOSIGNAL) < 0) {
if (send(fd, msg, len + 1, 0) < 0) {
struct ipc_watch_client *wc, *tmp;
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
if (wc->fd == fd) {
@ -462,7 +462,7 @@ static int ipc_watch_data_handler(int fd, uint32_t mask, void *data) {
}
if (mask & WL_EVENT_READABLE) {
char buf[64];
ssize_t n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
ssize_t n = recv(fd, buf, sizeof(buf), 0);
if (n == 0 || (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) {
ipc_remove_watch_client(wc);
}
@ -643,8 +643,7 @@ static int ipc_handle_client_data(int fd, uint32_t mask, void *data) {
available = client->buf_cap - client->buf_len;
}
ssize_t n = recv(fd, client->buf + client->buf_len, available - 1,
MSG_DONTWAIT);
ssize_t n = recv(fd, client->buf + client->buf_len, available - 1, 0);
if (n <= 0)
goto cleanup;
@ -683,8 +682,12 @@ static int ipc_handle_connection(int fd, uint32_t mask, void *data) {
if (client_fd < 0)
return 0;
// 设置 O_NONBLOCK
int flags = fcntl(client_fd, F_GETFL, 0);
fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
// 设置 FD_CLOEXEC
flags = fcntl(client_fd, F_GETFD, 0);
fcntl(client_fd, F_SETFD, flags | FD_CLOEXEC);
struct ipc_client_state *client = calloc(1, sizeof(*client));
client->fd = client_fd;
@ -715,7 +718,7 @@ void ipc_notify_monitor(Monitor *m) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -731,10 +734,8 @@ void ipc_notify_last_surface_ws_name(Monitor *m) {
if (wc->type != IPC_WATCH_LAST_OPEN_SURFACE)
continue;
/* 匹配具体 monitor 名称,或空名称表示默认 selmon */
bool match = false;
if (wc->target.monitor.name[0] == '\0') {
/* 订阅的是 selmon */
if (m == selmon)
match = true;
} else {
@ -759,7 +760,7 @@ void ipc_notify_last_surface_ws_name(Monitor *m) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
free(json_str);
@ -790,7 +791,7 @@ void ipc_notify_focusing_client(void) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -814,7 +815,7 @@ void ipc_notify_client(Client *c) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -840,7 +841,7 @@ void ipc_notify_tags(Monitor *m) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -870,7 +871,7 @@ void ipc_notify_all_monitors(void) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -900,7 +901,7 @@ void ipc_notify_all_clients(void) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -925,7 +926,7 @@ void ipc_notify_all_tags(void) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -951,7 +952,7 @@ void ipc_notify_keymode(void) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -977,7 +978,7 @@ void ipc_notify_kb_layout(void) {
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
if (send(wc->fd, json_str, len + 1, 0) < 0)
ipc_remove_watch_client(wc);
}
}
@ -1000,11 +1001,25 @@ void ipc_init(struct wl_event_loop *event_loop) {
snprintf(ipc_socket_path, sizeof(ipc_socket_path), "%s/mango-%d.sock",
xdg_runtime, getpid());
ipc_sock_fd =
socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
ipc_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (ipc_sock_fd < 0)
return;
// 设置 FD_CLOEXEC
int flags = fcntl(ipc_sock_fd, F_GETFD, 0);
if (flags == -1 || fcntl(ipc_sock_fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
wlr_log(WLR_ERROR, "failed to set FD_CLOEXEC on IPC socket");
close(ipc_sock_fd);
return;
}
// 设置 O_NONBLOCK
flags = fcntl(ipc_sock_fd, F_GETFL, 0);
if (flags == -1 || fcntl(ipc_sock_fd, F_SETFL, flags | O_NONBLOCK) == -1) {
wlr_log(WLR_ERROR, "failed to set O_NONBLOCK on IPC socket");
close(ipc_sock_fd);
return;
}
struct sockaddr_un addr = {.sun_family = AF_UNIX};
strncpy(addr.sun_path, ipc_socket_path, sizeof(addr.sun_path) - 1);

View file

@ -25,6 +25,16 @@ void set_size_per(Monitor *m, Client *c) {
c->master_inner_per = 1.0f;
c->stack_inner_per = 1.0f;
}
if (!c->iscustom_scroller_proportion) {
c->scroller_proportion =
m->pertag->scroller_default_proportion[m->pertag->curtag];
}
if (!c->iscustom_scroller_proportion_single) {
c->scroller_proportion_single =
m->pertag->scroller_default_proportion_single[m->pertag->curtag];
}
}
void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
@ -751,6 +761,7 @@ void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx,
void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, bool isvertical) {
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
return;
if (grabc->mon->isoverview)
@ -772,7 +783,7 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
Client *stack_head_client = headnode->client;
if (m->visible_tiling_clients == 1 &&
if (m->visible_scroll_tiling_clients == 1 &&
!config.scroller_ignore_proportion_single)
return;
@ -1212,6 +1223,10 @@ arrange(Monitor *m, bool want_animation, bool from_view) {
if (!m->wlr_output->enabled)
return;
if (!m->sel) {
m->sel = focustop(m);
}
pre_caculate_before_arrange(m, want_animation, from_view, false);
if (m->isoverview) {

View file

@ -565,6 +565,7 @@ void dwindle(Monitor *m) {
break;
}
// 清理树中已不存在的客户端
{
DwindleNode *leaves[512];
int32_t lc = 0;
@ -602,9 +603,14 @@ void dwindle(Monitor *m) {
}
}
// 获得焦点客户端,若为空则用第一个可见平铺客户端兜底
Client *focused = focustop(m);
if (focused && !dwindle_find_leaf(*root, focused))
focused = m->sel;
if (!focused && count > 0)
focused = vis[0];
for (int32_t i = 0; i < count; i++) {
if (!dwindle_find_leaf(*root, vis[i]))
dwindle_insert_with_config(root, vis[i], focused, ratio);

View file

@ -500,16 +500,13 @@ void deck(Monitor *m) {
return;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISFAKETILED(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];
// Calculate master width including outer gaps
if (n > nmasters)
mw = nmasters ? round((m->w.width - 2 * cur_gappoh) * mfact) : 0;
else
@ -521,16 +518,15 @@ void deck(Monitor *m) {
continue;
if (i < nmasters) {
c->master_mfact_per = mfact;
// Master area clients
client_tile_resize(
c,
int32_t h =
(m->w.height - 2 * cur_gappov - my) / (MIN(n, nmasters) - i);
client_tile_resize(c,
(struct wlr_box){.x = m->w.x + cur_gappoh,
.y = m->w.y + cur_gappov + my,
.width = mw,
.height = (m->w.height - 2 * cur_gappov - my) /
(MIN(n, nmasters) - i)},
.height = h},
0);
my += c->geom.height;
my += h;
} else {
// Stack area clients
c->master_mfact_per = mfact;
@ -588,6 +584,7 @@ void grid(Monitor *m) {
int32_t target_gappi = enablegaps ? config.gappih : 0;
float single_width_ratio = 0.9;
float single_height_ratio = 0.9;
struct wlr_box target_geom;
n = m->visible_fake_tiling_clients;
@ -603,11 +600,11 @@ void grid(Monitor *m) {
ISFAKETILED(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;
c->geom.height = ch;
client_tile_resize(c, c->geom, 0);
target_geom.x = m->w.x + (m->w.width - cw) / 2;
target_geom.y = m->w.y + (m->w.height - ch) / 2;
target_geom.width = cw;
target_geom.height = ch;
client_tile_resize(c, target_geom, 0);
return;
}
}
@ -651,16 +648,16 @@ void grid(Monitor *m) {
cw = avail_w * (col_pers[i] / sum_col);
if (i == 0) {
c->geom.x = m->w.x + target_gappo;
target_geom.x = m->w.x + target_gappo;
} else if (i == 1) {
// 第二个窗口的 X 坐标紧跟第一个窗口后面
float cw0 = avail_w * (col_pers[0] / sum_col);
c->geom.x = m->w.x + target_gappo + cw0 + target_gappi;
target_geom.x = m->w.x + target_gappo + cw0 + target_gappi;
}
c->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
c->geom.width = cw;
c->geom.height = ch;
client_tile_resize(c, c->geom, 0);
target_geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
target_geom.width = cw;
target_geom.height = ch;
client_tile_resize(c, target_geom, 0);
i++;
}
}
@ -757,11 +754,11 @@ void grid(Monitor *m) {
? (m->w.y + m->w.height - target_gappo - fl_cy)
: avail_h * (row_pers[r_idx] / sum_row);
c->geom.x = (int32_t)fl_cx;
c->geom.y = (int32_t)fl_cy;
c->geom.width = (int32_t)fl_cw;
c->geom.height = (int32_t)fl_ch;
client_tile_resize(c, c->geom, 0);
target_geom.x = (int32_t)fl_cx;
target_geom.y = (int32_t)fl_cy;
target_geom.width = (int32_t)fl_cw;
target_geom.height = (int32_t)fl_ch;
client_tile_resize(c, target_geom, 0);
i++;
}
}

View file

@ -353,10 +353,65 @@ void overview_resize(Monitor *m) {
free(c_arr);
}
void create_jump_hints(Monitor *m) {
const char jump_labels[] = "HJKLASDFGQWERTYUIOPZXCVBNM";
int label_idx = 0;
Client *c;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
if (label_idx >= 26)
break;
char c_char = jump_labels[label_idx];
c->jump_char = c_char;
// 把字符变成字符串
char label_text[2] = {c_char, '\0'};
mango_text_node_update(c->text_node, label_text, 1.0f);
wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, true);
wlr_scene_node_raise_to_top(&c->text_node->scene_buffer->node);
wlr_scene_node_set_position(
&c->text_node->scene_buffer->node,
c->geom.width / 2 - c->text_node->logical_width / 2,
c->geom.height / 2 - c->text_node->logical_height / 2);
label_idx++;
}
}
}
void begin_jump_mode(Monitor *m) { m->is_jump_mode = 1; }
void finish_jump_mode(Monitor *m) {
Client *c = NULL;
if (!m->is_jump_mode) {
return;
}
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m)) {
if (c->text_node->scene_buffer->node.enabled) {
c->jump_char = '\0';
wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node,
false);
}
}
}
m->is_jump_mode = 0;
}
void overview(Monitor *m) {
if (config.ov_no_resize) {
overview_scale(m);
} else {
overview_resize(m);
}
if (m->is_jump_mode) {
create_jump_hints(m);
}
}

View file

@ -284,6 +284,10 @@ void scroller(Monitor *m) {
uint32_t tag = m->pertag->curtag;
struct TagScrollerState *st = ensure_scroller_state(m, tag);
Client *c = NULL;
float scroller_default_proportion_single =
m->pertag->scroller_default_proportion_single[tag];
int32_t scroller_ignore_proportion_single =
m->pertag->scroller_ignore_proportion_single[tag];
/* 按全局客户端链表顺序收集所有堆叠头,确保视觉顺序正确 */
struct ScrollerStackNode *heads[64];
@ -323,14 +327,13 @@ void scroller(Monitor *m) {
m->w.width - 2 * config.scroller_structs - cur_gappih;
/* 单客户端特例 */
if (n_heads == 1 && !config.scroller_ignore_proportion_single &&
if (n_heads == 1 && !scroller_ignore_proportion_single &&
!heads[0]->client->isfullscreen &&
!heads[0]->client->ismaximizescreen) {
struct ScrollerStackNode *head = heads[0];
float single_proportion =
head->scroller_proportion_single > 0.0f
float single_proportion = head->scroller_proportion_single > 0.0f
? head->scroller_proportion_single
: config.scroller_default_proportion_single;
: scroller_default_proportion_single;
struct wlr_box target_geom;
target_geom.height = m->w.height - 2 * cur_gappov;
target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion;
@ -420,7 +423,7 @@ void scroller(Monitor *m) {
max_client_width) >
m->w.width - 2 * config.scroller_structs - cur_gappih)));
if (n_heads == 1 && config.scroller_ignore_proportion_single) {
if (n_heads == 1 && scroller_ignore_proportion_single) {
need_scroller = true;
}
if (start_drag_window)
@ -507,6 +510,10 @@ void vertical_scroller(Monitor *m) {
uint32_t tag = m->pertag->curtag;
struct TagScrollerState *st = ensure_scroller_state(m, tag);
Client *c = NULL;
float scroller_default_proportion_single =
m->pertag->scroller_default_proportion_single[tag];
int32_t scroller_ignore_proportion_single =
m->pertag->scroller_ignore_proportion_single[tag];
/* 按全局顺序收集堆叠头 */
struct ScrollerStackNode *heads[64];
@ -542,14 +549,13 @@ void vertical_scroller(Monitor *m) {
int32_t max_client_height =
m->w.height - 2 * config.scroller_structs - cur_gappiv;
if (n_heads == 1 && !config.scroller_ignore_proportion_single &&
if (n_heads == 1 && !scroller_ignore_proportion_single &&
!heads[0]->client->isfullscreen &&
!heads[0]->client->ismaximizescreen) {
struct ScrollerStackNode *head = heads[0];
float single_proportion =
head->scroller_proportion_single > 0.0f
float single_proportion = head->scroller_proportion_single > 0.0f
? head->scroller_proportion_single
: config.scroller_default_proportion_single;
: scroller_default_proportion_single;
struct wlr_box target_geom;
target_geom.width = m->w.width - 2 * cur_gappoh;
target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion;
@ -638,7 +644,7 @@ void vertical_scroller(Monitor *m) {
max_client_height) >
m->w.height - 2 * config.scroller_structs - cur_gappiv)));
if (n_heads == 1 && config.scroller_ignore_proportion_single) {
if (n_heads == 1 && scroller_ignore_proportion_single) {
need_scroller = true;
}
if (start_drag_window)
@ -743,9 +749,9 @@ void scroller_insert_stack(Client *c, Client *target_client,
return;
if (c->isfullscreen)
setfullscreen(c, 0);
setfullscreen(c, 0, true);
if (c->ismaximizescreen)
setmaximizescreen(c, 0);
setmaximizescreen(c, 0, true);
Monitor *m = c->mon;
uint32_t tag = m->pertag->curtag;
@ -785,9 +791,9 @@ void scroller_insert_stack(Client *c, Client *target_client,
head = head->prev_in_stack;
Client *stack_head = head->client;
if (stack_head->ismaximizescreen)
setmaximizescreen(stack_head, 0);
setmaximizescreen(stack_head, 0, true);
if (stack_head->isfullscreen)
setfullscreen(stack_head, 0);
setfullscreen(stack_head, 0, true);
/* 同步到 Client 字段 */
sync_scroller_state_to_clients(m, tag);

View file

@ -137,12 +137,10 @@ void vertical_deck(Monitor *m) {
return;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISFAKETILED(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];
@ -156,16 +154,18 @@ void vertical_deck(Monitor *m) {
if (!VISIBLEON(c, m) || !ISFAKETILED(c))
continue;
if (i < nmasters) {
client_tile_resize(
c,
c->master_mfact_per = mfact;
int32_t w =
(m->w.width - 2 * cur_gappoh - mx) / (MIN(n, nmasters) - i);
client_tile_resize(c,
(struct wlr_box){.x = m->w.x + cur_gappoh + mx,
.y = m->w.y + cur_gappov,
.width = (m->w.width - 2 * cur_gappoh - mx) /
(MIN(n, nmasters) - i),
.width = w,
.height = mh},
0);
mx += c->geom.width;
mx += w;
} else {
c->master_mfact_per = mfact;
client_tile_resize(
c,
(struct wlr_box){.x = m->w.x + cur_gappoh,
@ -186,14 +186,13 @@ void vertical_grid(Monitor *m) {
int32_t cw, ch;
int32_t rows, cols, overrows;
Client *c = NULL;
int32_t target_gappo =
enablegaps ? m->isoverview ? config.overviewgappo : config.gappov : 0;
int32_t target_gappi =
enablegaps ? m->isoverview ? config.overviewgappi : config.gappiv : 0;
float single_width_ratio = m->isoverview ? 0.7 : 0.9;
float single_height_ratio = m->isoverview ? 0.8 : 0.9;
int32_t target_gappo = enablegaps ? config.gappov : 0;
int32_t target_gappi = enablegaps ? config.gappiv : 0;
float single_width_ratio = 0.9;
float single_height_ratio = 0.9;
struct wlr_box target_geom;
n = m->isoverview ? m->visible_clients : m->visible_fake_tiling_clients;
n = m->visible_fake_tiling_clients;
if (n == 0)
return;
@ -202,15 +201,14 @@ void vertical_grid(Monitor *m) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) ||
ISFAKETILED(c))) {
(!client_is_x11_popup(c) || ISFAKETILED(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;
client_tile_resize(c, c->geom, 0);
target_geom.x = m->w.x + (m->w.width - cw) / 2;
target_geom.y = m->w.y + (m->w.height - ch) / 2;
target_geom.width = cw;
target_geom.height = ch;
client_tile_resize(c, target_geom, 0);
return;
}
}
@ -224,8 +222,7 @@ void vertical_grid(Monitor *m) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) ||
ISFAKETILED(c))) {
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
if (i < 2)
row_pers[i] =
(c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
@ -242,8 +239,7 @@ void vertical_grid(Monitor *m) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) ||
ISFAKETILED(c))) {
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
c->grid_col_idx = 0;
c->grid_row_idx = i;
c->grid_col_per = 1.0f;
@ -252,17 +248,17 @@ void vertical_grid(Monitor *m) {
// 根据分配的权重动态计算当前窗口的高度
ch = avail_h * (row_pers[i] / sum_row);
c->geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo;
target_geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo;
if (i == 0) {
c->geom.y = m->w.y + target_gappo;
target_geom.y = m->w.y + target_gappo;
} else if (i == 1) {
// 第二个窗口的 Y 坐标紧跟第一个窗口下面
float ch0 = avail_h * (row_pers[0] / sum_row);
c->geom.y = m->w.y + target_gappo + ch0 + target_gappi;
target_geom.y = m->w.y + target_gappo + ch0 + target_gappi;
}
c->geom.width = cw;
c->geom.height = ch;
client_tile_resize(c, c->geom, 0);
target_geom.width = cw;
target_geom.height = ch;
client_tile_resize(c, target_geom, 0);
i++;
}
}
@ -287,7 +283,7 @@ void vertical_grid(Monitor *m) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISFAKETILED(c))) {
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
int32_t c_idx = i / rows;
int32_t r_idx = i % rows;
if (r_idx == 0)
@ -314,7 +310,7 @@ void vertical_grid(Monitor *m) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISFAKETILED(c))) {
(!client_is_x11_popup(c) || ISFAKETILED(c))) {
int32_t c_idx = i / rows;
int32_t r_idx = i % rows;
@ -352,11 +348,11 @@ void vertical_grid(Monitor *m) {
? (m->w.x + m->w.width - target_gappo - fl_cx)
: avail_w * (col_pers[c_idx] / sum_col);
c->geom.x = (int32_t)fl_cx;
c->geom.y = (int32_t)fl_cy;
c->geom.width = (int32_t)fl_cw;
c->geom.height = (int32_t)fl_ch;
client_tile_resize(c, c->geom, 0);
target_geom.x = (int32_t)fl_cx;
target_geom.y = (int32_t)fl_cy;
target_geom.width = (int32_t)fl_cw;
target_geom.height = (int32_t)fl_ch;
client_tile_resize(c, target_geom, 0);
i++;
}
}

View file

@ -96,6 +96,7 @@
#include <xcb/xcb_icccm.h>
#endif
#include "common/util.h"
#include "draw/text-node.h"
/* macros */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
@ -190,6 +191,7 @@ enum { NONE, OPEN, MOVE, CLOSE, TAG, FOCUS, OPAFADEIN, OPAFADEOUT, OVERVIEW };
enum { UNFOLD, FOLD, INVALIDFOLD };
enum { PREV, NEXT };
enum { STATE_UNSPECIFIED = 0, STATE_ENABLED, STATE_DISABLED };
enum { FORCE, UNFORCE };
enum tearing_mode {
TEARING_DISABLED = 0,
@ -326,6 +328,7 @@ struct Client {
struct wlr_scene_shadow *shadow;
struct wlr_scene_tree *scene_surface;
struct wlr_scene_tree *overview_scene_surface;
struct mango_text_node *text_node;
struct wl_list link;
struct wl_list flink;
struct wl_list fadeout_link;
@ -380,6 +383,8 @@ struct Client {
int32_t is_in_scratchpad;
int32_t iscustomsize;
int32_t iscustompos;
int32_t iscustom_scroller_proportion;
int32_t iscustom_scroller_proportion_single;
int32_t is_scratchpad_show;
int32_t isglobal;
int32_t isnoborder;
@ -430,6 +435,7 @@ struct Client {
int32_t allow_shortcuts_inhibit;
float scroller_proportion_single;
bool isfocusing;
char jump_char;
bool enable_drop_area_draw;
int32_t drop_direction;
struct wlr_box drag_tile_float_backup_geom;
@ -551,6 +557,7 @@ struct Monitor {
uint32_t ovbk_prev_tagset;
Client *sel, *prevsel;
int32_t isoverview;
int32_t is_jump_mode;
int32_t is_in_hotarea;
int32_t asleep;
uint32_t visible_clients;
@ -734,8 +741,9 @@ static void run(char *startup_cmd);
static void setcursor(struct wl_listener *listener, void *data);
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 setfullscreen(Client *c, int32_t fullscreen, bool rearrange);
static void setmaximizescreen(Client *c, int32_t maximizescreen,
bool rearrange);
static void reset_maximizescreen_size(Client *c);
static void setgaps(int32_t oh, int32_t ov, int32_t ih, int32_t iv);
@ -854,7 +862,7 @@ static float *get_border_color(Client *c);
static void clear_fullscreen_and_maximized_state(Monitor *m);
static void request_fresh_all_monitors(void);
static Client *find_client_by_direction(Client *tc, const Arg *arg,
bool findfloating, bool ignore_align);
bool findfloating);
static void exit_scroller_stack(Client *c);
static Client *scroll_get_stack_head_client(Client *c);
static bool client_only_in_one_tag(Client *c);
@ -895,6 +903,10 @@ Client *scroll_get_stack_tail_client(Client *c);
static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c);
static void overview_backup_surface(Client *c);
static void create_jump_hints(Monitor *m);
static void finish_jump_mode(Monitor *m);
static void begin_jump_mode(Monitor *m);
#include "data/static_keymap.h"
#include "dispatch/bind_declare.h"
#include "layout/layout.h"
@ -1025,6 +1037,9 @@ struct Pertag {
int32_t no_hide[LENGTH(tags) + 1];
int32_t no_render_border[LENGTH(tags) + 1];
int32_t open_as_floating[LENGTH(tags) + 1];
float scroller_default_proportion[LENGTH(tags) + 1];
float scroller_default_proportion_single[LENGTH(tags) + 1];
int32_t scroller_ignore_proportion_single[LENGTH(tags) + 1];
struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
const Layout *ltidxs[LENGTH(tags) + 1];
struct TagScrollerState *scroller_state[LENGTH(tags) + 1];
@ -1072,7 +1087,7 @@ static struct wl_listener last_cursor_surface_destroy_listener = {
.notify = last_cursor_surface_destroy};
#ifdef XWAYLAND
static void fix_xwayland_unmanaged_coordinate(Client *c);
static void fix_xwayland_coordinate(struct wlr_box *geom);
static int32_t synckeymap(void *data);
static void activatex11(struct wl_listener *listener, void *data);
static void configurex11(struct wl_listener *listener, void *data);
@ -1149,11 +1164,11 @@ void clear_fullscreen_flag(Client *c) {
}
if (c->isfullscreen) {
setfullscreen(c, false);
setfullscreen(c, false, true);
}
if (c->ismaximizescreen) {
setmaximizescreen(c, 0);
setmaximizescreen(c, 0, true);
}
}
@ -1614,7 +1629,7 @@ void applyrules(Client *c) {
#ifdef XWAYLAND
if (c->isfloating && client_is_x11(c)) {
fix_xwayland_unmanaged_coordinate(c);
fix_xwayland_coordinate(&c->geom);
c->float_geom = c->geom;
}
#endif
@ -1653,6 +1668,14 @@ void applyrules(Client *c) {
c->isfloating = 1;
}
if (r->scroller_proportion > 0.0f) {
c->iscustom_scroller_proportion = 1;
}
if (r->scroller_proportion_single > 0.0f) {
c->iscustom_scroller_proportion_single = 1;
}
// set geometry of floating client
if (r->width > 1)
@ -1747,7 +1770,7 @@ void applyrules(Client *c) {
view_in_mon(&(Arg){.ui = c->tags}, true, c->mon, true);
}
setfullscreen(c, fullscreen_state_backup);
setfullscreen(c, fullscreen_state_backup, true);
if (c->isfakefullscreen) {
setfakefullscreen(c, 1);
@ -2201,9 +2224,10 @@ void hold_end(struct wl_listener *listener, void *data) {
Client *find_closest_tiled_client(Client *c) {
Client *tc, *closest = NULL;
long min_dist = LONG_MAX;
Monitor *cursor_mon = xytomon(cursor->x, cursor->y);
wl_list_for_each(tc, &clients, link) {
if (tc == c || !ISTILED(tc) || !VISIBLEON(tc, c->mon))
if (tc == c || !ISTILED(tc) || !VISIBLEON(tc, cursor_mon))
continue;
if (cursor->x >= tc->geom.x &&
@ -2235,7 +2259,9 @@ void place_drag_tile_client(Client *c) {
if (closest->drop_direction == UNDIR) {
setfloating(c, 0);
exchange_two_client(c, closest);
wl_list_remove(&c->link);
wl_list_insert(closest->link.prev, &c->link);
arrange(closest->mon, false, false);
return;
}
@ -2334,6 +2360,17 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) {
}
}
// overview模式下鼠标左键跳转右键关闭窗口
if (selmon && selmon->isoverview && event->button == BTN_LEFT && c) {
toggleoverview(&(Arg){.i = 1});
return true;
}
if (selmon && selmon->isoverview && event->button == BTN_RIGHT && c) {
pending_kill_client(c);
return true;
}
// 当鼠标焦点在layer上的时候不检测虚拟键盘的mod状态
// 避免layer虚拟键盘锁死mod按键状态
hard_keyboard = &kb_group->wlr_group->keyboard;
@ -2350,16 +2387,6 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) {
break;
m = &config.mouse_bindings[ji];
if (selmon->isoverview && event->button == BTN_LEFT && c) {
toggleoverview(&(Arg){.i = 1});
return true;
}
if (selmon->isoverview && event->button == BTN_RIGHT && c) {
pending_kill_client(c);
return true;
}
if (CLEANMASK(mods) == CLEANMASK(m->mod) &&
event->button == m->button && m->func &&
(CLEANMASK(m->mod) != 0 ||
@ -2542,6 +2569,8 @@ void cleanup(void) {
/* Destroy after the wayland display (when the monitors are already
destroyed) to avoid destroying them with an invalid scene output. */
wlr_scene_node_destroy(&scene->tree.node);
mango_text_global_finish();
}
void cleanupmon(struct wl_listener *listener, void *data) {
@ -3139,7 +3168,6 @@ void createlayersurface(struct wl_listener *listener, void *data) {
LISTEN(&l->scene->node.events.destroy, &l->destroy, destroylayernodenotify);
wl_list_insert(&l->mon->layers[layer_surface->pending.layer], &l->link);
wlr_surface_send_enter(surface, layer_surface->output);
}
void createlocksurface(struct wl_listener *listener, void *data) {
@ -3939,7 +3967,7 @@ fullscreennotify(struct wl_listener *listener, void *data) {
if (!c || c->iskilling)
return;
setfullscreen(c, client_wants_fullscreen(c));
setfullscreen(c, client_wants_fullscreen(c), true);
}
void requestmonstate(struct wl_listener *listener, void *data) {
@ -4205,6 +4233,11 @@ void keypress(struct wl_listener *listener, void *data) {
}
}
if (config.cursor_hide_on_keypress && !cursor_hidden &&
event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
hidecursor(NULL);
}
/* On _press_ if there is no active screen locker,
* attempt to process a compositor keybinding. */
for (i = 0; i < nsyms; i++)
@ -4231,6 +4264,27 @@ void keypress(struct wl_listener *listener, void *data) {
if (handled)
return;
if (selmon && selmon->is_jump_mode &&
event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
for (i = 0; i < nsyms; i++) {
xkb_keysym_t sym = xkb_keysym_to_lower(syms[i]);
if (sym >= XKB_KEY_a && sym <= XKB_KEY_z) {
char c_char = 'A' + (sym - XKB_KEY_a);
Client *c;
wl_list_for_each(c, &clients, link) {
if (c->mon == selmon && c->jump_char == c_char) {
focusclient(c, 1);
toggleoverview(&(Arg){.i = 1});
return;
}
}
} else if (sym == XKB_KEY_Escape) {
togglejump(&(Arg){.i = 0});
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 */
@ -4398,6 +4452,8 @@ void init_client_properties(Client *c) {
c->ignore_minimize = 1;
c->iscustomsize = 0;
c->iscustompos = 0;
c->iscustom_scroller_proportion = 0;
c->iscustom_scroller_proportion_single = 0;
c->master_mfact_per = 0.0f;
c->master_inner_per = 0.0f;
c->stack_inner_per = 0.0f;
@ -4468,26 +4524,30 @@ mapnotify(struct wl_listener *listener, void *data) {
/* Handle unmanaged clients first so we can return prior create borders
*/
#ifdef XWAYLAND
if (client_is_unmanaged(c)) {
/* Unmanaged clients always are floating */
#ifdef XWAYLAND
if (client_is_x11(c)) {
fix_xwayland_unmanaged_coordinate(c);
fix_xwayland_coordinate(&c->geom);
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x,
c->geom.y, c->geom.width,
c->geom.height);
LISTEN(&c->surface.xwayland->events.set_geometry, &c->set_geometry,
setgeometrynotify);
}
#endif
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
if (client_wants_focus(c)) {
focusclient(c, 1);
exclusive_focus = c;
}
return;
}
#endif
// extra node
c->text_node = mango_text_node_create(c->scene, config.jumhitdata);
wlr_scene_node_lower_to_bottom(&c->text_node->scene_buffer->node);
wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, false);
for (i = 0; i < 2; i++) {
c->splitindicator[i] = wlr_scene_rect_create(
c->scene, 0, 0,
@ -4577,9 +4637,9 @@ void maximizenotify(struct wl_listener *listener, void *data) {
}
if (client_request_maximize(c, data)) {
setmaximizescreen(c, 1);
setmaximizescreen(c, 1, true);
} else {
setmaximizescreen(c, 0);
setmaximizescreen(c, 0, true);
}
}
@ -4807,7 +4867,8 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
if (!surface && !seat->drag && !cursor_hidden)
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
if (c && c->mon && !c->animation.running && (INSIDEMON(c) || !ISTILED(c))) {
if (c && c->mon && !c->animation.running &&
(INSIDEMON(c) || !ISSCROLLTILED(c))) {
scroller_focus_lock = 0;
}
@ -4819,13 +4880,15 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
}
if (!scroller_focus_lock || !(c && c->mon && !INSIDEMON(c))) {
if (c && c->mon && is_scroller_layout(c->mon) && !INSIDEMON(c)) {
if (c && c->mon && ISSCROLLTILED(c) && is_scroller_layout(c->mon) &&
!INSIDEMON(c)) {
should_lock = true;
}
if (!((!config.edge_scroller_pointer_focus ||
speed < config.edge_scroller_focus_allow_speed) &&
c && c->mon && is_scroller_layout(c->mon) && !INSIDEMON(c))) {
c && c->mon && ISSCROLLTILED(c) && is_scroller_layout(c->mon) &&
!INSIDEMON(c))) {
pointerfocus(c, surface, sx, sy, time);
}
@ -5154,7 +5217,8 @@ void exchange_two_client(Client *c1, Client *c2) {
const Layout *layout1 = m1->pertag->ltidxs[m1->pertag->curtag];
const Layout *layout2 = m2->pertag->ltidxs[m2->pertag->curtag];
if (layout1->id == SCROLLER || layout2->id == SCROLLER) {
if (layout1->id == SCROLLER || layout2->id == SCROLLER ||
layout1->id == VERTICAL_SCROLLER || layout2->id == VERTICAL_SCROLLER) {
exchange_two_scroller_clients(c1, c2);
return;
}
@ -5440,7 +5504,7 @@ void exit_scroller_stack(Client *c) {
}
}
void setmaximizescreen(Client *c, int32_t maximizescreen) {
void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) {
struct wlr_box maximizescreen_box;
if (!c || !c->mon || !client_surface(c)->mapped || c->iskilling)
return;
@ -5481,6 +5545,7 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) {
client_set_maximized(c, true);
}
if (rearrange)
arrange(c->mon, false, false);
}
@ -5490,12 +5555,13 @@ void setfakefullscreen(Client *c, int32_t fakefullscreen) {
return;
if (c->isfullscreen)
setfullscreen(c, 0);
setfullscreen(c, 0, true);
client_set_fullscreen(c, fakefullscreen);
}
void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自带全屏
void setfullscreen(Client *c, int32_t fullscreen,
bool rearrange) // 用自定义全屏代理自带全屏
{
if (!c || !c->mon || !client_surface(c)->mapped || c->iskilling)
@ -5524,6 +5590,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层
if (!is_scroller_layout(c->mon) || c->isfloating)
resize(c, c->mon->m, 1);
} else {
c->bw = c->isnoborder ? 0 : config.borderpx;
if (c->isfloating)
@ -5540,6 +5607,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
layers[fullscreen || c->isfloating ? LyrTop : LyrTile]);
}
if (rearrange)
arrange(c->mon, false, false);
}
@ -5669,7 +5737,8 @@ void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) {
client_reset_mon_tags(c, m, newtags);
check_match_tag_floating_rule(c, m);
setfloating(c, c->isfloating);
setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
setfullscreen(c, c->isfullscreen,
true); /* This will call arrange(c->mon) */
}
if (focus && !client_is_x11_popup(c)) {
@ -5805,13 +5874,20 @@ void setup(void) {
}
init_baked_points();
int32_t drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE};
int32_t drm_fd, i;
int32_t sig[] = {SIGCHLD, SIGINT,
SIGTERM}; // 不设置SIGPIPE,因为ipc发送失败不应该影响主程序
struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
sigemptyset(&sa.sa_mask);
for (i = 0; i < LENGTH(sig); i++)
sigaction(sig[i], &sa, NULL);
// 单独为 SIGPIPE 设置忽略
struct sigaction sa_pipe = {.sa_flags = 0, .sa_handler = SIG_IGN};
sigemptyset(&sa_pipe.sa_mask);
sigaction(SIGPIPE, &sa_pipe, NULL);
wlr_log_init(config.log_level, NULL);
/* The Wayland display is managed by libwayland. It handles accepting
@ -6254,13 +6330,13 @@ void overview_restore(Client *c, const Arg *arg) {
resize(c, c->overview_backup_geom, 0);
} else if (c->isfullscreen || c->ismaximizescreen) {
if (want_restore_fullscreen(c) && c->ismaximizescreen) {
setmaximizescreen(c, 1);
setmaximizescreen(c, 1, false);
} else if (want_restore_fullscreen(c) && c->isfullscreen) {
setfullscreen(c, 1);
setfullscreen(c, 1, false);
} else {
client_pending_fullscreen_state(c, 0);
client_pending_maximized_state(c, 0);
setfullscreen(c, false);
setfullscreen(c, false, false);
}
} else {
if (c->is_restoring_from_ov) {
@ -6456,8 +6532,8 @@ void unmapnotify(struct wl_listener *listener, void *data) {
}
if (c->swallowedby) {
setmaximizescreen(c->swallowedby, c->ismaximizescreen);
setfullscreen(c->swallowedby, c->isfullscreen);
setmaximizescreen(c->swallowedby, c->ismaximizescreen, true);
setfullscreen(c->swallowedby, c->isfullscreen, true);
c->swallowedby->swallowing = NULL;
c->swallowedby = NULL;
}
@ -6469,6 +6545,7 @@ void unmapnotify(struct wl_listener *listener, void *data) {
c->stack_proportion = 0.0f;
mango_text_node_destroy(c->text_node);
wlr_scene_node_destroy(&c->scene->node);
printstatus(IPC_WATCH_ARRANGGE);
motionnotify(0, NULL, 0, 0, 0, 0);
@ -6815,16 +6892,17 @@ void virtualpointer(struct wl_listener *listener, void *data) {
}
#ifdef XWAYLAND
void fix_xwayland_unmanaged_coordinate(Client *c) {
void fix_xwayland_coordinate(struct wlr_box *geom) {
if (!selmon)
return;
// 1. 如果窗口已经在当前活动显示器内,直接返回
if (c->geom.x >= selmon->m.x && c->geom.x < selmon->m.x + selmon->m.width &&
c->geom.y >= selmon->m.y && c->geom.y < selmon->m.y + selmon->m.height)
if (geom->x >= selmon->m.x && geom->x <= selmon->m.x + selmon->m.width &&
geom->y >= selmon->m.y && geom->y <= selmon->m.y + selmon->m.height)
return;
c->geom = setclient_coordinate_center(c, selmon, c->geom, 0, 0);
geom->x = selmon->m.x + (selmon->m.width - geom->width) / 2;
geom->y = selmon->m.y + (selmon->m.height - geom->height) / 2;
}
int32_t synckeymap(void *data) {
@ -6879,24 +6957,41 @@ void activatex11(struct wl_listener *listener, void *data) {
void configurex11(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, configure);
struct wlr_xwayland_surface_configure_event *event = data;
struct wlr_box new_geo;
new_geo.x = event->x;
new_geo.y = event->y;
new_geo.width = event->width;
new_geo.height = event->height;
fix_xwayland_coordinate(&new_geo);
if (!client_surface(c) || !client_surface(c)->mapped) {
wlr_xwayland_surface_configure(c->surface.xwayland, event->x, event->y,
event->width, event->height);
wlr_xwayland_surface_configure(c->surface.xwayland, new_geo.x,
new_geo.y, new_geo.width,
new_geo.height);
return;
}
if (client_is_unmanaged(c)) {
wlr_scene_node_set_position(&c->scene->node, event->x, event->y);
wlr_xwayland_surface_configure(c->surface.xwayland, event->x, event->y,
event->width, event->height);
wlr_scene_node_set_position(&c->scene->node, new_geo.x, new_geo.y);
wlr_xwayland_surface_configure(c->surface.xwayland, new_geo.x,
new_geo.y, new_geo.width,
new_geo.height);
return;
}
if ((c->isfloating && c != grabc) ||
!c->mon->pertag->ltidxs[c->mon->pertag->curtag]->arrange) {
if (c->isfloating && c != grabc) {
new_geo.x = new_geo.x - c->bw;
new_geo.y = new_geo.y - c->bw;
new_geo.width = new_geo.width + c->bw * 2;
new_geo.height = new_geo.height + c->bw * 2;
fix_xwayland_coordinate(&new_geo);
resize(c,
(struct wlr_box){.x = event->x - c->bw,
.y = event->y - c->bw,
.width = event->width + c->bw * 2,
.height = event->height + c->bw * 2},
(struct wlr_box){.x = new_geo.x,
.y = new_geo.y,
.width = new_geo.width,
.height = new_geo.height},
0);
} else {
arrange(c->mon, false, false);