aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md26
-rw-r--r--build.zig9
-rw-r--r--doc/river.1.scd14
-rw-r--r--river/main.zig45
4 files changed, 38 insertions, 56 deletions
diff --git a/README.md b/README.md
index 37f9b51..1a58374 100644
--- a/README.md
+++ b/README.md
@@ -10,11 +10,11 @@ and missing features. If you run into a bug don't hesitate to
## Design goals
-- Simplicity and minimalism, river should not overstep the bounds of a
-window manager.
+- Simple and predictable behavior, river should be easy to use and have a
+low cognitive load.
- Window management based on a stack of views and tags.
-- Dynamic layouts generated by external, user-written executables. (A default
-`rivertile` layout generator is provided.)
+- Dynamic layouts generated by external, user-written executables. A default
+`rivertile` layout generator is provided.
- Scriptable configuration and control through a custom wayland protocol and
separate `riverctl` binary implementing it.
@@ -53,20 +53,18 @@ To enable experimental Xwayland support pass the `-Dxwayland` option as well.
## Usage
River can either be run nested in an X11/wayland session or directly
-from a tty using KMS/DRM.
+from a tty using KMS/DRM. Simply run the `river` command.
-On startup river will look for and run an **executable** file at one of the
-following locations, checked in the order listed:
+On startup river will run an executable file at `$XDG_CONFIG_HOME/river/init`
+if such an executable exists. If $XDG_CONFIG_HOME is not set, ~/.config/ will
+be used instead.
-- `$XDG_CONFIG_HOME/river/init`
-- `$HOME/.config/river/init`
-- `/etc/river/init`
-
-Usually this executable init file will be a shell script invoking riverctl
-to create mappings and preform other configuration.
+Usually this executable is a shell script invoking *riverctl*(1) to create
+mappings, start programs such as a layout generator or status bar, and
+preform other configuration.
An example init script with sane defaults is provided [here](example/init)
-in the example directory and installed to `/etc/river/init`.
+in the example directory.
For complete documentation see the `river(1)`, `riverctl(1)`, and
`rivertile(1)` man pages.
diff --git a/build.zig b/build.zig
index d41e62f..d2f43ee 100644
--- a/build.zig
+++ b/build.zig
@@ -48,14 +48,6 @@ pub fn build(b: *zbs.Builder) !void {
const examples = b.option(bool, "examples", "Set to true to build examples") orelse false;
- const rel_config_path = if (mem.eql(u8, try fs.path.resolve(b.allocator, &[_][]const u8{b.install_prefix}), "/usr"))
- "../etc/river/init"
- else
- "etc/river/init";
- b.installFile("example/init", rel_config_path);
- const abs_config_path = try fs.path.resolve(b.allocator, &[_][]const u8{ b.install_prefix, rel_config_path });
- assert(fs.path.isAbsolute(abs_config_path));
-
const scanner = ScanProtocolsStep.create(b);
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
scanner.addSystemProtocol("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml");
@@ -72,7 +64,6 @@ pub fn build(b: *zbs.Builder) !void {
river.setTarget(target);
river.setBuildMode(mode);
river.addBuildOption(bool, "xwayland", xwayland);
- river.addBuildOption([]const u8, "default_config_path", abs_config_path);
addServerDeps(river, scanner);
diff --git a/doc/river.1.scd b/doc/river.1.scd
index 1b3e4d0..0ca73bf 100644
--- a/doc/river.1.scd
+++ b/doc/river.1.scd
@@ -32,19 +32,17 @@ utility may be used to communicate with river over these protocols.
# CONFIGURATION
-On startup river will look for and run an executable file at one of the
-following locations, checked in the order listed:
-
-- $XDG_CONFIG_HOME/river/init
-- $HOME/.config/river/init
-- /etc/river/init
+On startup river will run an executable file at `$XDG_CONFIG_HOME/river/init`
+if such an executable exists. If $XDG_CONFIG_HOME is not set, ~/.config/ will
+be used instead.
The executable init file will be run as a process group leader after river's
wayland server is initialized but before entering the main loop. On exit,
river will send SIGTERM to this process group.
-Usually this will be a shell script invoking *riverctl*(1) to create mappings,
-start programs such as a status bar, and preform other configuration.
+Usually this executable is a shell script invoking *riverctl*(1) to create
+mappings, start programs such as a layout generator or status bar, and
+preform other configuration.
# ENVIRONMENT
diff --git a/river/main.zig b/river/main.zig
index 874b518..30b2f78 100644
--- a/river/main.zig
+++ b/river/main.zig
@@ -16,6 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
+const fs = std.fs;
const os = std.os;
const wlr = @import("wlroots");
@@ -86,9 +87,7 @@ pub fn main() anyerror!void {
.warn, .err, .crit, .alert, .emerg => .err,
});
- if (startup_command == null) {
- if (try getStartupCommand()) |path| startup_command = path;
- }
+ if (startup_command == null) startup_command = try defaultInitPath();
std.log.info("initializing server", .{});
try server.init();
@@ -99,7 +98,7 @@ pub fn main() anyerror!void {
// Run the child in a new process group so that we can send SIGTERM to all
// descendants on exit.
const child_pgid = if (startup_command) |cmd| blk: {
- std.log.info("running startup command '{s}'", .{cmd});
+ std.log.info("running init executable '{s}'", .{cmd});
const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null };
const pid = try os.fork();
if (pid == 0) {
@@ -111,8 +110,9 @@ pub fn main() anyerror!void {
// Since the child has called setsid, the pid is the pgid
break :blk pid;
} else null;
- defer if (child_pgid) |pgid|
- os.kill(-pgid, os.SIGTERM) catch |e| std.log.err("failed to kill startup process: {s}", .{e});
+ defer if (child_pgid) |pgid| os.kill(-pgid, os.SIGTERM) catch |err| {
+ std.log.err("failed to kill init process group: {s}", .{@errorName(err)});
+ };
std.log.info("running server", .{});
@@ -127,29 +127,24 @@ fn printErrorExit(comptime format: []const u8, args: anytype) noreturn {
os.exit(1);
}
-fn testConfigPath(comptime fmt: []const u8, args: anytype) std.fmt.AllocPrintError!?[:0]const u8 {
- const path = try std.fmt.allocPrintZ(util.gpa, fmt, args);
- os.access(path, os.X_OK) catch {
+fn defaultInitPath() !?[:0]const u8 {
+ const path = blk: {
+ if (os.getenv("XDG_CONFIG_HOME")) |xdg_config_home| {
+ break :blk try fs.path.joinZ(util.gpa, &[_][]const u8{ xdg_config_home, "river/init" });
+ } else if (os.getenv("HOME")) |home| {
+ break :blk try fs.path.joinZ(util.gpa, &[_][]const u8{ home, ".config/river/init" });
+ } else {
+ return null;
+ }
+ };
+
+ os.accessZ(path, os.X_OK) catch |err| {
+ std.log.err("failed to run init executable {s}: {s}", .{ path, @errorName(err) });
util.gpa.free(path);
return null;
};
- return path;
-}
-fn getStartupCommand() std.fmt.AllocPrintError!?[:0]const u8 {
- if (os.getenv("XDG_CONFIG_HOME")) |xdg_config_home| {
- if (try testConfigPath("{s}/river/init", .{xdg_config_home})) |path| {
- return path;
- }
- } else if (os.getenv("HOME")) |home| {
- if (try testConfigPath("{s}/.config/river/init", .{home})) |path| {
- return path;
- }
- }
- if (try testConfigPath(build_options.default_config_path, .{})) |path| {
- return path;
- }
- return null;
+ return path;
}
pub fn log(