mirror of
https://github.com/emersion/kanshi
synced 2024-09-18 09:51:36 +02:00
Add exec
to execute commands when a configuration is matched
This commit is contained in:
parent
15029bd28b
commit
5a30abdf0b
@ -29,11 +29,17 @@ struct kanshi_profile_output {
|
||||
enum wl_output_transform transform;
|
||||
};
|
||||
|
||||
struct kanshi_profile_command {
|
||||
struct wl_list link;
|
||||
char *command;
|
||||
};
|
||||
|
||||
struct kanshi_profile {
|
||||
struct wl_list link;
|
||||
char *name;
|
||||
// Wildcard outputs are stored at the end of the list
|
||||
struct wl_list outputs;
|
||||
struct wl_list commands;
|
||||
};
|
||||
|
||||
struct kanshi_config {
|
||||
|
31
kanshi.5.scd
31
kanshi.5.scd
@ -35,6 +35,37 @@ Directives are followed by space-separated arguments. Arguments can be quoted
|
||||
On *sway*(1), output names and descriptions can be obtained via
|
||||
*swaymsg -t get_outputs*.
|
||||
|
||||
*exec* <command>
|
||||
An exec directive executes a command when the profile was successfully
|
||||
applied. This can be used to update the compositor state to the profile
|
||||
when not done automatically.
|
||||
|
||||
Commands are executed asynchronously and their order may not be preserved.
|
||||
If you need to execute sequential commands, you should collect in one exec
|
||||
statement or in a separate script.
|
||||
|
||||
On *sway*(1) for example, *exec* can be used to move workspaces to the
|
||||
right output:
|
||||
|
||||
```
|
||||
multihead {
|
||||
output eDP-1 enable
|
||||
output DP-1 enable transform 270
|
||||
exec swaymsg workspace 1, move workspace to eDP-1
|
||||
}
|
||||
```
|
||||
|
||||
Note that some extra care must be taken with outputs identified by an
|
||||
output description as the real name may change:
|
||||
|
||||
```
|
||||
complex {
|
||||
output "Some Other Company GTBZ 2525" mode 1920x1200
|
||||
exec swaymsg workspace 1, move workspace to output '"Some Other Company GTBZ 2525"'
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# OUTPUT DIRECTIVES
|
||||
|
||||
*enable*|*disable*
|
||||
|
53
main.c
53
main.c
@ -1,8 +1,12 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "config.h"
|
||||
@ -71,10 +75,59 @@ static struct kanshi_profile *match(struct kanshi_state *state,
|
||||
}
|
||||
|
||||
|
||||
static void exec_command(char *cmd) {
|
||||
pid_t pid, child;
|
||||
if ((pid = fork()) == 0) {
|
||||
setsid();
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
if ((child = fork()) == 0) {
|
||||
execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
|
||||
fprintf(stderr, "Executing command '%s' failed: %s", cmd, strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
if (child < 0) {
|
||||
fprintf(stderr, "Impossible to fork a new process to execute"
|
||||
" command '%s': %s", cmd, strerror(errno));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Try to give some meaningful information on the command success
|
||||
int wstatus;
|
||||
if (waitpid(child, &wstatus, 0) != child) {
|
||||
perror("waitpid");
|
||||
exit(0);
|
||||
}
|
||||
if (WIFEXITED(wstatus)) {
|
||||
fprintf(stderr, "Command '%s' returned with exit status %d.\n",
|
||||
cmd, WEXITSTATUS(wstatus));
|
||||
} else {
|
||||
fprintf(stderr, "Command '%s' was killed, aborted or disappeared"
|
||||
" in dire circumstances.\n", cmd);
|
||||
}
|
||||
exit(0); // Close child process
|
||||
}
|
||||
|
||||
if (pid < 0) {
|
||||
perror("Impossible to fork a new process");
|
||||
}
|
||||
}
|
||||
|
||||
static void execute_profile_commands(struct kanshi_profile *profile) {
|
||||
struct kanshi_profile_command *command;
|
||||
wl_list_for_each(command, &profile->commands, link) {
|
||||
fprintf(stderr, "Running command '%s'\n", command->command);
|
||||
exec_command(command->command);
|
||||
}
|
||||
}
|
||||
|
||||
static void config_handle_succeeded(void *data,
|
||||
struct zwlr_output_configuration_v1 *config) {
|
||||
struct kanshi_pending_profile *pending = data;
|
||||
zwlr_output_configuration_v1_destroy(config);
|
||||
fprintf(stderr, "running commands for configuration '%s'\n", pending->profile->name);
|
||||
execute_profile_commands(pending->profile);
|
||||
fprintf(stderr, "configuration for profile '%s' applied\n",
|
||||
pending->profile->name);
|
||||
pending->state->current_profile = pending->profile;
|
||||
|
49
parser.c
49
parser.c
@ -91,6 +91,24 @@ static bool parser_read_quoted(struct kanshi_parser *parser) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool parser_read_line(struct kanshi_parser *parser) {
|
||||
while (1) {
|
||||
int ch = parser_peek_char(parser);
|
||||
if (ch < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ch == '\n' || ch == '\0') {
|
||||
parser->tok_str[parser->tok_str_len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parser_append_tok_ch(parser, parser_read_char(parser))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool parser_read_str(struct kanshi_parser *parser) {
|
||||
while (1) {
|
||||
int ch = parser_peek_char(parser);
|
||||
@ -338,9 +356,32 @@ static struct kanshi_profile_output *parse_profile_output(
|
||||
}
|
||||
}
|
||||
|
||||
static struct kanshi_profile_command *parse_profile_command(
|
||||
struct kanshi_parser *parser) {
|
||||
// Skip the 'exec' directive.
|
||||
if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!parser_read_line(parser)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (parser->tok_str_len <= 0) {
|
||||
fprintf(stderr, "Ignoring empty command in config file on line %d\n",
|
||||
parser->line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct kanshi_profile_command *command = calloc(1, sizeof(*command));
|
||||
command->command = strdup(parser->tok_str);
|
||||
return command;
|
||||
}
|
||||
|
||||
static struct kanshi_profile *parse_profile(struct kanshi_parser *parser) {
|
||||
struct kanshi_profile *profile = calloc(1, sizeof(*profile));
|
||||
wl_list_init(&profile->outputs);
|
||||
wl_list_init(&profile->commands);
|
||||
|
||||
// First parse an optional profile name
|
||||
parser->tok_str_len = 0;
|
||||
@ -391,6 +432,14 @@ static struct kanshi_profile *parse_profile(struct kanshi_parser *parser) {
|
||||
} else {
|
||||
wl_list_insert(&profile->outputs, &output->link);
|
||||
}
|
||||
} else if (strcmp(directive, "exec") == 0) {
|
||||
struct kanshi_profile_command *command =
|
||||
parse_profile_command(parser);
|
||||
if (command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Insert commands at the end to preserve order
|
||||
wl_list_insert(profile->commands.prev, &command->link);
|
||||
} else {
|
||||
fprintf(stderr, "unknown directive '%s' in profile '%s'\n",
|
||||
directive, profile->name);
|
||||
|
Loading…
Reference in New Issue
Block a user