Add explicit Wayland socket selection

be3154162611498109b4409c5e95e16cc151330a
Alexis Sellier committed ago 1 parent 059c81bf
README +9 -6
76 76
# RUN
77 77
78 78
Start `swm` from a TTY, select it in a display manager, or run it nested inside
79 79
another Wayland session. `XDG_RUNTIME_DIR` must be set.
80 80
81 -
    swm [-s command]
82 -
83 -
The `-s` flag starts a command through `/bin/sh`; when swm exits it terminates
84 -
that command's process group. Programs listed in the `autostart` array in
85 -
`config.h` are started independently. Set `SWM_NO_AUTOSTART=1` to suppress that
86 -
array, which is useful for testing or a minimal session.
81 +
    swm [--socket name] [-s command]
82 +
83 +
The `--socket` option selects the Wayland display socket name. swm creates the
84 +
socket after the display backend starts. Without this option, swm selects an
85 +
available socket name. The `-s` flag starts a command through `/bin/sh`; when
86 +
swm exits it terminates that command's process group. Programs listed in the
87 +
`autostart` array in `config.h` are started independently. Set
88 +
`SWM_NO_AUTOSTART=1` to suppress that array, which is useful for testing or a
89 +
minimal session.
87 90
88 91
swm sets `WAYLAND_DISPLAY`, `DISPLAY` when Xwayland is available, and
89 92
`XDG_CURRENT_DESKTOP=swm` for programs it starts.
90 93
91 94
config.def.h +0 -2
171 171
172 172
/* Run these commands once at startup. A nullptr separates commands, and a
173 173
 * second nullptr ends the list. Environment variables are expanded without
174 174
 * shell word splitting. */
175 175
static const char *const autostart[] = {
176 -
    "dbus-update-activation-environment", "--systemd",     "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP", nullptr,
177 176
    "foot",                               "--login-shell",                                           nullptr,
178 -
    "waybar",                                                                                        nullptr,
179 177
    "swaybg",                             "-m", "tile", "-i", "$HOME/images/wallpaper.png",          nullptr,
180 178
    "sh",                                 "-c", "wl-paste --type text --watch cliphist store",       nullptr,
181 179
    "sh",                                 "-c", "wl-paste --type image --watch cliphist store",      nullptr,
182 180
    nullptr /* End the command list. */
183 181
};
swm.1.adoc +6 -1
7 7
8 8
swm - simple window manager for Wayland
9 9
10 10
== SYNOPSIS
11 11
12 -
*swm* [*-v*] [*-d*] [*-s* _command_]
12 +
*swm* [*-v*] [*-d*] [*--socket* _name_] [*-s* _command_]
13 13
14 14
== DESCRIPTION
15 15
16 16
*swm* is a simple Wayland window manager based on wlroots and forked from
17 17
*dwl*.
37 37
Write the name and version to standard error and exit unsuccessfully.
38 38
39 39
*-d*::
40 40
Enable full wlroots logging, including debug information.
41 41
42 +
*--socket* _name_::
43 +
Create the Wayland display socket with _name_. The socket is created after the
44 +
backend starts. If this option is not set, *swm* selects an available socket
45 +
name.
46 +
42 47
*-s* _command_::
43 48
Start a shell process running _command_. On exit, send `SIGTERM` to the child
44 49
process group and wait for it to stop.
45 50
46 51
== KEY BINDINGS
swm.c +30 -18
616 616
static void         save_window_states(void);
617 617
static void         request_monitor_state(struct wl_listener *listener, void *data);
618 618
static void         resize(client_t *c, struct wlr_box geo, bool interact);
619 619
static void         resize_apply(client_t *c);
620 620
static void         restore_client(client_t *c);
621 -
static void         run(char *startup_cmd);
621 +
static void         run(char *startup_cmd, const char *socket_name);
622 622
static void         set_cursor(struct wl_listener *listener, void *data);
623 623
static void         set_cursor_shape(struct wl_listener *listener, void *data);
624 624
static void         set_floating(client_t *c, bool floating);
625 625
static void         set_fullscreen(client_t *c, bool fullscreen);
626 626
static void         set_monitor(client_t *c, monitor_t *m, workspace_t *ws);
5148 5148
    client_get_clip(c, &clip);
5149 5149
    wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
