mirror of
https://github.com/labwc/labwc.git
synced 2026-06-13 14:33:18 -04:00
Merge 591fdf40c4 into 78227f1724
This commit is contained in:
commit
37a72a1608
10 changed files with 233 additions and 11 deletions
|
|
@ -180,6 +180,7 @@ this is for compatibility with Openbox.
|
|||
<xwaylandPersistence>no</xwaylandPersistence>
|
||||
<primarySelection>yes</primarySelection>
|
||||
<promptCommand>[see details below]</promptCommand>
|
||||
<errorCommand>[see details below]</errorCommand>
|
||||
</core>
|
||||
```
|
||||
|
||||
|
|
@ -316,6 +317,29 @@ this is for compatibility with Openbox.
|
|||
--cancel-label="%n"
|
||||
```
|
||||
|
||||
*<core><errorCommand>*
|
||||
Set command to be invoked for displaying errors in the config files,
|
||||
it is executed when errors are detected on startup and reconfigure.
|
||||
The errors are sent to STDIN of the program, and a SIGTERM is sent to
|
||||
it if the process is still running when a reconfigure is triggered.
|
||||
|
||||
The default error command is:
|
||||
```
|
||||
labnag \\
|
||||
--message 'Config Error' \\
|
||||
--button-dismiss 'Close' \\
|
||||
--layer overlay \\
|
||||
--timeout 0 \\
|
||||
--detailed-message
|
||||
```
|
||||
|
||||
Example using `zenity`:
|
||||
```
|
||||
<core>
|
||||
<errorCommand>zenity --title='Config Error' --text-info</errorCommand>
|
||||
</core>
|
||||
```
|
||||
|
||||
## PLACEMENT
|
||||
|
||||
```
|
||||
|
|
|
|||
21
include/common/log.h
Normal file
21
include/common/log.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef LABWC_LOG_H
|
||||
#define LABWC_LOG_H
|
||||
|
||||
#include <wlr/util/log.h>
|
||||
#include "common/buf.h"
|
||||
|
||||
struct buf *log_get_buf(void);
|
||||
void log_set_error(enum wlr_log_importance importance);
|
||||
void log_reset(void);
|
||||
void nag_show(void);
|
||||
void nag_show_callback(void *data);
|
||||
|
||||
#define nag_log(verb, fmt, ...) \
|
||||
do { \
|
||||
wlr_log(verb, fmt, ##__VA_ARGS__); \
|
||||
log_set_error(verb); \
|
||||
buf_add_fmt(log_get_buf(), fmt "\n", ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif /* LABWC_LOG_H */
|
||||
|
|
@ -22,6 +22,19 @@ void spawn_async_no_shell(char const *command);
|
|||
*/
|
||||
void spawn_sync_no_shell(char const *command);
|
||||
|
||||
/**
|
||||
* spawn_piped_async_no_shell - execute asynchronously
|
||||
* @command: command to be executed
|
||||
* @pipe_fd_w: set to the write end of a pipe
|
||||
* connected to stdin of the command
|
||||
* Notes:
|
||||
* The returned pid_t has to be waited for to
|
||||
* not produce zombies and the pipe_fd_w has to
|
||||
* be closed. spawn_piped_close() can be used
|
||||
* to ensure both.
|
||||
*/
|
||||
pid_t spawn_piped_async_no_shell(const char *command, int *pipe_fd_w);
|
||||
|
||||
/**
|
||||
* spawn_piped - execute asynchronously
|
||||
* @command: command to be executed
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ struct rcxml {
|
|||
bool xwayland_persistence;
|
||||
bool primary_selection;
|
||||
char *prompt_command;
|
||||
char *error_command;
|
||||
|
||||
/* placement */
|
||||
enum lab_placement_policy placement_policy;
|
||||
|
|
|
|||
19
src/action.c
19
src/action.c
|
|
@ -13,6 +13,7 @@
|
|||
#include "common/buf.h"
|
||||
#include "common/macros.h"
|
||||
#include "common/list.h"
|
||||
#include "common/log.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/parse-bool.h"
|
||||
#include "common/spawn.h"
|
||||
|
|
@ -347,7 +348,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
} else if (!strcasecmp(content, "current")) {
|
||||
action_arg_add_int(action, argument, CYCLE_WORKSPACE_CURRENT);
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
}
|
||||
goto cleanup;
|
||||
|
|
@ -360,7 +361,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
} else if (!strcasecmp(content, "focused")) {
|
||||
action_arg_add_int(action, argument, CYCLE_OUTPUT_FOCUSED);
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
}
|
||||
goto cleanup;
|
||||
|
|
@ -371,7 +372,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
} else if (!strcasecmp(content, "current")) {
|
||||
action_arg_add_int(action, argument, CYCLE_APP_ID_CURRENT);
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
}
|
||||
goto cleanup;
|
||||
|
|
@ -401,7 +402,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
if (!strcmp(argument, "direction")) {
|
||||
enum view_axis axis = view_axis_parse(content);
|
||||
if (axis == VIEW_AXIS_NONE || axis == VIEW_AXIS_INVALID) {
|
||||
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
} else {
|
||||
action_arg_add_int(action, argument, axis);
|
||||
|
|
@ -415,7 +416,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
if (mode != LAB_SSD_MODE_INVALID) {
|
||||
action_arg_add_int(action, argument, mode);
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
}
|
||||
goto cleanup;
|
||||
|
|
@ -430,7 +431,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
enum lab_edge edge = lab_edge_parse(content,
|
||||
/*tiled*/ true, /*any*/ false);
|
||||
if (edge == LAB_EDGE_NONE || edge == LAB_EDGE_CENTER) {
|
||||
wlr_log(WLR_ERROR,
|
||||
nag_log(WLR_ERROR,
|
||||
"Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
} else {
|
||||
|
|
@ -497,7 +498,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
enum lab_edge edge = lab_edge_parse(content,
|
||||
/*tiled*/ false, /*any*/ false);
|
||||
if (edge == LAB_EDGE_NONE) {
|
||||
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
} else {
|
||||
action_arg_add_int(action, argument, edge);
|
||||
|
|
@ -521,7 +522,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
enum lab_placement_policy policy =
|
||||
view_placement_parse(content);
|
||||
if (policy == LAB_PLACE_INVALID) {
|
||||
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
} else {
|
||||
action_arg_add_int(action, argument, policy);
|
||||
|
|
@ -542,7 +543,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'",
|
||||
nag_log(WLR_ERROR, "Invalid argument for action %s: '%s'",
|
||||
action_names[action->type], argument);
|
||||
|
||||
cleanup:
|
||||
|
|
|
|||
67
src/common/log.c
Normal file
67
src/common/log.c
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "common/log.h"
|
||||
#include "common/spawn.h"
|
||||
#include <stdarg.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "common/buf.h"
|
||||
#include "config/rcxml.h"
|
||||
|
||||
static struct buf log_buf = BUF_INIT;
|
||||
static bool has_error = false;
|
||||
static pid_t pid = 0;
|
||||
|
||||
struct buf *
|
||||
log_get_buf(void)
|
||||
{
|
||||
return &log_buf;
|
||||
}
|
||||
|
||||
void
|
||||
log_set_error(enum wlr_log_importance importance)
|
||||
{
|
||||
if (!has_error && importance == WLR_ERROR) {
|
||||
has_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
log_reset(void)
|
||||
{
|
||||
has_error = false;
|
||||
buf_reset(&log_buf);
|
||||
if (pid > 0) {
|
||||
if (!waitpid(pid, NULL, WNOHANG)) {
|
||||
kill(pid, SIGTERM);
|
||||
/* waitpid() is done in a generic SIGCHLD handler in src/server.c */
|
||||
}
|
||||
}
|
||||
pid = 0;
|
||||
}
|
||||
|
||||
void
|
||||
nag_show(void)
|
||||
{
|
||||
if (!has_error) {
|
||||
return;
|
||||
}
|
||||
int pipe_w;
|
||||
pid = spawn_piped_async_no_shell(rc.error_command, &pipe_w);
|
||||
if (pid > 0) {
|
||||
ssize_t bytes = write(pipe_w, log_buf.data, log_buf.len);
|
||||
if (bytes < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to write errors to process: %s",
|
||||
rc.error_command);
|
||||
}
|
||||
spawn_piped_close(pid, pipe_w);
|
||||
} else if (pid < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to launch process: %s", rc.error_command);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nag_show_callback(void *data)
|
||||
{
|
||||
nag_show();
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ labwc_sources += files(
|
|||
'font.c',
|
||||
'graphic-helpers.c',
|
||||
'lab-scene-rect.c',
|
||||
'log.c',
|
||||
'match.c',
|
||||
'mem.c',
|
||||
'nodename.c',
|
||||
|
|
|
|||
|
|
@ -155,6 +155,82 @@ spawn_primary_client(const char *command)
|
|||
}
|
||||
}
|
||||
|
||||
pid_t
|
||||
spawn_piped_async_no_shell(const char *command, int *pipe_fd_w)
|
||||
{
|
||||
assert(command);
|
||||
|
||||
GError *err = NULL;
|
||||
gchar **argv = NULL;
|
||||
|
||||
/* Use glib's shell-parse to mimic Openbox's behaviour */
|
||||
g_shell_parse_argv((gchar *)command, NULL, &argv, &err);
|
||||
if (err) {
|
||||
g_message("%s", err->message);
|
||||
g_error_free(err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pipe_rw[2];
|
||||
if (pipe(pipe_rw) != 0) {
|
||||
wlr_log(WLR_ERROR, "unable to pipe()");
|
||||
g_strfreev(argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid_t child = 0;
|
||||
child = fork();
|
||||
if (child < 0) {
|
||||
wlr_log(WLR_ERROR, "unable to fork() child");
|
||||
close(pipe_rw[0]);
|
||||
close(pipe_rw[1]);
|
||||
g_strfreev(argv);
|
||||
return child;
|
||||
}
|
||||
|
||||
if (child == 0) {
|
||||
/* Child */
|
||||
reset_signals_and_limits();
|
||||
|
||||
/*
|
||||
* replace stdout and stderr with /dev/null
|
||||
* and stdin with the read end of the pipe
|
||||
*/
|
||||
close(pipe_rw[1]);
|
||||
dup2(pipe_rw[0], STDIN_FILENO);
|
||||
close(pipe_rw[0]);
|
||||
|
||||
int dev_null = open("/dev/null", O_WRONLY);
|
||||
if (dev_null < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "opening /dev/null failed");
|
||||
/*
|
||||
* Just close stdout and stderr and
|
||||
* hope $command can deal with that.
|
||||
*/
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
} else {
|
||||
dup2(dev_null, STDOUT_FILENO);
|
||||
dup2(dev_null, STDERR_FILENO);
|
||||
close(dev_null);
|
||||
}
|
||||
execvp(argv[0], argv);
|
||||
_exit(1);
|
||||
}
|
||||
/* labwc */
|
||||
close(pipe_rw[0]);
|
||||
g_strfreev(argv);
|
||||
|
||||
/*
|
||||
* Prevent leaking the write end of the pipe to further
|
||||
* children forked during the lifetime of the descriptor.
|
||||
*/
|
||||
set_cloexec(pipe_rw[1]);
|
||||
*pipe_fd_w = pipe_rw[1];
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
pid_t
|
||||
spawn_piped(const char *command, int *pipe_fd)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "common/buf.h"
|
||||
#include "common/dir.h"
|
||||
#include "common/list.h"
|
||||
#include "common/log.h"
|
||||
#include "common/macros.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/nodename.h"
|
||||
|
|
@ -1167,6 +1168,8 @@ entry(xmlNode *node, char *nodename, char *content)
|
|||
|
||||
} else if (!strcasecmp(nodename, "promptCommand.core")) {
|
||||
xstrdup_replace(rc.prompt_command, content);
|
||||
} else if (!strcasecmp(nodename, "errorCommand.core")) {
|
||||
xstrdup_replace(rc.error_command, content);
|
||||
|
||||
} else if (!strcmp(nodename, "policy.placement")) {
|
||||
enum lab_placement_policy policy = view_placement_parse(content);
|
||||
|
|
@ -1476,7 +1479,7 @@ rcxml_parse_xml(struct buf *b)
|
|||
int options = 0;
|
||||
xmlDoc *d = xmlReadMemory(b->data, b->len, NULL, NULL, options);
|
||||
if (!d) {
|
||||
wlr_log(WLR_ERROR, "error parsing config file");
|
||||
nag_log(WLR_ERROR, "error parsing config file");
|
||||
return;
|
||||
}
|
||||
xmlNode *root = xmlDocGetRootElement(d);
|
||||
|
|
@ -1802,6 +1805,15 @@ post_processing(void)
|
|||
"--layer overlay "
|
||||
"--timeout 0");
|
||||
}
|
||||
if (!rc.error_command) {
|
||||
rc.error_command =
|
||||
xstrdup("labnag "
|
||||
"--message 'Config Error' "
|
||||
"--button-dismiss 'Close' "
|
||||
"--layer overlay "
|
||||
"--timeout 0 "
|
||||
"--detailed-message");
|
||||
}
|
||||
if (!rc.fallback_app_icon_name) {
|
||||
rc.fallback_app_icon_name = xstrdup("labwc");
|
||||
}
|
||||
|
|
@ -2030,7 +2042,7 @@ rcxml_read(const char *filename)
|
|||
continue;
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO, "read config file %s", path->string);
|
||||
nag_log(WLR_INFO, "read config file %s", path->string);
|
||||
|
||||
rcxml_parse_xml(&b);
|
||||
buf_reset(&b);
|
||||
|
|
@ -2052,6 +2064,7 @@ rcxml_finish(void)
|
|||
zfree(rc.font_menuitem.name);
|
||||
zfree(rc.font_osd.name);
|
||||
zfree(rc.prompt_command);
|
||||
zfree(rc.error_command);
|
||||
zfree(rc.theme_name);
|
||||
zfree(rc.icon_theme_name);
|
||||
zfree(rc.fallback_app_icon_name);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#endif
|
||||
|
||||
#include "action.h"
|
||||
#include "common/log.h"
|
||||
#include "common/macros.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/scene-helpers.h"
|
||||
|
|
@ -98,6 +99,7 @@ reload_config_and_theme(void)
|
|||
|
||||
scaled_buffer_invalidate_sharing();
|
||||
rcxml_finish();
|
||||
log_reset();
|
||||
rcxml_read(rc.config_file);
|
||||
theme_finish(rc.theme);
|
||||
theme_init(rc.theme, rc.theme_name);
|
||||
|
|
@ -119,6 +121,8 @@ reload_config_and_theme(void)
|
|||
resize_indicator_reconfigure();
|
||||
kde_server_decoration_update_default();
|
||||
workspaces_reconfigure();
|
||||
|
||||
wl_event_loop_add_idle(server.wl_event_loop, nag_show_callback, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -809,6 +813,7 @@ server_init(void)
|
|||
#if HAVE_XWAYLAND
|
||||
xwayland_server_init(server.compositor);
|
||||
#endif
|
||||
wl_event_loop_add_idle(server.wl_event_loop, nag_show_callback, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue