Compare commits

...

29 commits
0.14.0 ... main

Author SHA1 Message Date
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
20 changed files with 464 additions and 170 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. |

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). |
@ -48,4 +49,4 @@ description: Advanced settings for XWayland, focus behavior, and system integrat
| `idleinhibit_ignore_visible` | `0` | Allow invisible clients (e.g., background audio players) to inhibit idle. |
| `tag_carousel` | `0` | Enable tag carousel (cycling through tags). |
| `drag_tile_refresh_interval` | `8.0` | Interval (1.016.0) to refresh tiled window resize during drag. Too small may cause application lag. |
| `drag_floating_refresh_interval` | `8.0` | Interval (1.016.0) to refresh floating window resize during drag. Too small may cause application lag. |
| `drag_floating_refresh_interval` | `8.0` | Interval (1.016.0) to refresh floating window resize during drag. Too small may cause application lag. |

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`.
---

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

@ -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

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)

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'],
version : '0.14.0',
project('mango', ['c'],
version : '0.14.2',
)
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'))
@ -42,12 +38,11 @@ libscenefx_dep = dependency('scenefx-0.4',version: '>=0.4.1')
pixman_dep = dependency('pixman-1')
cjson_dep = dependency('libcjson')
# 获取版本信息
# version info
git = find_program('git', required : false)
is_git_repo = false
# 检查当前目录是否是 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 +51,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 +72,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 +85,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'
@ -135,7 +130,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 +145,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

@ -51,7 +51,7 @@ static void finish_exchange_arrange_and_focus(Client *c1, Client *c2,
}
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) {
@ -86,4 +86,10 @@ void client_active(Client *c) {
target = get_tags_first_tag(c->tags);
view_in_mon(&(Arg){.ui = target}, true, c->mon, true);
focusclient(c, 1);
}
void client_pending_force_kill(Client *c) {
if (!c)
return;
kill(c->pid, SIGKILL);
}

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);

View file

@ -261,6 +261,7 @@ typedef struct {
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;
@ -559,6 +560,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];
@ -1029,6 +1050,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 +1715,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) {
@ -3325,6 +3349,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);
@ -3486,6 +3512,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;

View file

@ -345,7 +345,11 @@ int32_t setmfact(const Arg *arg) {
int32_t killclient(const Arg *arg) {
Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL);
if (c) {
pending_kill_client(c);
if (arg->i == FORCE) {
client_pending_force_kill(c);
} else {
pending_kill_client(c);
}
}
return 0;
}

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 {

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

@ -751,6 +751,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 +773,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;

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,
(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)},
0);
my += c->geom.height;
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 = h},
0);
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

@ -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,
(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),
.height = mh},
0);
mx += c->geom.width;
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 = w,
.height = mh},
0);
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

@ -190,6 +190,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,
@ -1072,7 +1073,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);
@ -1614,7 +1615,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
@ -3139,7 +3140,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) {
@ -4205,6 +4205,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++)
@ -4468,24 +4473,24 @@ 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);
LISTEN(&c->surface.xwayland->events.set_geometry, &c->set_geometry,
setgeometrynotify);
}
#endif
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
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);
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
if (client_wants_focus(c)) {
focusclient(c, 1);
exclusive_focus = c;
}
return;
}
#endif
// extra node
for (i = 0; i < 2; i++) {
@ -4807,7 +4812,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 +4825,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 +5162,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;
}
@ -5524,6 +5533,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)
@ -5805,13 +5815,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
@ -6815,16 +6832,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 +6897,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);