]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Add -S|--session <command> option
authorJohan Malm <jgm323@gmail.com>
Sat, 9 Mar 2024 17:12:54 +0000 (17:12 +0000)
committerConsolatis <35009135+Consolatis@users.noreply.github.com>
Sun, 14 Apr 2024 11:05:25 +0000 (13:05 +0200)
...to start <command> on startup and to terminate the compositor when
<command> exits.

This is useful for session management as it allows the session client (for
example `lxqt-session`) to terminate labwc - be exiting itself.

Under X, xinit starts the server and keeps it alive for as long as
lxqt-session runs. Thus either the session client starts the Window
Manager, or the Window Manager can be launched independently first.  On
Wayland, the Compositor is both Display Server and Window Manager, so the
described session management mechanisms do not work because the Compositor
needs to be running before the session can function.

As some session clients support both X11 and Wayland, this command line
option avoids re-writes and fragmentation.

Co-authored-by: @Consolatis
docs/labwc.1.scd
include/common/spawn.h
include/labwc.h
src/common/spawn.c
src/main.c
src/server.c

index 0d09620709e5151555fb457c938e1760c31d9819..2bb66572889d3ad5f00a2b12634a4c7655f44a1a 100644 (file)
@@ -56,6 +56,19 @@ the `--exit` and `--reconfigure` options use.
 *-s, --startup* <command>
        Run command on startup
 
+*-S, --session* <command>
+       Run command on startup and terminate compositor on exit. This is useful
+       for session management as it allows the session client to terminate
+       labwc by exiting itself. This is a Wayland specific use-case because
+       under X, xinit starts the server and keeps it alive for as long as the
+       session client. Thus either the session client starts the Window
+       Manager, or the Window Manager can be launched independently first. On
+       Wayland, the Compositor is both Display Server and Window Manager, so
+       the described session management mechanisms do not work because the
+       Compositor needs to be running before the session can function.  As some
+       session clients support both X11 and Wayland, this command line option
+       avoids re-writes and fragmentation.
+
 *-v, --version*
        Show the version number and quit
 
index 28557482220a9cdd96b213a7a1171e061ef3e39e..d864e9539bfe5cc3047bcf1b05f10792a5230433 100644 (file)
@@ -4,6 +4,12 @@
 
 #include <sys/types.h>
 