5150 5150
}
5151 5151
5152 5152
/* Start the backend and child commands, then run the Wayland event loop. */
5153 -
void run(char *startup_cmd) {
5154 -
    /* Add a Unix socket to the Wayland display. */
5155 -
    const char *socket = wl_display_add_socket_auto(dpy);
5153 +
void run(char *startup_cmd, const char *socket_name) {
5154 +
    const char *socket;
5156 5155
5157 -
    if (!socket)
5158 -
        die("startup: display_add_socket_auto");
5159 -
    setenv("WAYLAND_DISPLAY", socket, 1);
5160 5156
    setenv("XDG_CURRENT_DESKTOP", "swm", 1);
5161 5157
5162 -
    /* Start the backend. This will enumerate outputs and inputs, become the DRM
5163 -
     * master, and control the display devices. */
5158 +
    /* Publish the Wayland socket only after the backend is ready. */
5164 5159
    if (!wlr_backend_start(backend))
5165 5160
        die("startup: backend_start");
5166 5161
5167 -
    /* Now that the socket exists and the backend is started, run the
5168 -
     * autostart commands and the startup command. */
5162 +
    if (socket_name) {
5163 +
        if (wl_display_add_socket(dpy, socket_name) < 0)
5164 +
            die("startup: display_add_socket");
5165 +
        socket = socket_name;
5166 +
    } else if (!(socket = wl_display_add_socket_auto(dpy))) {
5167 +
        die("startup: display_add_socket_auto");
5168 +
    }
5169 +
    setenv("WAYLAND_DISPLAY", socket, 1);
5170 +
5171 +
    /* Now that the backend and socket are ready, run the autostart commands
5172 +
     * and the startup command. */
5169 5173
    if (!getenv("SWM_NO_AUTOSTART"))
5170 5174
        autostart_exec();
5171 5175
5172 5176
    if (startup_cmd) {
5173 5177
        int piperw[2];
7124 7128
        );
7125 7129
}
7126 7130
7127 7131
/* Parse command-line options, validate the environment, and run swm. */
7128 7132
int main(int argc, char *argv[]) {
7129 -
    char *startup_cmd = nullptr;
7130 -
    int   c;
7131 -
7132 -
    while ((c = getopt(argc, argv, "s:hdv")) != -1) {
7133 -
        if (c == 's')
7133 +
    enum { OPTION_SOCKET = 256 };
7134 +
    static const struct option options[] = {
7135 +
        { "socket", required_argument, nullptr, OPTION_SOCKET },
7136 +
        { nullptr, 0, nullptr, 0 },
7137 +
    };
7138 +
    char       *startup_cmd = nullptr;
7139 +
    const char *socket_name = nullptr;
7140 +
    int         c;
7141 +
7142 +
    while ((c = getopt_long(argc, argv, "s:hdv", options, nullptr)) != -1) {
7143 +
        if (c == OPTION_SOCKET)
7144 +
            socket_name = optarg;
7145 +
        else if (c == 's')
7134 7146
            startup_cmd = optarg;
7135 7147
        else if (c == 'd')
7136 7148
            log_level = WLR_DEBUG;
7137 7149
        else if (c == 'v') {
7138 7150
            printf("swm %s\n", VERSION);
7145 7157
7146 7158
    /* Wayland needs XDG_RUNTIME_DIR to create its communication socket. */
7147 7159
    if (!getenv("XDG_RUNTIME_DIR"))
7148 7160
        die("XDG_RUNTIME_DIR must be set");
7149 7161
    setup();
7150 -
    run(startup_cmd);
7162 +
    run(startup_cmd, socket_name);
7151 7163
    cleanup();
7152 7164
    return EXIT_SUCCESS;
7153 7165
7154 7166
usage:
7155 -
    die("usage: %s [-v] [-d] [-s startup command]", argv[0]);
7167 +
    die("usage: %s [-v] [-d] [--socket name] [-s startup command]", argv[0]);
7156 7168
}
test/unit.c +2 -0
1040 1040
/* Verify version output and invalid command-line invocations. */
1041 1041
static void test_command_line_errors(void) {
1042 1042
    char *version[]         = { "swm", "-v", nullptr };
1043 1043
    char *help[]            = { "swm", "-h", nullptr };
1044 1044
    char *extra[]           = { "swm", "extra", nullptr };
1045 +
    char *missing_socket[]  = { "swm", "--socket", nullptr };
1045 1046
    char *missing_runtime[] = { "swm", nullptr };
1046 1047
1047 1048
    assert_program_exits(2, version, 0, 0); /* -v prints the version and succeeds. */
1048 1049
    assert_program_fails(2, help, 0);
1049 1050
    assert_program_fails(2, extra, 0);
1051 +
    assert_program_fails(2, missing_socket, 0);
1050 1052
    assert_program_fails(1, missing_runtime, 1);
1051 1053
}
1052 1054
1053 1055
/* Verify pointer-constraint and decoration lifecycle handlers. */
1054 1056
static void test_pointer_and_decoration_lifecycle(void) {