Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions assets/config.conf
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ no_border_when_single=0
axis_bind_apply_timeout=100
focus_on_activate=1
idleinhibit_ignore_visible=0
# Restore and save session state across compositor restarts.
# Disabled by default to preserve Mango's current behavior.
# Minimized restore is currently unsupported.
# Restore expects target outputs to already exist before clients map.
# Mango only restores from a trusted session file owned by the current user
# and not writable by group or others.
session_restore=0
# Optional relaunch override used when Mango cannot recover a suitable launcher.
# Format: session_launch=app_id|command
# or: session_launch=app_id|title|command
# Mango prefers exact Mango-owned spawn commands, then normalized desktop/Flatpak
# launchers, then raw process argv before consulting session_launch.
# session_launch=foot|foot
# session_launch=foot|gamma|foot -a foot -T gamma -e sh -lc "sleep 600"
# session_launch=org.kde.dolphin|dolphin .
sloppyfocus=1
warpcursor=1
focus_cross_monitor=0
Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ endif
executable('mango',
'src/mango.c',
'src/common/util.c',
'src/session/session.c',
'src/ext-protocol/wlr_ext_workspace_v1.c',
wayland_sources,
dependencies : [
Expand Down
89 changes: 89 additions & 0 deletions src/config/parse_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ typedef struct {
char *value;
} ConfigEnv;

typedef struct {
char app_id[256];
char title[512];
char command[1024];
} ConfigSessionLaunchRule;

typedef struct {
const char *id;
const char *title;
Expand Down Expand Up @@ -251,6 +257,7 @@ typedef struct {

uint32_t axis_bind_apply_timeout;
uint32_t focus_on_activate;
int32_t session_restore;
int32_t idleinhibit_ignore_visible;
int32_t sloppyfocus;
int32_t warpcursor;
Expand Down Expand Up @@ -352,6 +359,8 @@ typedef struct {

char **exec_once;
int32_t exec_once_count;
ConfigSessionLaunchRule *session_launch_rules;
int32_t session_launch_rules_count;

char *cursor_theme;
uint32_t cursor_size;
Expand Down Expand Up @@ -1426,6 +1435,71 @@ bool parse_option(Config *config, char *key, char *value) {
config->allow_shortcuts_inhibit = atoi(value);
} else if (strcmp(key, "allow_lock_transparent") == 0) {
config->allow_lock_transparent = atoi(value);
} else if (strcmp(key, "session_restore") == 0) {
config->session_restore = atoi(value);
} else if (strcmp(key, "session_launch") == 0) {
ConfigSessionLaunchRule rule = {0};
ConfigSessionLaunchRule *new_rules = NULL;
char *first_sep = strchr(value, '|');
char *second_sep = NULL;
size_t app_len, title_len = 0, cmd_len;

if (!first_sep) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid session_launch "
"format. Expected app_id|command or "
"app_id|title|command\033[0m\n");
return false;
}

second_sep = strchr(first_sep + 1, '|');
app_len = (size_t)(first_sep - value);
if (second_sep) {
title_len = (size_t)(second_sep - (first_sep + 1));
cmd_len = strlen(second_sep + 1);
} else {
cmd_len = strlen(first_sep + 1);
}

if (app_len == 0 || cmd_len == 0 || app_len >= sizeof(rule.app_id) ||
cmd_len >= sizeof(rule.command) ||
title_len >= sizeof(rule.title)) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid session_launch "
"entry length\033[0m\n");
return false;
}

memcpy(rule.app_id, value, app_len);
rule.app_id[app_len] = '\0';
if (second_sep) {
memcpy(rule.title, first_sep + 1, title_len);
rule.title[title_len] = '\0';
memcpy(rule.command, second_sep + 1, cmd_len + 1);
} else {
memcpy(rule.command, first_sep + 1, cmd_len + 1);
}
trim_whitespace(rule.app_id);
trim_whitespace(rule.title);
trim_whitespace(rule.command);
if (rule.app_id[0] == '\0' || rule.command[0] == '\0') {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m session_launch requires "
"both app_id and command\033[0m\n");
return false;
}

new_rules = realloc(config->session_launch_rules,
sizeof(*config->session_launch_rules) *
(config->session_launch_rules_count + 1));
if (!new_rules) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Failed to allocate "
"session_launch rules\033[0m\n");
return false;
}
config->session_launch_rules = new_rules;
config->session_launch_rules[config->session_launch_rules_count++] = rule;
} else if (strcmp(key, "no_border_when_single") == 0) {
config->no_border_when_single = atoi(value);
} else if (strcmp(key, "no_radius_when_single") == 0) {
Expand Down Expand Up @@ -2826,6 +2900,14 @@ void free_circle_layout(Config *config) {
config->circle_layout_count = 0; // 重置计数
}

void free_session_launch_rules(Config *config) {
if (config->session_launch_rules) {
free(config->session_launch_rules);
config->session_launch_rules = NULL;
}
config->session_launch_rules_count = 0;
}

void free_baked_points(void) {
if (baked_points_move) {
free(baked_points_move);
Expand Down Expand Up @@ -3096,6 +3178,7 @@ void free_config(void) {

// 释放 circle_layout
free_circle_layout(&config);
free_session_launch_rules(&config);

// 释放动画资源
free_baked_points();
Expand Down Expand Up @@ -3171,6 +3254,7 @@ void override_config(void) {
config.axis_bind_apply_timeout =
CLAMP_INT(config.axis_bind_apply_timeout, 0, 1000);
config.focus_on_activate = CLAMP_INT(config.focus_on_activate, 0, 1);
config.session_restore = CLAMP_INT(config.session_restore, 0, 1);
config.idleinhibit_ignore_visible =
CLAMP_INT(config.idleinhibit_ignore_visible, 0, 1);
config.sloppyfocus = CLAMP_INT(config.sloppyfocus, 0, 1);
Expand Down Expand Up @@ -3275,6 +3359,9 @@ void set_value_default() {

config.axis_bind_apply_timeout = 100;
config.focus_on_activate = 1;
config.session_restore = 0;
config.session_launch_rules = NULL;
config.session_launch_rules_count = 0;
config.new_is_master = 1;
config.default_mfact = 0.55f;
config.default_nmaster = 1;
Expand Down Expand Up @@ -3511,6 +3598,8 @@ bool parse_config(void) {
config.exec_count = 0;
config.exec_once = NULL;
config.exec_once_count = 0;
config.session_launch_rules = NULL;
config.session_launch_rules_count = 0;
config.scroller_proportion_preset = NULL;
config.scroller_proportion_preset_count = 0;
config.circle_layout = NULL;
Expand Down
13 changes: 11 additions & 2 deletions src/dispatch/bind_define.h
Original file line number Diff line number Diff line change
Expand Up @@ -836,10 +836,13 @@ int32_t centerwin(const Arg *arg) {
}

int32_t spawn_shell(const Arg *arg) {
pid_t pid;

if (!arg->v)
return 0;

if (fork() == 0) {
pid = fork();
if (pid == 0) {
// 1. 忽略可能导致 coredump 的信号
signal(SIGSEGV, SIG_IGN);
signal(SIGABRT, SIG_IGN);
Expand All @@ -859,14 +862,18 @@ int32_t spawn_shell(const Arg *arg) {
arg->v, strerror(errno));
_exit(EXIT_FAILURE);
}
if (pid > 0)
mango_session_track_spawned_command(pid, arg->v);
return 0;
}

int32_t spawn(const Arg *arg) {
pid_t pid;
if (!arg->v)
return 0;

if (fork() == 0) {
pid = fork();
if (pid == 0) {
// 1. 忽略可能导致 coredump 的信号
signal(SIGSEGV, SIG_IGN);
signal(SIGABRT, SIG_IGN);
Expand All @@ -891,6 +898,8 @@ int32_t spawn(const Arg *arg) {
wordfree(&p); // 释放 wordexp 分配的内存
_exit(EXIT_FAILURE);
}
if (pid > 0)
mango_session_track_spawned_command(pid, arg->v);
return 0;
}

Expand Down
Loading