+/**
+ * spawn_primary_client - execute asynchronously
+ * @command: command to be executed
+ */
+pid_t spawn_primary_client(const char *command);
+
 /**
  * spawn_async_no_shell - execute asynchronously
  * @command: command to be executed
index 482ce35fa60c03a786525e440042d5a83557cbc7..e0790727b665190809a003a238c7ac8eec759231 100644 (file)
@@ -348,6 +348,8 @@ struct server {
 
        struct menu *menu_current;
        struct wl_list menus;
+
+       pid_t primary_client_pid;
 };
 
 #define LAB_NR_LAYERS (4)
index 8c0e10bafcc290ec8628318e8bf978219ac38e25..9d729e22e18f883cf82fdb5b23b2b4743f1cec6e 100644 (file)
@@ -25,11 +25,22 @@ reset_signals_and_limits(void)
        signal(SIGPIPE, SIG_DFL);
 }
 
-static void
+static bool
 set_cloexec(int fd)
 {
        int flags = fcntl(fd, F_GETFD);
-       fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+       if (flags == -1) {
+               wlr_log_errno(WLR_ERROR,
+                       "Unable to set the CLOEXEC flag: fnctl failed");
+               return false;
+       }
+       flags = flags | FD_CLOEXEC;
+       if (fcntl(fd, F_SETFD, flags) == -1) {
+               wlr_log_errno(WLR_ERROR,
+                       "Unable to set the CLOEXEC flag: fnctl failed");
+               return false;
+       }
+       return true;
 }
 
 void
@@ -79,6 +90,41 @@ out:
        g_strfreev(argv);
 }
 
+pid_t
+spawn_primary_client(const char *command)
+{
+       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;
+       }
+
+       pid_t child = fork();
+       switch (child) {
+       case -1:
+               wlr_log_errno(WLR_ERROR, "Failed to fork");
+               g_strfreev(argv);
+               return -1;
+       case 0:
+               /* child */
+               close(STDIN_FILENO);
+               reset_signals_and_limits();
+               execvp(argv[0], argv);
+               wlr_log_errno(WLR_ERROR, "Failed to execute primary client %s", command);
+               _exit(1);
+       default:
+               g_strfreev(argv);
+               return child;
+       }
+}
+
 pid_t
 spawn_piped(const char *command, int *pipe_fd)
 {
index 35b2f3fad53e2038b6ce28bc2d9512e19e478fe4..1860efa5c6fb10488c40f3a6aafcb655ed1d5472 100644 (file)
@@ -24,6 +24,7 @@ static const struct option long_options[] = {
        {"merge-config", no_argument, NULL, 'm'},
        {"reconfigure", no_argument, NULL, 'r'},
        {"startup", required_argument, NULL, 's'},
+       {"session", required_argument, NULL, 'S'},
        {"version", no_argument, NULL, 'v'},
        {"verbose", no_argument, NULL, 'V'},
        {0, 0, 0, 0}
@@ -39,6 +40,7 @@ static const char labwc_usage[] =
 "  -m, --merge-config       Merge user config files/theme in all XDG Base Dirs\n"
 "  -r, --reconfigure        Reload the compositor configuration\n"
 "  -s, --startup <command>  Run command on startup\n"
+"  -S, --session <command>  Run command on startup and terminate on exit\n"
 "  -v, --version            Show version number and quit\n"
 "  -V, --verbose            Enable more verbose logging\n";
 
@@ -87,12 +89,13 @@ main(int argc, char *argv[])
        textdomain(GETTEXT_PACKAGE);
 #endif
        char *startup_cmd = NULL;
+       char *primary_client = NULL;
        enum wlr_log_importance verbosity = WLR_ERROR;
 
        int c;
        while (1) {
                int index = 0;
-               c = getopt_long(argc, argv, "c:C:dehmrs:vV", long_options, &index);
+               c = getopt_long(argc, argv, "c:C:dehmrs:S:vV", long_options, &index);
                if (c == -1) {
                        break;
                }
@@ -118,6 +121,9 @@ main(int argc, char *argv[])
                case 's':
                        startup_cmd = optarg;
                        break;
+               case 'S':
+                       primary_client = optarg;
+                       break;
                case 'v':
                        printf("labwc " LABWC_VERSION "\n");
                        exit(0);
@@ -171,6 +177,16 @@ main(int argc, char *argv[])
 
        menu_init(&server);
 
+       /* Start session-manager if one is specified by -S|--session */
+       if (primary_client) {
+               server.primary_client_pid = spawn_primary_client(primary_client);
+               if (server.primary_client_pid < 0) {
+                       wlr_log(WLR_ERROR, "fatal error starting primary client: %s",
+                               primary_client);
+                       goto out;
+               }
+       }
+
        session_autostart_init(&server);
        if (startup_cmd) {
                spawn_async_no_shell(startup_cmd);
@@ -178,6 +194,7 @@ main(int argc, char *argv[])
 
        wl_display_run(server.wl_display);
 
+out:
        session_shutdown(&server);
 
        server_finish(&server);
index 4210f069d69a82baefd6f952f4eb00b8edaa1239..962fd5d5a361ff5c41e2bab1961e169b0a184b57 100644 (file)
@@ -90,6 +90,7 @@ handle_sigchld(int signal, void *data)
 {
        siginfo_t info;
        info.si_pid = 0;
+       struct server *server = data;
 
        /* First call waitid() with NOWAIT which doesn't consume the zombie */
        if (waitid(P_ALL, /*id*/ 0, &info, WEXITED | WNOHANG | WNOWAIT) == -1) {
@@ -102,8 +103,7 @@ handle_sigchld(int signal, void *data)
        }
 
 #if HAVE_XWAYLAND
-       /* Verify that we do not break xwayland lazy initialization */
-       struct server *server = data;
+       /* Ensure that we do not break xwayland lazy initialization */
        if (server->xwayland && server->xwayland->server
                        && info.si_pid == server->xwayland->server->pid) {
                return 0;
@@ -139,6 +139,11 @@ handle_sigchld(int signal, void *data)
                        " please report", (long)info.si_pid, info.si_code);
        }
 
+       if (info.si_pid == server->primary_client_pid) {
+               wlr_log(WLR_INFO, "primary client %ld exited", (long)info.si_pid);
+               wl_display_terminate(server->wl_display);
+       }
+
        return 0;
 }
 
@@ -290,6 +295,7 @@ get_headless_backend(struct wlr_backend *backend, void *data)
 void
 server_init(struct server *server)
 {
+       server->primary_client_pid = -1;
        server->wl_display = wl_display_create();
        if (!server->wl_display) {
                wlr_log(WLR_ERROR, "cannot allocate a wayland display");