Add explicit Wayland socket selection
be3154162611498109b4409c5e95e16cc151330a
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) { |