Labwc uses openbox-3.6 specification for configuration and theming, but does not
support all options. The following files form the basis of the labwc
-configuration: rc.xml, menu.xml, autostart and environment.
+configuration: rc.xml, menu.xml, autostart, shutdown and environment.
No configuration files are needed to start and run labwc.
The configuration directory location can be override with the -C command line
option.
-All configuration and theme files except autostart are re-loaded on receiving
-signal SIGHUP.
-
-The *autostart* file is executed as a shell script. This is the place for
-executing clients for handling background images, panels and similar.
+All configuration and theme files except autostart and shutdown are re-loaded on
+receiving signal SIGHUP.
The *environment* file is parsed as *variable=value* and sets environment
variables accordingly. It is recommended to specify keyboard layout settings and
Note: Tilde (~) and environment variables in the value are expanded, but
subshell syntax and apostrophes are ignored.
+The *autostart* file is executed as a shell script after labwc has read its
+configuration and set variables defined in the environment file. Additionally,
+the environment variables WAYLAND_DISPLAY and (when labwc is built with Xwayland
+support) DISPLAY will be defined. This is the place for executing clients for
+handling background images, panels and other tasks that should run automatically
+when labwc launches.
+
+The *shutdown* file is executed as a shell script when labwc is preparing to
+terminate itself. All environment variables, including WAYLAND_DISPLAY and
+DISPLAY, will be available to the script. However, because the script runs
+asynchronously with other termination tasks, the shutdown file should not assume
+that the display will be usable. This file is useful to perform any custom
+operations necessary to finalize a labwc session.
+
The *menu.xml* file defines the context/root-menus and is described in
labwc-menu(5).
*/
char *strdup_printf(const char *fmt, ...);
+/**
+ * str_join - format and join an array of strings with a separator
+ * @parts: NULL-terminated array of string parts to be joined
+ * @fmt: printf-style format string applied to each part
+ * @sep: separator inserted between parts when joining
+ *
+ * A new string is allocated to hold the joined result. The user must free the
+ * returned string. Returns NULL on error.
+ *
+ * Each part of the array is converted via the equivalent of sprintf(output,
+ * fmt, part), so fmt should include a single "%s" format specification. If fmt
+ * is NULL, a default "%s" will be used to copy each part verbatim.
+ *
+ * The separator is arbitrary. When the separator is NULL, a single space will
+ * be used.
+ */
+char *str_join(const char * const parts[],
+ const char *restrict fmt, const char *restrict sep);
+
#endif /* LABWC_STRING_HELPERS_H */
*/
void session_autostart_init(void);
+/**
+ * session_shutdown - run session shutdown file as shell script
+ * Note: Same as `sh ~/.config/labwc/shutdown` (or equivalent XDG config dir)
+ */
+void session_shutdown(void);
+
#endif /* LABWC_SESSION_H */
// SPDX-License-Identifier: GPL-2.0-only
+#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
}
return p;
}
+
+char *
+str_join(const char * const parts[],
+ const char *restrict fmt, const char *restrict sep)
+{
+ assert(parts);
+
+ if (!fmt) {
+ fmt = "%s";
+ }
+
+ if (!sep) {
+ sep = " ";
+ }
+
+ size_t size = 0;
+ size_t n_parts = 0;
+
+ size_t sep_len = strlen(sep);
+
+ /* Count the length of each formatted string */
+ for (const char *const *s = parts; *s; ++s) {
+ int n = snprintf(NULL, 0, fmt, *s);
+ if (n < 0) {
+ return NULL;
+ }
+ size += (size_t)n;
+ ++n_parts;
+ }
+
+ if (n_parts < 1) {
+ return NULL;
+ }
+
+ /* Need (n_parts - 1) separators, plus one NULL terminator */
+ size += (n_parts - 1) * sep_len + 1;
+
+ /* Concatenate the strings and separators */
+ char *buf = xzalloc(size);
+ char *p = buf;
+ for (const char *const *s = parts; *s; ++s) {
+ int n = 0;
+
+ if (p != buf) {
+ n = snprintf(p, size, "%s", sep);
+ if (n < 0 || (size_t)n >= size) {
+ p = NULL;
+ break;
+ }
+ size -= (size_t)n;
+ p += (size_t)n;
+ }
+
+ n = snprintf(p, size, fmt, *s);
+ if (n < 0 || (size_t)n >= size) {
+ p = NULL;
+ break;
+ }
+ size -= (size_t)n;
+ p += (size_t)n;
+ }
+
+ if (!p) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "common/buf.h"
#include "common/dir.h"
#include "common/file-helpers.h"
+#include "common/mem.h"
#include "common/spawn.h"
#include "common/string-helpers.h"
#include "config/session.h"
#include "labwc.h"
+static const char *const env_vars[] = {
+ "DISPLAY",
+ "WAYLAND_DISPLAY",
+ "XDG_CURRENT_DESKTOP",
+ NULL,
+};
+
static void
process_line(char *line)
{
}
static void
-update_activation_env(const char *env_keys)
+update_activation_env(bool initialize)
{
if (!getenv("DBUS_SESSION_BUS_ADDRESS")) {
/* Prevent accidentally auto-launching a dbus session */
}
wlr_log(WLR_INFO, "Updating dbus execution environment");
- char *cmd = strdup_printf("dbus-update-activation-environment %s", env_keys);
+ char *env_keys = str_join(env_vars, "%s", " ");
+ char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " ");
+
+ char *cmd =
+ strdup_printf("dbus-update-activation-environment %s",
+ initialize ? env_keys : env_unset_keys);
spawn_async_no_shell(cmd);
free(cmd);
- cmd = strdup_printf("systemctl --user import-environment %s", env_keys);
+ cmd = strdup_printf("systemctl --user %s %s",
+ initialize ? "import-environment" : "unset-environment", env_keys);
spawn_async_no_shell(cmd);
free(cmd);
+
+ free(env_keys);
+ free(env_unset_keys);
}
void
paths_destroy(&paths);
}
-void
-session_autostart_init(void)
+static void
+run_session_script(const char *script)
{
- /* Update dbus and systemd user environment, each may fail gracefully */
- update_activation_env("DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP");
-
struct wl_list paths;
- paths_config_create(&paths, "autostart");
+ paths_config_create(&paths, script);
bool should_merge_config = rc.merge_config;
struct wl_list *(*iter)(struct wl_list *list);
if (!file_exists(path->string)) {
continue;
}
- wlr_log(WLR_INFO, "run autostart file %s", path->string);
+ wlr_log(WLR_INFO, "run session script %s", path->string);
char *cmd = strdup_printf("sh %s", path->string);
spawn_async_no_shell(cmd);
free(cmd);
}
paths_destroy(&paths);
}
+
+void
+session_autostart_init(void)
+{
+ /* Update dbus and systemd user environment, each may fail gracefully */
+ update_activation_env(/* initialize */ true);
+ run_session_script("autostart");
+}
+
+void
+session_shutdown(void)
+{
+ run_session_script("shutdown");
+
+ /* Clear the dbus and systemd user environment, each may fail gracefully */
+ update_activation_env(/* initialize */ false);
+}
wl_display_run(server.wl_display);
+ session_shutdown();
+
server_finish(&server);
menu_finish(&server);