Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
river: remove river-control-unstable-v1
river/Control.zig | 143 -----------
river/Cursor.zig | 3 +-
river/Seat.zig | 26 +-
river/Server.zig | 5 -
river/command.zig | 170 -------------
river/command/attach_mode.zig | 61 -----
river/command/close.zig | 31 ---
river/command/config.zig | 119 ---------
river/command/cursor.zig | 51 ----
river/command/declare_mode.zig | 49 ----
river/command/enter_mode.zig | 63 -----
river/command/exit.zig | 32 ---
river/command/focus_follows_cursor.zig | 35 ---
river/command/input.zig | 121 ---------
river/command/keyboard.zig | 108 --------
river/command/keyboard_group.zig | 100 --------
river/command/layout.zig | 85 -------
river/command/map.zig | 437 ---------------------------------
river/command/move.zig | 150 -----------
river/command/output.zig | 135 ----------
river/command/rule.zig | 265 --------------------
river/command/set_repeat.zig | 45 ----
river/command/spawn.zig | 62 -----
river/command/tags.zig | 144 -----------
river/command/toggle_float.zig | 47 ----
river/command/toggle_fullscreen.zig | 38 ---
river/command/view_operations.zig | 145 -----------
river/command/xcursor_theme.zig | 34 ---
river/command/zoom.zig | 85 -------
29 files changed, 3 insertions(+), 2786 deletions(-)
diff --git a/river/Control.zig b/river/Control.zig
deleted file mode 100644
index 2b0d21f..0000000
--- a/river/Control.zig
+++ /dev/null
@@ -1,143 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const Control = @This();
-
-const std = @import("std");
-const mem = std.mem;
-const wlr = @import("wlroots");
-const wayland = @import("wayland");
-const wl = wayland.server.wl;
-const zriver = wayland.server.zriver;
-
-const command = @import("command.zig");
-const server = &@import("main.zig").server;
-const util = @import("util.zig");
-
-const Seat = @import("Seat.zig");
-const Server = @import("Server.zig");
-
-const ArgMap = std.AutoHashMap(struct { client: *wl.Client, id: u32 }, std.ArrayListUnmanaged([:0]const u8));
-
-global: *wl.Global,
-
-args_map: ArgMap,
-
-server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleServerDestroy),
-
-pub fn init(control: *Control) !void {
- control.* = .{
- .global = try wl.Global.create(server.wl_server, zriver.ControlV1, 1, *Control, control, bind),
- .args_map = ArgMap.init(util.gpa),
- };
-
- server.wl_server.addDestroyListener(&control.server_destroy);
-}
-
-fn handleServerDestroy(listener: *wl.Listener(*wl.Server), _: *wl.Server) void {
- const control: *Control = @fieldParentPtr("server_destroy", listener);
- control.global.destroy();
- control.args_map.deinit();
-}
-
-/// Called when a client binds our global
-fn bind(client: *wl.Client, control: *Control, version: u32, id: u32) void {
- const control_v1 = zriver.ControlV1.create(client, version, id) catch {
- client.postNoMemory();
- return;
- };
- control.args_map.putNoClobber(.{ .client = client, .id = id }, .{}) catch {
- control_v1.destroy();
- client.postNoMemory();
- return;
- };
- control_v1.setHandler(*Control, handleRequest, handleDestroy, control);
-}
-
-fn handleRequest(control_v1: *zriver.ControlV1, request: zriver.ControlV1.Request, control: *Control) void {
- switch (request) {
- .destroy => control_v1.destroy(),
- .add_argument => |add_argument| {
- const owned_slice = util.gpa.dupeZ(u8, mem.sliceTo(add_argument.argument, 0)) catch {
- control_v1.getClient().postNoMemory();
- return;
- };
-
- const args = control.args_map.getPtr(.{ .client = control_v1.getClient(), .id = control_v1.getId() }).?;
- args.append(util.gpa, owned_slice) catch {
- control_v1.getClient().postNoMemory();
- util.gpa.free(owned_slice);
- return;
- };
- },
- .run_command => |run_command| {
- const seat: *Seat = @ptrFromInt(wlr.Seat.Client.fromWlSeat(run_command.seat).?.seat.data);
-
- const callback = zriver.CommandCallbackV1.create(
- control_v1.getClient(),
- control_v1.getVersion(),
- run_command.callback,
- ) catch {
- control_v1.getClient().postNoMemory();
- return;
- };
-
- const args = control.args_map.getPtr(.{ .client = control_v1.getClient(), .id = control_v1.getId() }).?;
- defer {
- for (args.items) |arg| util.gpa.free(arg);
- args.items.len = 0;
- }
-
- var out: ?[]const u8 = null;
- defer if (out) |s| util.gpa.free(s);
- command.run(seat, args.items, &out) catch |err| {
- const failure_message = switch (err) {
- command.Error.OutOfMemory => {
- callback.getClient().postNoMemory();
- return;
- },
- command.Error.Other => util.gpa.dupeZ(u8, out.?) catch {
- callback.getClient().postNoMemory();
- return;
- },
- else => command.errToMsg(err),
- };
- defer if (err == command.Error.Other) util.gpa.free(failure_message);
- callback.destroySendFailure(failure_message);
- return;
- };
-
- const success_message = if (out) |s|
- util.gpa.dupeZ(u8, s) catch {
- callback.getClient().postNoMemory();
- return;
- }
- else
- "";
- defer if (out != null) util.gpa.free(success_message);
- callback.destroySendSuccess(success_message);
- },
- }
-}
-
-/// Remove the resource from the hash map and free all stored args
-fn handleDestroy(control_v1: *zriver.ControlV1, control: *Control) void {
- var args = control.args_map.fetchRemove(
- .{ .client = control_v1.getClient(), .id = control_v1.getId() },
- ).?.value;
- for (args.items) |arg| util.gpa.free(arg);
- args.deinit(util.gpa);
-}
diff --git a/river/Cursor.zig b/river/Cursor.zig
index 235f29c..d48da99 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -666,9 +666,8 @@ fn handlePointerMapping(cursor: *Cursor, event: *wlr.Pointer.event.Button, view:
switch (mapping.action) {
.move => if (!fullscreen) cursor.startMove(view),
.resize => if (!fullscreen) cursor.startResize(view, null),
- .command => |args| {
+ .command => |_| {
cursor.seat.focus(view);
- cursor.seat.runCommand(args);
// This is mildly inefficient as running the command may have already
// started a transaction. However we need to start one after the Seat.focus()
// call in the case where it didn't.
diff --git a/river/Seat.zig b/river/Seat.zig
index 257c72d..0e5638e 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -23,7 +23,6 @@ const wlr = @import("wlroots");
const wl = @import("wayland").server.wl;
const xkb = @import("xkbcommon");
-const command = @import("command.zig");
const server = &@import("main.zig").server;
const util = @import("util.zig");
@@ -397,7 +396,6 @@ pub fn handleMapping(
log.err("failed to update mapping repeat timer", .{});
};
}
- seat.runCommand(mapping.command_args);
return true;
}
@@ -413,30 +411,11 @@ pub fn handleSwitchMapping(
const modes = &server.config.modes;
for (modes.items[seat.mode_id].switch_mappings.items) |mapping| {
if (std.meta.eql(mapping.switch_type, switch_type) and std.meta.eql(mapping.switch_state, switch_state)) {
- seat.runCommand(mapping.command_args);
+ // send trigger
}
}
}
-pub fn runCommand(seat: *Seat, args: []const [:0]const u8) void {
- var out: ?[]const u8 = null;
- defer if (out) |s| util.gpa.free(s);
- command.run(seat, args, &out) catch |err| {
- const failure_message = switch (err) {
- command.Error.Other => out.?,
- else => command.errToMsg(err),
- };
- std.log.scoped(.command).err("{s}: {s}", .{ args[0], failure_message });
- return;
- };
- if (out) |s| {
- const stdout = std.io.getStdOut().writer();
- stdout.print("{s}", .{s}) catch |err| {
- std.log.scoped(.command).err("{s}: write to stdout failed {}", .{ args[0], err });
- };
- }
-}
-
pub fn clearRepeatingMapping(seat: *Seat) void {
seat.mapping_repeat_timer.timerUpdate(0) catch {
log.err("failed to clear mapping repeat timer", .{});
@@ -446,13 +425,12 @@ pub fn clearRepeatingMapping(seat: *Seat) void {
/// Repeat key mapping
fn handleMappingRepeatTimeout(seat: *Seat) c_int {
- if (seat.repeating_mapping) |mapping| {
+ if (seat.repeating_mapping) |_| {
const rate = server.config.repeat_rate;
const ms_delay = if (rate > 0) 1000 / rate else 0;
seat.mapping_repeat_timer.timerUpdate(ms_delay) catch {
log.err("failed to update mapping repeat timer", .{});
};
- seat.runCommand(mapping.command_args);
}
return 0;
}
diff --git a/river/Server.zig b/river/Server.zig
index 6a25d34..f0d626e 100644
--- a/river/Server.zig
+++ b/river/Server.zig
@@ -27,7 +27,6 @@ const c = @import("c.zig");
const util = @import("util.zig");
const Config = @import("Config.zig");
-const Control = @import("Control.zig");
const IdleInhibitManager = @import("IdleInhibitManager.zig");
const InputManager = @import("InputManager.zig");
const LayerSurface = @import("LayerSurface.zig");
@@ -86,7 +85,6 @@ foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
input_manager: InputManager,
root: Root,
config: Config,
-control: Control,
layout_manager: LayoutManager,
idle_inhibit_manager: IdleInhibitManager,
lock_manager: LockManager,
@@ -159,7 +157,6 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
.root = undefined,
.input_manager = undefined,
- .control = undefined,
.layout_manager = undefined,
.idle_inhibit_manager = undefined,
.lock_manager = undefined,
@@ -182,7 +179,6 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
try server.root.init();
try server.input_manager.init();
- try server.control.init();
try server.layout_manager.init();
try server.idle_inhibit_manager.init();
try server.lock_manager.init();
@@ -320,7 +316,6 @@ fn blocklist(server: *Server, global: *const wl.Global) bool {
global == server.export_dmabuf_manager.global or
global == server.data_control_manager.global or
global == server.layout_manager.global or
- global == server.control.global or
global == server.root.output_manager.global or
global == server.root.power_manager.global or
global == server.root.gamma_control_manager.global or
diff --git a/river/command.zig b/river/command.zig
deleted file mode 100644
index 6ea5b96..0000000
--- a/river/command.zig
+++ /dev/null
@@ -1,170 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const assert = std.debug.assert;
-
-const Seat = @import("Seat.zig");
-
-pub const Direction = enum {
- next,
- previous,
-};
-
-pub const PhysicalDirection = enum {
- up,
- down,
- left,
- right,
-};
-
-pub const Orientation = enum {
- horizontal,
- vertical,
-};
-
-const command_impls = std.StaticStringMap(
- *const fn (*Seat, []const [:0]const u8, *?[]const u8) Error!void,
-).initComptime(
- .{
- // zig fmt: off
- .{ "attach-mode", @import("command/attach_mode.zig").defaultAttachMode },
- .{ "background-color", @import("command/config.zig").backgroundColor },
- .{ "border-color-focused", @import("command/config.zig").borderColorFocused },
- .{ "border-color-unfocused", @import("command/config.zig").borderColorUnfocused },
- .{ "border-color-urgent", @import("command/config.zig").borderColorUrgent },
- .{ "border-width", @import("command/config.zig").borderWidth },
- .{ "close", @import("command/close.zig").close },
- .{ "declare-mode", @import("command/declare_mode.zig").declareMode },
- .{ "default-attach-mode", @import("command/attach_mode.zig").defaultAttachMode },
- .{ "default-layout", @import("command/layout.zig").defaultLayout },
- .{ "enter-mode", @import("command/enter_mode.zig").enterMode },
- .{ "exit", @import("command/exit.zig").exit },
- .{ "focus-follows-cursor", @import("command/focus_follows_cursor.zig").focusFollowsCursor },
- .{ "focus-output", @import("command/output.zig").focusOutput },
- .{ "focus-previous-tags", @import("command/tags.zig").focusPreviousTags },
- .{ "focus-view", @import("command/view_operations.zig").focusView },
- .{ "hide-cursor", @import("command/cursor.zig").cursor },
- .{ "input", @import("command/input.zig").input },
- .{ "keyboard-group-add", @import("command/keyboard_group.zig").keyboardGroupAdd },
- .{ "keyboard-group-create", @import("command/keyboard_group.zig").keyboardGroupCreate },
- .{ "keyboard-group-destroy", @import("command/keyboard_group.zig").keyboardGroupDestroy },
- .{ "keyboard-group-remove", @import("command/keyboard_group.zig").keyboardGroupRemove },
- .{ "keyboard-layout", @import("command/keyboard.zig").keyboardLayout },
- .{ "keyboard-layout-file", @import("command/keyboard.zig").keyboardLayoutFile },
- .{ "list-input-configs", @import("command/input.zig").listInputConfigs},
- .{ "list-inputs", @import("command/input.zig").listInputs },
- .{ "list-rules", @import("command/rule.zig").listRules},
- .{ "map", @import("command/map.zig").map },
- .{ "map-pointer", @import("command/map.zig").mapPointer },
- .{ "map-switch", @import("command/map.zig").mapSwitch },
- .{ "move", @import("command/move.zig").move },
- .{ "output-attach-mode", @import("command/attach_mode.zig").outputAttachMode },
- .{ "output-layout", @import("command/layout.zig").outputLayout },
- .{ "resize", @import("command/move.zig").resize },
- .{ "rule-add", @import("command/rule.zig").ruleAdd },
- .{ "rule-del", @import("command/rule.zig").ruleDel },
- .{ "send-layout-cmd", @import("command/layout.zig").sendLayoutCmd },
- .{ "send-to-output", @import("command/output.zig").sendToOutput },
- .{ "send-to-previous-tags", @import("command/tags.zig").sendToPreviousTags },
- .{ "set-cursor-warp", @import("command/config.zig").setCursorWarp },
- .{ "set-focused-tags", @import("command/tags.zig").setFocusedTags },
- .{ "set-repeat", @import("command/set_repeat.zig").setRepeat },
- .{ "set-view-tags", @import("command/tags.zig").setViewTags },
- .{ "snap", @import("command/move.zig").snap },
- .{ "spawn", @import("command/spawn.zig").spawn },
- .{ "spawn-tagmask", @import("command/tags.zig").spawnTagmask },
- .{ "swap", @import("command/view_operations.zig").swap},
- .{ "toggle-float", @import("command/toggle_float.zig").toggleFloat },
- .{ "toggle-focused-tags", @import("command/tags.zig").toggleFocusedTags },
- .{ "toggle-fullscreen", @import("command/toggle_fullscreen.zig").toggleFullscreen },
- .{ "toggle-view-tags", @import("command/tags.zig").toggleViewTags },
- .{ "unmap", @import("command/map.zig").unmap },
- .{ "unmap-pointer", @import("command/map.zig").unmapPointer },
- .{ "unmap-switch", @import("command/map.zig").unmapSwitch },
- .{ "xcursor-theme", @import("command/xcursor_theme.zig").xcursorTheme },
- .{ "zoom", @import("command/zoom.zig").zoom },
- // zig fmt: on
- },
-);
-
-pub const Error = error{
- NoCommand,
- UnknownCommand,
- NotEnoughArguments,
- TooManyArguments,
- OutOfBounds,
- Overflow,
- InvalidButton,
- InvalidCharacter,
- InvalidDirection,
- InvalidGlob,
- InvalidPhysicalDirection,
- InvalidOutputIndicator,
- InvalidOrientation,
- InvalidRgba,
- InvalidValue,
- CannotReadFile,
- CannotParseFile,
- UnknownOption,
- ConflictingOptions,
- OutOfMemory,
- Other,
-};
-
-/// Run a command for the given Seat. The `args` parameter is similar to the
-/// classic argv in that the command to be run is passed as the first argument.
-/// The optional slice passed as the out parameter must initially be set to
-/// null. If the command produces output or Error.Other is returned, the slice
-/// will be set to the output of the command or a failure message, respectively.
-/// The caller is then responsible for freeing that slice, which will be
-/// allocated using the provided allocator.
-pub fn run(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- assert(out.* == null);
- if (args.len == 0) return Error.NoCommand;
- const impl_fn = command_impls.get(args[0]) orelse return Error.UnknownCommand;
- try impl_fn(seat, args, out);
-}
-
-/// Return a short error message for the given error. Passing Error.Other is invalid.
-pub fn errToMsg(err: Error) [:0]const u8 {
- return switch (err) {
- Error.NoCommand => "no command given",
- Error.UnknownCommand => "unknown command",
- Error.UnknownOption => "unknown option",
- Error.ConflictingOptions => "options conflict",
- Error.NotEnoughArguments => "not enough arguments",
- Error.TooManyArguments => "too many arguments",
- Error.OutOfBounds, Error.Overflow => "value out of bounds",
- Error.InvalidButton => "invalid button",
- Error.InvalidCharacter => "invalid character in argument",
- Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'",
- Error.InvalidGlob => "invalid glob. '*' is only allowed as the first and/or last character",
- Error.InvalidPhysicalDirection => "invalid direction. Must be 'up', 'down', 'left' or 'right'",
- Error.InvalidOutputIndicator => "invalid indicator for an output. Must be 'next', 'previous', 'up', 'down', 'left', 'right' or a valid output name",
- Error.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'",
- Error.InvalidRgba => "invalid color format, must be hexadecimal 0xRRGGBB or 0xRRGGBBAA",
- Error.InvalidValue => "invalid value",
- Error.CannotReadFile => "cannot read file",
- Error.CannotParseFile => "cannot parse file",
- Error.OutOfMemory => "out of memory",
- Error.Other => unreachable,
- };
-}
diff --git a/river/command/attach_mode.zig b/river/command/attach_mode.zig
deleted file mode 100644
index 6ffc615..0000000
--- a/river/command/attach_mode.zig
+++ /dev/null
@@ -1,61 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const mem = std.mem;
-const meta = std.meta;
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-const Config = @import("../Config.zig");
-
-fn parseAttachMode(args: []const [:0]const u8) Error!Config.AttachMode {
- if (args.len < 2) return Error.NotEnoughArguments;
-
- const tag = meta.stringToEnum(meta.Tag(Config.AttachMode), args[1]) orelse return Error.UnknownOption;
- switch (tag) {
- inline .top, .bottom, .above, .below => |mode| {
- if (args.len > 2) return Error.TooManyArguments;
-
- return mode;
- },
- .after => {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
-
- return .{ .after = try std.fmt.parseInt(u32, args[2], 10) };
- },
- }
-}
-
-pub fn outputAttachMode(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- const output = seat.focused_output orelse return;
- output.attach_mode = try parseAttachMode(args);
-}
-
-pub fn defaultAttachMode(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- server.config.default_attach_mode = try parseAttachMode(args);
-}
diff --git a/river/command/close.zig b/river/command/close.zig
deleted file mode 100644
index 6deaad5..0000000
--- a/river/command/close.zig
+++ /dev/null
@@ -1,31 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-/// Close the focused view, if any.
-pub fn close(
- seat: *Seat,
- _: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- // Note: we don't call arrange() here as it will be called
- // automatically when the view is unmapped.
- if (seat.focused == .view) seat.focused.view.close();
-}
diff --git a/river/command/config.zig b/river/command/config.zig
deleted file mode 100644
index a97e233..0000000
--- a/river/command/config.zig
+++ /dev/null
@@ -1,119 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const fmt = std.fmt;
-const mem = std.mem;
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-const Config = @import("../Config.zig");
-
-pub fn borderWidth(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- server.config.border_width = try fmt.parseInt(u31, args[1], 10);
- server.root.applyPending();
-}
-
-pub fn backgroundColor(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- server.config.background_color = try parseRgba(args[1]);
- var it = server.root.all_outputs.iterator(.forward);
- while (it.next()) |output| {
- output.layers.background_color_rect.setColor(&server.config.background_color);
- }
-}
-
-pub fn borderColorFocused(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- server.config.border_color_focused = try parseRgba(args[1]);
- server.root.applyPending();
-}
-
-pub fn borderColorUnfocused(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- server.config.border_color_unfocused = try parseRgba(args[1]);
- server.root.applyPending();
-}
-
-pub fn borderColorUrgent(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- server.config.border_color_urgent = try parseRgba(args[1]);
- server.root.applyPending();
-}
-
-pub fn setCursorWarp(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
- server.config.warp_cursor = std.meta.stringToEnum(Config.WarpCursorMode, args[1]) orelse
- return Error.UnknownOption;
-}
-
-/// Parse a color in the format 0xRRGGBB or 0xRRGGBBAA. Returned color has premultiplied alpha.
-fn parseRgba(string: []const u8) ![4]f32 {
- if (string.len != 8 and string.len != 10) return error.InvalidRgba;
- if (string[0] != '0' or string[1] != 'x') return error.InvalidRgba;
-
- const r = try fmt.parseInt(u8, string[2..4], 16);
- const g = try fmt.parseInt(u8, string[4..6], 16);
- const b = try fmt.parseInt(u8, string[6..8], 16);
- const a = if (string.len == 10) try fmt.parseInt(u8, string[8..10], 16) else 255;
-
- const alpha = @as(f32, @floatFromInt(a)) / 255.0;
-
- return [4]f32{
- @as(f32, @floatFromInt(r)) / 255.0 * alpha,
- @as(f32, @floatFromInt(g)) / 255.0 * alpha,
- @as(f32, @floatFromInt(b)) / 255.0 * alpha,
- alpha,
- };
-}
diff --git a/river/command/cursor.zig b/river/command/cursor.zig
deleted file mode 100644
index 33b7b50..0000000
--- a/river/command/cursor.zig
+++ /dev/null
@@ -1,51 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2022 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const util = @import("../util.zig");
-
-const server = &@import("../main.zig").server;
-
-const Config = @import("../Config.zig");
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-pub fn cursor(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (std.mem.eql(u8, "timeout", args[1])) {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
- server.config.cursor_hide_timeout = try std.fmt.parseInt(u31, args[2], 10);
- var seat_it = server.input_manager.seats.first;
- while (seat_it) |seat_node| : (seat_it = seat_node.next) {
- const seat = &seat_node.data;
- seat.cursor.unhide();
- }
- } else if (std.mem.eql(u8, "when-typing", args[1])) {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
- server.config.cursor_hide_when_typing = std.meta.stringToEnum(Config.HideCursorWhenTypingMode, args[2]) orelse
- return Error.UnknownOption;
- } else {
- return Error.UnknownOption;
- }
-}
diff --git a/river/command/declare_mode.zig b/river/command/declare_mode.zig
deleted file mode 100644
index 3ebbd03..0000000
--- a/river/command/declare_mode.zig
+++ /dev/null
@@ -1,49 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Mode = @import("../Mode.zig");
-const Error = @import("../command.zig").Error;
-const Mapping = @import("../Mapping.zig");
-const Seat = @import("../Seat.zig");
-
-/// Declare a new keymap mode
-pub fn declareMode(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- const config = &server.config;
- const new_mode_name = args[1];
-
- if (config.mode_to_id.get(new_mode_name) != null) return;
-
- try config.mode_to_id.ensureUnusedCapacity(1);
- try config.modes.ensureUnusedCapacity(util.gpa, 1);
-
- const owned_name = try util.gpa.dupeZ(u8, new_mode_name);
-
- const id: u32 = @intCast(config.modes.items.len);
- config.mode_to_id.putAssumeCapacityNoClobber(owned_name, id);
- config.modes.appendAssumeCapacity(.{ .name = owned_name });
-}
diff --git a/river/command/enter_mode.zig b/river/command/enter_mode.zig
deleted file mode 100644
index c982e60..0000000
--- a/river/command/enter_mode.zig
+++ /dev/null
@@ -1,63 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-/// Switch to the given mode
-pub fn enterMode(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- if (seat.mode_id == 1) {
- out.* = try std.fmt.allocPrint(
- util.gpa,
- "manually exiting mode 'locked' is not allowed",
- .{},
- );
- return Error.Other;
- }
-
- const target_mode = args[1];
- const mode_id = server.config.mode_to_id.get(target_mode) orelse {
- out.* = try std.fmt.allocPrint(
- util.gpa,
- "cannot enter non-existant mode '{s}'",
- .{target_mode},
- );
- return Error.Other;
- };
-
- if (mode_id == 1) {
- out.* = try std.fmt.allocPrint(
- util.gpa,
- "manually entering mode 'locked' is not allowed",
- .{},
- );
- return Error.Other;
- }
-
- seat.enterMode(mode_id);
-}
diff --git a/river/command/exit.zig b/river/command/exit.zig
deleted file mode 100644
index 7d9ba0c..0000000
--- a/river/command/exit.zig
+++ /dev/null
@@ -1,32 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-/// Exit the compositor, terminating the wayland session.
-pub fn exit(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len > 1) return Error.TooManyArguments;
- server.wl_server.terminate();
-}
diff --git a/river/command/focus_follows_cursor.zig b/river/command/focus_follows_cursor.zig
deleted file mode 100644
index a374b8b..0000000
--- a/river/command/focus_follows_cursor.zig
+++ /dev/null
@@ -1,35 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const server = &@import("../main.zig").server;
-
-const Config = @import("../Config.zig");
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-pub fn focusFollowsCursor(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- server.config.focus_follows_cursor =
- std.meta.stringToEnum(Config.FocusFollowsCursorMode, args[1]) orelse return Error.UnknownOption;
-}
diff --git a/river/command/input.zig b/river/command/input.zig
deleted file mode 100644
index acc7927..0000000
--- a/river/command/input.zig
+++ /dev/null
@@ -1,121 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2021 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const mem = std.mem;
-const sort = std.sort;
-
-const globber = @import("globber");
-
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-const InputConfig = @import("../InputConfig.zig");
-
-pub fn listInputs(
- _: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len > 1) return error.TooManyArguments;
-
- var input_list = std.ArrayList(u8).init(util.gpa);
- const writer = input_list.writer();
- var prev = false;
-
- var it = server.input_manager.devices.iterator(.forward);
- while (it.next()) |device| {
- const configured = for (server.input_manager.configs.items) |*input_config| {
- if (globber.match(device.identifier, input_config.glob)) {
- break true;
- }
- } else false;
-
- if (prev) try input_list.appendSlice("\n");
- prev = true;
-
- try writer.print("{s}\n\tconfigured: {}\n", .{
- device.identifier,
- configured,
- });
- }
-
- out.* = try input_list.toOwnedSlice();
-}
-
-pub fn listInputConfigs(
- _: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len > 1) return error.TooManyArguments;
-
- var input_list = std.ArrayList(u8).init(util.gpa);
- const writer = input_list.writer();
-
- for (server.input_manager.configs.items, 0..) |*input_config, i| {
- if (i > 0) try writer.writeByte('\n');
- try input_config.write(writer);
- }
-
- out.* = try input_list.toOwnedSlice();
-}
-
-pub fn input(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 4) return Error.NotEnoughArguments;
- if (args.len > 4) return Error.TooManyArguments;
-
- try globber.validate(args[1]);
-
- // Try to find an existing InputConfig with matching glob pattern, or create
- // a new one if none was found.
- for (server.input_manager.configs.items) |*input_config| {
- if (mem.eql(u8, input_config.glob, args[1])) {
- try input_config.parse(args[2], args[3]);
- break;
- }
- } else {
- var input_config: InputConfig = .{
- .glob = try util.gpa.dupe(u8, args[1]),
- };
- errdefer util.gpa.free(input_config.glob);
-
- try server.input_manager.configs.ensureUnusedCapacity(1);
-
- try input_config.parse(args[2], args[3]);
-
- server.input_manager.configs.appendAssumeCapacity(input_config);
- }
-
- // Sort input configs from most general to least general
- sort.insertion(InputConfig, server.input_manager.configs.items, {}, lessThan);
-
- // We need to update all input device matching the glob. The user may
- // add an input configuration at an arbitrary position in the generality
- // ordered list, so the simplest way to ensure the device is configured
- // correctly is to apply all input configurations again, in order.
- server.input_manager.reconfigureDevices();
-}
-
-fn lessThan(_: void, a: InputConfig, b: InputConfig) bool {
- return globber.order(a.glob, b.glob) == .gt;
-}
diff --git a/river/command/keyboard.zig b/river/command/keyboard.zig
deleted file mode 100644
index c09db1f..0000000
--- a/river/command/keyboard.zig
+++ /dev/null
@@ -1,108 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2022 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const mem = std.mem;
-
-const xkb = @import("xkbcommon");
-const flags = @import("flags");
-
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-pub fn keyboardLayout(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- const result = flags.parser([:0]const u8, &.{
- .{ .name = "rules", .kind = .arg },
- .{ .name = "model", .kind = .arg },
- .{ .name = "variant", .kind = .arg },
- .{ .name = "options", .kind = .arg },
- }).parse(args[1..]) catch {
- return error.InvalidValue;
- };
- if (result.args.len < 1) return Error.NotEnoughArguments;
- if (result.args.len > 1) return Error.TooManyArguments;
-
- const rule_names = xkb.RuleNames{
- .layout = result.args[0],
- // TODO(zig) these should eventually coerce without this hack.
- .rules = if (result.flags.rules) |s| s else null,
- .model = if (result.flags.model) |s| s else null,
- .variant = if (result.flags.variant) |s| s else null,
- .options = if (result.flags.options) |s| s else null,
- };
-
- const new_keymap = xkb.Keymap.newFromNames(
- server.config.xkb_context,
- &rule_names,
- .no_flags,
- ) orelse return error.InvalidValue;
- defer new_keymap.unref();
-
- applyLayout(new_keymap);
-}
-
-pub fn keyboardLayoutFile(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- const file = std.fs.cwd().openFile(args[1], .{}) catch return error.CannotReadFile;
- defer file.close();
-
- // 1 GiB is arbitrarily chosen as an exceedingly large but not infinite upper bound.
- const file_bytes = file.readToEndAlloc(util.gpa, 1024 * 1024 * 1024) catch |err| {
- switch (err) {
- error.FileTooBig, error.OutOfMemory => return error.OutOfMemory,
- else => return error.CannotReadFile,
- }
- };
- defer util.gpa.free(file_bytes);
-
- const new_keymap = xkb.Keymap.newFromBuffer(
- server.config.xkb_context,
- file_bytes.ptr,
- file_bytes.len,
- .text_v1,
- .no_flags,
- ) orelse return error.CannotParseFile;
- defer new_keymap.unref();
-
- applyLayout(new_keymap);
-}
-
-fn applyLayout(new_keymap: *xkb.Keymap) void {
- server.config.keymap.unref();
- server.config.keymap = new_keymap.ref();
-
- var it = server.input_manager.devices.iterator(.forward);
- while (it.next()) |device| {
- if (device.wlr_device.type != .keyboard) continue;
- const wlr_keyboard = device.wlr_device.toKeyboard();
- // wlroots will log an error if this fails and there's unfortunately
- // nothing we can really do in the case of failure.
- _ = wlr_keyboard.setKeymap(new_keymap);
- }
-}
diff --git a/river/command/keyboard_group.zig b/river/command/keyboard_group.zig
deleted file mode 100644
index 442fbdc..0000000
--- a/river/command/keyboard_group.zig
+++ /dev/null
@@ -1,100 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2022 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const mem = std.mem;
-
-const globber = @import("globber");
-
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-const KeyboardGroup = @import("../KeyboardGroup.zig");
-
-pub fn keyboardGroupCreate(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- if (keyboardGroupFromName(seat, args[1]) != null) {
- const msg = try util.gpa.dupe(u8, "error: failed to create keybaord group: group of same name already exists\n");
- out.* = msg;
- return;
- }
-
- try KeyboardGroup.create(seat, args[1]);
-}
-
-pub fn keyboardGroupDestroy(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
- const group = keyboardGroupFromName(seat, args[1]) orelse {
- const msg = try util.gpa.dupe(u8, "error: no keyboard group with that name exists\n");
- out.* = msg;
- return;
- };
- group.destroy();
-}
-
-pub fn keyboardGroupAdd(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
-
- const group = keyboardGroupFromName(seat, args[1]) orelse {
- const msg = try util.gpa.dupe(u8, "error: no keyboard group with that name exists\n");
- out.* = msg;
- return;
- };
- try globber.validate(args[2]);
- try group.addIdentifier(args[2]);
-}
-
-pub fn keyboardGroupRemove(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
-
- const group = keyboardGroupFromName(seat, args[1]) orelse {
- const msg = try util.gpa.dupe(u8, "error: no keyboard group with that name exists\n");
- out.* = msg;
- return;
- };
- try group.removeIdentifier(args[2]);
-}
-
-fn keyboardGroupFromName(seat: *Seat, name: []const u8) ?*KeyboardGroup {
- var it = seat.keyboard_groups.first;
- while (it) |node| : (it = node.next) {
- if (mem.eql(u8, node.data.name, name)) return &node.data;
- }
- return null;
-}
diff --git a/river/command/layout.zig b/river/command/layout.zig
deleted file mode 100644
index 798dff6..0000000
--- a/river/command/layout.zig
+++ /dev/null
@@ -1,85 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2021 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const mem = std.mem;
-const wl = @import("wayland").server.wl;
-const util = @import("../util.zig");
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-pub fn outputLayout(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- const output = seat.focused_output orelse return;
- const old_layout_namespace = output.layout_namespace;
- output.layout_namespace = try util.gpa.dupe(u8, args[1]);
- if (old_layout_namespace) |old| util.gpa.free(old);
- output.handleLayoutNamespaceChange();
-}
-
-pub fn defaultLayout(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- const old_default_layout_namespace = server.config.default_layout_namespace;
- server.config.default_layout_namespace = try util.gpa.dupe(u8, args[1]);
- util.gpa.free(old_default_layout_namespace);
-
- var it = server.root.all_outputs.iterator(.forward);
- while (it.next()) |output| {
- if (output.layout_namespace == null) output.handleLayoutNamespaceChange();
- }
-}
-
-/// riverctl send-layout-cmd rivertile "mod-main-count 1"
-/// riverctl send-layout-cmd rivertile "mod-main-factor -0.1"
-/// riverctl send-layout-cmd rivertile "main-location top"
-pub fn sendLayoutCmd(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
-
- const output = seat.focused_output orelse return;
- const target_namespace = args[1];
-
- var it = output.layouts.first;
- const layout = while (it) |node| : (it = node.next) {
- const layout = &node.data;
- if (mem.eql(u8, layout.namespace, target_namespace)) break layout;
- } else return;
-
- if (layout.layout_v3.getVersion() >= 2) {
- layout.layout_v3.sendUserCommandTags(output.pending.tags);
- }
- layout.layout_v3.sendUserCommand(args[2]);
- if (layout == output.layout) server.root.applyPending();
-}
diff --git a/river/command/map.zig b/river/command/map.zig
deleted file mode 100644
index e8ded57..0000000
--- a/river/command/map.zig
+++ /dev/null
@@ -1,437 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const fmt = std.fmt;
-const mem = std.mem;
-const meta = std.meta;
-const wlr = @import("wlroots");
-const xkb = @import("xkbcommon");
-const flags = @import("flags");
-
-const c = @import("../c.zig");
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Error = @import("../command.zig").Error;
-const Mapping = @import("../Mapping.zig");
-const PointerMapping = @import("../PointerMapping.zig");
-const SwitchMapping = @import("../SwitchMapping.zig");
-const Switch = @import("../Switch.zig");
-const Seat = @import("../Seat.zig");
-
-/// Create a new mapping for a given mode
-///
-/// Example:
-/// map normal Mod4+Shift Return spawn foot
-pub fn map(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- const result = flags.parser([:0]const u8, &.{
- .{ .name = "release", .kind = .boolean },
- .{ .name = "repeat", .kind = .boolean },
- .{ .name = "layout", .kind = .arg },
- }).parse(args[1..]) catch {
- return error.InvalidValue;
- };
- if (result.args.len < 4) return Error.NotEnoughArguments;
-
- if (result.flags.release and result.flags.repeat) return Error.ConflictingOptions;
-
- const layout_index = blk: {
- if (result.flags.layout) |layout_raw| {
- break :blk try fmt.parseInt(u32, layout_raw, 10);
- } else {
- break :blk null;
- }
- };
-
- const mode_raw = result.args[0];
- const modifiers_raw = result.args[1];
- const keysym_raw = result.args[2];
- const command = result.args[3..];
-
- const mode_id = try modeNameToId(mode_raw, out);
- const modifiers = try parseModifiers(modifiers_raw, out);
- const keysym = try parseKeysym(keysym_raw, out);
-
- const mode_mappings = &server.config.modes.items[mode_id].mappings;
-
- const new = try Mapping.init(
- keysym,
- modifiers,
- command,
- .{
- .release = result.flags.release,
- .repeat = result.flags.repeat,
- .layout_index = layout_index,
- },
- );
- errdefer new.deinit();
-
- if (mappingExists(mode_mappings, modifiers, keysym, result.flags.release)) |current| {
- mode_mappings.items[current].deinit();
- mode_mappings.items[current] = new;
- // Warn user if they overwrote an existing keybinding using riverctl.
- const opts = if (result.flags.release) "-release " else "";
- out.* = try fmt.allocPrint(
- util.gpa,
- "overwrote an existing keybinding: {s} {s}{s} {s}",
- .{ mode_raw, opts, modifiers_raw, keysym_raw },
- );
- } else {
- // Repeating mappings borrow the Mapping directly. To prevent a
- // possible crash if the Mapping ArrayList is reallocated, stop any
- // currently repeating mappings.
- seat.clearRepeatingMapping();
- try mode_mappings.append(util.gpa, new);
- }
-}
-
-/// Create a new switch mapping for a given mode
-///
-/// Example:
-/// map-switch normal lid close spawn "wlr-randr --output eDP-1 --off"
-pub fn mapSwitch(
- _: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 5) return Error.NotEnoughArguments;
-
- const mode_id = try modeNameToId(args[1], out);
- const switch_type = try parseSwitchType(args[2], out);
- const switch_state = try parseSwitchState(switch_type, args[3], out);
-
- const new = try SwitchMapping.init(switch_type, switch_state, args[4..]);
- errdefer new.deinit();
-
- const mode_mappings = &server.config.modes.items[mode_id].switch_mappings;
-
- if (switchMappingExists(mode_mappings, switch_type, switch_state)) |current| {
- mode_mappings.items[current].deinit();
- mode_mappings.items[current] = new;
- // Warn user if they overwrote an existing keybinding using riverctl.
- out.* = try std.fmt.allocPrint(
- util.gpa,
- "overwrote an existing keybinding: map-switch {s} {s} {s}",
- .{ args[1], args[2], args[3] },
- );
- } else {
- try mode_mappings.append(util.gpa, new);
- }
-}
-
-/// Create a new pointer mapping for a given mode
-///
-/// Example:
-/// map-pointer normal Mod4 BTN_LEFT move-view
-pub fn mapPointer(
- _: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 5) return Error.NotEnoughArguments;
-
- const mode_id = try modeNameToId(args[1], out);
- const modifiers = try parseModifiers(args[2], out);
- const event_code = try parseEventCode(args[3], out);
-
- const action: meta.Tag(PointerMapping.Action) = blk: {
- if (mem.eql(u8, args[4], "move-view")) {
- break :blk .move;
- } else if (mem.eql(u8, args[4], "resize-view")) {
- break :blk .resize;
- } else {
- break :blk .command;
- }
- };
-
- if (action != .command and args.len > 5) return Error.TooManyArguments;
-
- var new = try PointerMapping.init(
- event_code,
- modifiers,
- action,
- args[4..],
- );
- errdefer new.deinit();
-
- const mode_pointer_mappings = &server.config.modes.items[mode_id].pointer_mappings;
- if (pointerMappingExists(mode_pointer_mappings, modifiers, event_code)) |current| {
- mode_pointer_mappings.items[current].deinit();
- mode_pointer_mappings.items[current] = new;
- } else {
- try mode_pointer_mappings.append(util.gpa, new);
- }
-}
-
-fn modeNameToId(mode_name: []const u8, out: *?[]const u8) !usize {
- const config = &server.config;
- return config.mode_to_id.get(mode_name) orelse {
- out.* = try fmt.allocPrint(
- util.gpa,
- "cannot add/remove mapping to/from non-existant mode '{s}'",
- .{mode_name},
- );
- return Error.Other;
- };
-}
-
-/// Returns the index of the Mapping with matching modifiers, keysym and release, if any.
-fn mappingExists(
- mappings: *std.ArrayListUnmanaged(Mapping),
- modifiers: wlr.Keyboard.ModifierMask,
- keysym: xkb.Keysym,
- release: bool,
-) ?usize {
- for (mappings.items, 0..) |mapping, i| {
- if (meta.eql(mapping.modifiers, modifiers) and
- mapping.keysym == keysym and mapping.options.release == release)
- {
- return i;
- }
- }
-
- return null;
-}
-
-/// Returns the index of the SwitchMapping with matching switch_type and switch_state, if any.
-fn switchMappingExists(
- switch_mappings: *std.ArrayListUnmanaged(SwitchMapping),
- switch_type: Switch.Type,
- switch_state: Switch.State,
-) ?usize {
- for (switch_mappings.items, 0..) |mapping, i| {
- if (mapping.switch_type == switch_type and meta.eql(mapping.switch_state, switch_state)) {
- return i;
- }
- }
-
- return null;
-}
-
-/// Returns the index of the PointerMapping with matching modifiers and event code, if any.
-fn pointerMappingExists(
- pointer_mappings: *std.ArrayListUnmanaged(PointerMapping),
- modifiers: wlr.Keyboard.ModifierMask,
- event_code: u32,
-) ?usize {
- for (pointer_mappings.items, 0..) |mapping, i| {
- if (meta.eql(mapping.modifiers, modifiers) and mapping.event_code == event_code) {
- return i;
- }
- }
-
- return null;
-}
-
-fn parseEventCode(name: [:0]const u8, out: *?[]const u8) !u32 {
- const event_code = c.libevdev_event_code_from_name(c.EV_KEY, name.ptr);
- if (event_code < 1) {
- out.* = try fmt.allocPrint(util.gpa, "unknown button {s}", .{name});
- return Error.Other;
- }
-
- return @intCast(event_code);
-}
-
-fn parseKeysym(name: [:0]const u8, out: *?[]const u8) !xkb.Keysym {
- const keysym = xkb.Keysym.fromName(name, .case_insensitive);
- if (keysym == .NoSymbol) {
- out.* = try fmt.allocPrint(util.gpa, "invalid keysym '{s}'", .{name});
- return Error.Other;
- }
-
- // The case insensitive matching done by xkbcommon returns the first
- // lowercase match found if there are multiple matches that differ only in
- // case. This works great for alphabetic keys for example but there is one
- // problematic exception we handle specially here. For some reason there
- // exist both uppercase and lowercase versions of XF86ScreenSaver with
- // different keysym values for example. Switching to a case-sensitive match
- // would be too much of a breaking change at this point so fix this by
- // special-casing this exception.
- //
- // This has been fixed upstream in libxkbcommon 1.7.0
- // https://github.com/xkbcommon/libxkbcommon/pull/465
- // TODO remove the workaround once libxkbcommon 1.7.0 is widely distributed.
- if (@intFromEnum(keysym) == xkb.Keysym.XF86Screensaver) {
- if (mem.eql(u8, name, "XF86Screensaver")) {
- return keysym;
- } else if (mem.eql(u8, name, "XF86ScreenSaver")) {
- return @enumFromInt(xkb.Keysym.XF86ScreenSaver);
- } else {
- out.* = try fmt.allocPrint(util.gpa, "ambiguous keysym name '{s}'", .{name});
- return Error.Other;
- }
- }
-
- return keysym;
-}
-
-fn parseModifiers(modifiers_str: []const u8, out: *?[]const u8) !wlr.Keyboard.ModifierMask {
- var it = mem.split(u8, modifiers_str, "+");
- var modifiers = wlr.Keyboard.ModifierMask{};
- outer: while (it.next()) |mod_name| {
- if (mem.eql(u8, mod_name, "None")) continue;
- inline for ([_]struct { name: []const u8, field_name: []const u8 }{
- .{ .name = "Shift", .field_name = "shift" },
- .{ .name = "Control", .field_name = "ctrl" },
- .{ .name = "Mod1", .field_name = "alt" },
- .{ .name = "Alt", .field_name = "alt" },
- .{ .name = "Mod3", .field_name = "mod3" },
- .{ .name = "Mod4", .field_name = "logo" },
- .{ .name = "Super", .field_name = "logo" },
- .{ .name = "Mod5", .field_name = "mod5" },
- }) |def| {
- if (mem.eql(u8, def.name, mod_name)) {
- @field(modifiers, def.field_name) = true;
- continue :outer;
- }
- }
- out.* = try fmt.allocPrint(util.gpa, "invalid modifier '{s}'", .{mod_name});
- return Error.Other;
- }
- return modifiers;
-}
-
-fn parseSwitchType(
- switch_type_str: []const u8,
- out: *?[]const u8,
-) !Switch.Type {
- return meta.stringToEnum(Switch.Type, switch_type_str) orelse {
- out.* = try std.fmt.allocPrint(
- util.gpa,
- "invalid switch '{s}', must be 'lid' or 'tablet'",
- .{switch_type_str},
- );
- return Error.Other;
- };
-}
-
-fn parseSwitchState(
- switch_type: Switch.Type,
- switch_state_str: []const u8,
- out: *?[]const u8,
-) !Switch.State {
- switch (switch_type) {
- .lid => {
- const lid_state = meta.stringToEnum(
- Switch.LidState,
- switch_state_str,
- ) orelse {
- out.* = try std.fmt.allocPrint(
- util.gpa,
- "invalid lid state '{s}', must be 'close' or 'open'",
- .{switch_state_str},
- );
- return Error.Other;
- };
- return Switch.State{ .lid = lid_state };
- },
- .tablet => {
- const tablet_state = meta.stringToEnum(
- Switch.TabletState,
- switch_state_str,
- ) orelse {
- out.* = try std.fmt.allocPrint(
- util.gpa,
- "invalid tablet state '{s}', must be 'on' or 'off'",
- .{switch_state_str},
- );
- return Error.Other;
- };
- return Switch.State{ .tablet = tablet_state };
- },
- }
-}
-
-/// Remove a mapping from a given mode
-///
-/// Example:
-/// unmap normal Mod4+Shift Return
-pub fn unmap(seat: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void {
- const result = flags.parser([:0]const u8, &.{
- .{ .name = "release", .kind = .boolean },
- }).parse(args[1..]) catch {
- return error.InvalidValue;
- };
- if (result.args.len < 3) return Error.NotEnoughArguments;
- if (result.args.len > 3) return Error.TooManyArguments;
-
- const mode_id = try modeNameToId(result.args[0], out);
- const modifiers = try parseModifiers(result.args[1], out);
- const keysym = try parseKeysym(result.args[2], out);
-
- const mode_mappings = &server.config.modes.items[mode_id].mappings;
- const mapping_idx = mappingExists(
- mode_mappings,
- modifiers,
- keysym,
- result.flags.release,
- ) orelse return;
-
- // Repeating mappings borrow the Mapping directly. To prevent a possible
- // crash if the Mapping ArrayList is reallocated, stop any currently
- // repeating mappings.
- seat.clearRepeatingMapping();
-
- var mapping = mode_mappings.swapRemove(mapping_idx);
- mapping.deinit();
-}
-
-/// Remove a switch mapping from a given mode
-///
-/// Example:
-/// unmap-switch normal tablet on
-pub fn unmapSwitch(
- _: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 4) return Error.NotEnoughArguments;
-
- const mode_id = try modeNameToId(args[1], out);
- const switch_type = try parseSwitchType(args[2], out);
- const switch_state = try parseSwitchState(switch_type, args[3], out);
-
- const mode_mappings = &server.config.modes.items[mode_id].switch_mappings;
- const mapping_idx = switchMappingExists(mode_mappings, switch_type, switch_state) orelse return;
-
- var mapping = mode_mappings.swapRemove(mapping_idx);
- mapping.deinit();
-}
-
-/// Remove a pointer mapping for a given mode
-///
-/// Example:
-/// unmap-pointer normal Mod4 BTN_LEFT
-pub fn unmapPointer(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void {
- if (args.len < 4) return Error.NotEnoughArguments;
- if (args.len > 4) return Error.TooManyArguments;
-
- const mode_id = try modeNameToId(args[1], out);
- const modifiers = try parseModifiers(args[2], out);
- const event_code = try parseEventCode(args[3], out);
-
- const mode_pointer_mappings = &server.config.modes.items[mode_id].pointer_mappings;
- const mapping_idx = pointerMappingExists(mode_pointer_mappings, modifiers, event_code) orelse return;
-
- var mapping = mode_pointer_mappings.swapRemove(mapping_idx);
- mapping.deinit();
-}
diff --git a/river/command/move.zig b/river/command/move.zig
deleted file mode 100644
index 1d0ca00..0000000
--- a/river/command/move.zig
+++ /dev/null
@@ -1,150 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const math = std.math;
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const PhysicalDirection = @import("../command.zig").PhysicalDirection;
-const Orientation = @import("../command.zig").Orientation;
-const Seat = @import("../Seat.zig");
-const View = @import("../View.zig");
-
-pub fn move(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
-
- const delta = try std.fmt.parseInt(i32, args[2], 10);
- const direction = std.meta.stringToEnum(PhysicalDirection, args[1]) orelse
- return Error.InvalidPhysicalDirection;
-
- const view = getView(seat) orelse return;
- switch (direction) {
- .up => view.pending.move(0, -delta),
- .down => view.pending.move(0, delta),
- .left => view.pending.move(-delta, 0),
- .right => view.pending.move(delta, 0),
- }
-
- apply(view);
-}
-
-pub fn snap(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- const direction = std.meta.stringToEnum(PhysicalDirection, args[1]) orelse
- return Error.InvalidPhysicalDirection;
-
- const view = getView(seat) orelse return;
- const output = view.pending.output orelse return;
- const border_width = server.config.border_width;
- var output_width: i32 = undefined;
- var output_height: i32 = undefined;
- output.wlr_output.effectiveResolution(&output_width, &output_height);
- switch (direction) {
- .up => view.pending.box.y = border_width,
- .down => view.pending.box.y = output_height - view.pending.box.height - border_width,
- .left => view.pending.box.x = border_width,
- .right => view.pending.box.x = output_width - view.pending.box.width - border_width,
- }
-
- apply(view);
-}
-
-pub fn resize(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
-
- const delta = try std.fmt.parseInt(i32, args[2], 10);
- const orientation = std.meta.stringToEnum(Orientation, args[1]) orelse
- return Error.InvalidOrientation;
-
- const view = getView(seat) orelse return;
- var output_width: c_int = math.maxInt(c_int);
- var output_height: c_int = math.maxInt(c_int);
- if (view.pending.output) |output| {
- output.wlr_output.effectiveResolution(&output_width, &output_height);
- }
- switch (orientation) {
- .horizontal => {
- const prev_width = view.pending.box.width;
- view.pending.box.width += delta;
- view.applyConstraints(&view.pending.box);
- // Get width difference after applying view constraints, so that the
- // move reflects the actual size difference, but before applying the
- // output size constraints, to allow growing a view even if it is
- // up against an output edge.
- const diff_width = prev_width - view.pending.box.width;
- // Do not grow bigger than the output
- view.pending.box.width = @min(
- view.pending.box.width,
- output_width - 2 * server.config.border_width,
- );
- view.pending.move(@divFloor(diff_width, 2), 0);
- },
- .vertical => {
- const prev_height = view.pending.box.height;
- view.pending.box.height += delta;
- view.applyConstraints(&view.pending.box);
- const diff_height = prev_height - view.pending.box.height;
- // Do not grow bigger than the output
- view.pending.box.height = @min(
- view.pending.box.height,
- output_height - 2 * server.config.border_width,
- );
- view.pending.move(0, @divFloor(diff_height, 2));
- },
- }
-
- apply(view);
-}
-
-fn apply(view: *View) void {
- // Set the view to floating but keep the position and dimensions, if their
- // dimensions are set by a layout generator. If however the views are
- // unarranged, leave them as non-floating so the next active layout can
- // affect them.
- if (view.pending.output == null or view.pending.output.?.layout != null) {
- view.pending.float = true;
- }
-
- server.root.applyPending();
-}
-
-fn getView(seat: *Seat) ?*View {
- if (seat.focused != .view) return null;
- const view = seat.focused.view;
-
- // Do not touch fullscreen views
- if (view.pending.fullscreen) return null;
-
- return view;
-}
diff --git a/river/command/output.zig b/river/command/output.zig
deleted file mode 100644
index 8f7cd0e..0000000
--- a/river/command/output.zig
+++ /dev/null
@@ -1,135 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2021 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const assert = std.debug.assert;
-const mem = std.mem;
-const wl = @import("wayland").server.wl;
-const wlr = @import("wlroots");
-const flags = @import("flags");
-
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Direction = @import("../command.zig").Direction;
-const PhysicalDirectionDirection = @import("../command.zig").PhysicalDirection;
-const Error = @import("../command.zig").Error;
-const Output = @import("../Output.zig");
-const Seat = @import("../Seat.zig");
-
-pub fn focusOutput(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- // If the fallback pseudo-output is focused, there are no other outputs to switch to
- if (seat.focused_output == null) {
- assert(server.root.active_outputs.empty());
- return;
- }
-
- seat.focusOutput((try getOutput(seat, args[1])) orelse return);
- server.root.applyPending();
-}
-
-pub fn sendToOutput(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- const result = flags.parser([:0]const u8, &.{
- .{ .name = "current-tags", .kind = .boolean },
- }).parse(args[1..]) catch {
- return error.InvalidOption;
- };
- if (result.args.len < 1) return Error.NotEnoughArguments;
- if (result.args.len > 1) return Error.TooManyArguments;
-
- // If the fallback pseudo-output is focused, there is nowhere to send the view
- if (seat.focused_output == null) {
- assert(server.root.active_outputs.empty());
- return;
- }
-
- if (seat.focused == .view) {
- const destination_output = (try getOutput(seat, result.args[0])) orelse return;
-
- // If the view is already on destination_output, do nothing
- if (seat.focused.view.pending.output == destination_output) return;
-
- if (result.flags.@"current-tags") {
- seat.focused.view.pending.tags = destination_output.pending.tags;
- }
-
- seat.focused.view.setPendingOutput(destination_output);
-
- // When explicitly sending a view to an output, the user likely
- // does not expect a previously evacuated view moved back to a
- // re-connecting output.
- if (seat.focused.view.output_before_evac) |name| {
- util.gpa.free(name);
- seat.focused.view.output_before_evac = null;
- }
-
- server.root.applyPending();
- }
-}
-
-/// Find an output adjacent to the currently focused based on either logical or
-/// spacial direction
-fn getOutput(seat: *Seat, str: []const u8) !?*Output {
- if (std.meta.stringToEnum(Direction, str)) |direction| { // Logical direction
- // Return the next/prev output in the list
- var link = &seat.focused_output.?.active_link;
- link = switch (direction) {
- .next => link.next.?,
- .previous => link.prev.?,
- };
- // Wrap around list head
- if (link == &server.root.active_outputs.link) {
- link = switch (direction) {
- .next => link.next.?,
- .previous => link.prev.?,
- };
- }
- return @as(*Output, @fieldParentPtr("active_link", link));
- } else if (std.meta.stringToEnum(wlr.OutputLayout.Direction, str)) |direction| { // Spacial direction
- var focus_box: wlr.Box = undefined;
- server.root.output_layout.getBox(seat.focused_output.?.wlr_output, &focus_box);
- if (focus_box.empty()) return null;
-
- const wlr_output = server.root.output_layout.adjacentOutput(
- direction,
- seat.focused_output.?.wlr_output,
- @floatFromInt(focus_box.x + @divTrunc(focus_box.width, 2)),
- @floatFromInt(focus_box.y + @divTrunc(focus_box.height, 2)),
- ) orelse return null;
- return @as(*Output, @ptrFromInt(wlr_output.data));
- } else {
- // Check if an output matches by name
- var it = server.root.active_outputs.iterator(.forward);
- while (it.next()) |output| {
- if (mem.eql(u8, mem.sliceTo(output.wlr_output.name, 0), str)) {
- return output;
- }
- }
- return Error.InvalidOutputIndicator;
- }
-}
diff --git a/river/command/rule.zig b/river/command/rule.zig
deleted file mode 100644
index 7703955..0000000
--- a/river/command/rule.zig
+++ /dev/null
@@ -1,265 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2023 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const fmt = std.fmt;
-
-const globber = @import("globber");
-const flags = @import("flags");
-
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-const View = @import("../View.zig");
-
-const Action = enum {
- float,
- @"no-float",
- ssd,
- csd,
- tags,
- output,
- position,
- dimensions,
- fullscreen,
- @"no-fullscreen",
-};
-
-pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
- const result = flags.parser([:0]const u8, &.{
- .{ .name = "app-id", .kind = .arg },
- .{ .name = "title", .kind = .arg },
- }).parse(args[1..]) catch {
- return error.InvalidValue;
- };
-
- if (result.args.len < 1) return Error.NotEnoughArguments;
-
- const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption;
-
- const positional_arguments_count: u8 = switch (action) {
- .float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen" => 1,
- .tags, .output => 2,
- .position, .dimensions => 3,
- };
- if (result.args.len > positional_arguments_count) return Error.TooManyArguments;
- if (result.args.len < positional_arguments_count) return Error.NotEnoughArguments;
-
- const app_id_glob = result.flags.@"app-id" orelse "*";
- const title_glob = result.flags.title orelse "*";
-
- try globber.validate(app_id_glob);
- try globber.validate(title_glob);
-
- switch (action) {
- .float, .@"no-float" => {
- try server.config.rules.float.add(.{
- .app_id_glob = app_id_glob,
- .title_glob = title_glob,
- .value = (action == .float),
- });
- },
- .ssd, .csd => {
- try server.config.rules.ssd.add(.{
- .app_id_glob = app_id_glob,
- .title_glob = title_glob,
- .value = (action == .ssd),
- });
- apply_ssd_rules();
- server.root.applyPending();
- },
- .tags => {
- const tags = try fmt.parseInt(u32, result.args[1], 10);
- try server.config.rules.tags.add(.{
- .app_id_glob = app_id_glob,
- .title_glob = title_glob,
- .value = tags,
- });
- },
- .output => {
- const output_name = try util.gpa.dupe(u8, result.args[1]);
- errdefer util.gpa.free(output_name);
- try server.config.rules.output.add(.{
- .app_id_glob = app_id_glob,
- .title_glob = title_glob,
- .value = output_name,
- });
- },
- .position => {
- const x = try fmt.parseInt(u31, result.args[1], 10);
- const y = try fmt.parseInt(u31, result.args[2], 10);
- try server.config.rules.position.add(.{
- .app_id_glob = app_id_glob,
- .title_glob = title_glob,
- .value = .{
- .x = x,
- .y = y,
- },
- });
- },
- .dimensions => {
- const width = try fmt.parseInt(u31, result.args[1], 10);
- const height = try fmt.parseInt(u31, result.args[2], 10);
- try server.config.rules.dimensions.add(.{
- .app_id_glob = app_id_glob,
- .title_glob = title_glob,
- .value = .{
- .width = width,
- .height = height,
- },
- });
- },
- .fullscreen, .@"no-fullscreen" => {
- try server.config.rules.fullscreen.add(.{
- .app_id_glob = app_id_glob,
- .title_glob = title_glob,
- .value = (action == .fullscreen),
- });
- },
- }
-}
-
-pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
- const result = flags.parser([:0]const u8, &.{
- .{ .name = "app-id", .kind = .arg },
- .{ .name = "title", .kind = .arg },
- }).parse(args[1..]) catch {
- return error.InvalidValue;
- };
-
- if (result.args.len > 1) return Error.TooManyArguments;
- if (result.args.len < 1) return Error.NotEnoughArguments;
-
- const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption;
-
- const rule = .{
- .app_id_glob = result.flags.@"app-id" orelse "*",
- .title_glob = result.flags.title orelse "*",
- };
- switch (action) {
- .float, .@"no-float" => {
- _ = server.config.rules.float.del(rule);
- },
- .ssd, .csd => {
- _ = server.config.rules.ssd.del(rule);
- apply_ssd_rules();
- server.root.applyPending();
- },
- .tags => {
- _ = server.config.rules.tags.del(rule);
- },
- .output => {
- if (server.config.rules.output.del(rule)) |output_rule| {
- util.gpa.free(output_rule);
- }
- },
- .position => {
- _ = server.config.rules.position.del(rule);
- },
- .dimensions => {
- _ = server.config.rules.dimensions.del(rule);
- },
- .fullscreen, .@"no-fullscreen" => {
- _ = server.config.rules.fullscreen.del(rule);
- },
- }
-}
-
-fn apply_ssd_rules() void {
- var it = server.root.views.iterator(.forward);
- while (it.next()) |view| {
- if (view.destroying) continue;
-
- if (server.config.rules.ssd.match(view)) |ssd| {
- view.pending.ssd = ssd;
- }
- }
-}
-
-pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void {
- if (args.len < 2) return error.NotEnoughArguments;
- if (args.len > 2) return error.TooManyArguments;
-
- const rule_list = std.meta.stringToEnum(enum {
- float,
- ssd,
- tags,
- output,
- position,
- dimensions,
- fullscreen,
- }, args[1]) orelse return Error.UnknownOption;
- const max_glob_len = switch (rule_list) {
- inline else => |list| @field(server.config.rules, @tagName(list)).getMaxGlobLen(),
- };
- const app_id_column_max = 2 + @max("app-id".len, max_glob_len.app_id);
- const title_column_max = 2 + @max("title".len, max_glob_len.title);
-
- var buffer = std.ArrayList(u8).init(util.gpa);
- const writer = buffer.writer();
-
- try fmt.formatBuf("title", .{ .width = title_column_max, .alignment = .left }, writer);
- try fmt.formatBuf("app-id", .{ .width = app_id_column_max, .alignment = .left }, writer);
- try writer.writeAll("action\n");
-
- switch (rule_list) {
- inline .float, .ssd, .output, .fullscreen => |list| {
- const rules = switch (list) {
- .float => server.config.rules.float.rules.items,
- .ssd => server.config.rules.ssd.rules.items,
- .output => server.config.rules.output.rules.items,
- .fullscreen => server.config.rules.fullscreen.rules.items,
- else => unreachable,
- };
- for (rules) |rule| {
- try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer);
- try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer);
- try writer.print("{s}\n", .{switch (list) {
- .float => if (rule.value) "float" else "no-float",
- .ssd => if (rule.value) "ssd" else "csd",
- .output => rule.value,
- .fullscreen => if (rule.value) "fullscreen" else "no-fullscreen",
- else => unreachable,
- }});
- }
- },
- .tags => {
- for (server.config.rules.tags.rules.items) |rule| {
- try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer);
- try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer);
- try writer.print("{b}\n", .{rule.value});
- }
- },
- .position => {
- for (server.config.rules.position.rules.items) |rule| {
- try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer);
- try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer);
- try writer.print("{d},{d}\n", .{ rule.value.x, rule.value.y });
- }
- },
- .dimensions => {
- for (server.config.rules.dimensions.rules.items) |rule| {
- try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer);
- try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer);
- try writer.print("{d}x{d}\n", .{ rule.value.width, rule.value.height });
- }
- },
- }
-
- out.* = try buffer.toOwnedSlice();
-}
diff --git a/river/command/set_repeat.zig b/river/command/set_repeat.zig
deleted file mode 100644
index e58908f..0000000
--- a/river/command/set_repeat.zig
+++ /dev/null
@@ -1,45 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-/// Set the repeat rate and delay for all keyboards.
-pub fn setRepeat(
- _: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 3) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
-
- const rate = try std.fmt.parseInt(u31, args[1], 10);
- const delay = try std.fmt.parseInt(u31, args[2], 10);
-
- server.config.repeat_rate = rate;
- server.config.repeat_delay = delay;
-
- var it = server.input_manager.devices.iterator(.forward);
- while (it.next()) |device| {
- if (device.wlr_device.type == .keyboard) {
- device.wlr_device.toKeyboard().setRepeatInfo(rate, delay);
- }
- }
-}
diff --git a/river/command/spawn.zig b/river/command/spawn.zig
deleted file mode 100644
index 030535a..0000000
--- a/river/command/spawn.zig
+++ /dev/null
@@ -1,62 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const posix = std.posix;
-
-const c = @import("../c.zig");
-const util = @import("../util.zig");
-const process = @import("../process.zig");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-/// Spawn a program.
-pub fn spawn(
- _: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", args[1], null };
-
- const pid = posix.fork() catch {
- out.* = try std.fmt.allocPrint(util.gpa, "fork/execve failed", .{});
- return Error.Other;
- };
-
- if (pid == 0) {
- process.cleanupChild();
-
- const pid2 = posix.fork() catch c._exit(1);
- if (pid2 == 0) {
- posix.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
- }
-
- c._exit(0);
- }
-
- // Wait the intermediate child.
- const ret = posix.waitpid(pid, 0);
- if (!posix.W.IFEXITED(ret.status) or
- (posix.W.IFEXITED(ret.status) and posix.W.EXITSTATUS(ret.status) != 0))
- {
- out.* = try std.fmt.allocPrint(util.gpa, "fork/execve failed", .{});
- return Error.Other;
- }
-}
diff --git a/river/command/tags.zig b/river/command/tags.zig
deleted file mode 100644
index d1ac8da..0000000
--- a/river/command/tags.zig
+++ /dev/null
@@ -1,144 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const mem = std.mem;
-
-const server = &@import("../main.zig").server;
-const util = @import("../util.zig");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-/// Switch focus to the passed tags.
-pub fn setFocusedTags(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- const tags = try parseTags(args, out);
- const output = seat.focused_output orelse return;
- if (output.pending.tags != tags) {
- output.previous_tags = output.pending.tags;
- output.pending.tags = tags;
- server.root.applyPending();
- }
-}
-
-pub fn spawnTagmask(
- _: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- const tags = try parseTags(args, out);
- server.config.spawn_tagmask = tags;
-}
-
-/// Set the tags of the focused view.
-pub fn setViewTags(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- const tags = try parseTags(args, out);
- if (seat.focused == .view) {
- const view = seat.focused.view;
- view.pending.tags = tags;
- server.root.applyPending();
- }
-}
-
-/// Toggle focus of the passsed tags.
-pub fn toggleFocusedTags(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- const tags = try parseTags(args, out);
- const output = seat.focused_output orelse return;
- const new_focused_tags = output.pending.tags ^ tags;
- if (new_focused_tags != 0) {
- output.previous_tags = output.pending.tags;
- output.pending.tags = new_focused_tags;
- server.root.applyPending();
- }
-}
-
-/// Toggle the passed tags of the focused view
-pub fn toggleViewTags(
- seat: *Seat,
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!void {
- const tags = try parseTags(args, out);
- if (seat.focused == .view) {
- const new_tags = seat.focused.view.pending.tags ^ tags;
- if (new_tags != 0) {
- const view = seat.focused.view;
- view.pending.tags = new_tags;
- server.root.applyPending();
- }
- }
-}
-
-/// Switch focus to tags that were selected previously
-pub fn focusPreviousTags(
- seat: *Seat,
- args: []const []const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len > 1) return error.TooManyArguments;
- const output = seat.focused_output orelse return;
- const previous_tags = output.previous_tags;
- if (output.pending.tags != previous_tags) {
- output.previous_tags = output.pending.tags;
- output.pending.tags = previous_tags;
- server.root.applyPending();
- }
-}
-
-/// Set the tags of the focused view to the tags that were selected previously
-pub fn sendToPreviousTags(
- seat: *Seat,
- args: []const []const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len > 1) return error.TooManyArguments;
-
- const output = seat.focused_output orelse return;
- if (seat.focused == .view) {
- const view = seat.focused.view;
- view.pending.tags = output.previous_tags;
- server.root.applyPending();
- }
-}
-
-fn parseTags(
- args: []const [:0]const u8,
- out: *?[]const u8,
-) Error!u32 {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- const tags = try std.fmt.parseInt(u32, args[1], 10);
-
- if (tags == 0) {
- out.* = try std.fmt.allocPrint(util.gpa, "tags may not be 0", .{});
- return Error.Other;
- }
-
- return tags;
-}
diff --git a/river/command/toggle_float.zig b/river/command/toggle_float.zig
deleted file mode 100644
index 5185ae4..0000000
--- a/river/command/toggle_float.zig
+++ /dev/null
@@ -1,47 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-/// Make the focused view float or stop floating, depending on its current
-/// state.
-pub fn toggleFloat(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len > 1) return Error.TooManyArguments;
-
- if (seat.focused == .view) {
- const view = seat.focused.view;
-
- // If views are unarranged, don't allow changing the views float status.
- // It would just lead to confusing because this state would not be
- // visible immediately, only after a layout is connected.
- if (view.pending.output == null or view.pending.output.?.layout == null) return;
-
- // Don't float fullscreen views
- if (view.pending.fullscreen) return;
-
- view.pending.float = !view.pending.float;
- server.root.applyPending();
- }
-}
diff --git a/river/command/toggle_fullscreen.zig b/river/command/toggle_fullscreen.zig
deleted file mode 100644
index 5dfef41..0000000
--- a/river/command/toggle_fullscreen.zig
+++ /dev/null
@@ -1,38 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-/// Toggle fullscreen state of the currently focused view
-pub fn toggleFullscreen(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len > 1) return Error.TooManyArguments;
-
- if (seat.focused == .view) {
- const view = seat.focused.view;
-
- view.pending.fullscreen = !view.pending.fullscreen;
- server.root.applyPending();
- }
-}
diff --git a/river/command/view_operations.zig b/river/command/view_operations.zig
deleted file mode 100644
index 4f1319b..0000000
--- a/river/command/view_operations.zig
+++ /dev/null
@@ -1,145 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 - 2023 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const assert = std.debug.assert;
-const wlr = @import("wlroots");
-const flags = @import("flags");
-
-const server = &@import("../main.zig").server;
-
-const Direction = @import("../command.zig").Direction;
-const Error = @import("../command.zig").Error;
-const Output = @import("../Output.zig");
-const Seat = @import("../Seat.zig");
-const View = @import("../View.zig");
-const Vector = @import("../Vector.zig");
-
-/// Focus either the next or the previous visible view, depending on the enum
-/// passed. Does nothing if there are 1 or 0 views in the stack.
-pub fn focusView(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- const result = flags.parser([:0]const u8, &.{
- .{ .name = "skip-floating", .kind = .boolean },
- }).parse(args[1..]) catch {
- return error.InvalidValue;
- };
- if (result.args.len < 1) return Error.NotEnoughArguments;
- if (result.args.len > 1) return Error.TooManyArguments;
-
- if (try getTarget(
- seat,
- result.args[0],
- if (result.flags.@"skip-floating") .skip_float else .all,
- )) |target| {
- assert(!target.pending.fullscreen);
- seat.focus(target);
- server.root.applyPending();
- }
-}
-
-/// Swap the currently focused view with either the view higher or lower in the visible stack
-pub fn swap(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 2) return Error.TooManyArguments;
-
- if (try getTarget(seat, args[1], .skip_float)) |target| {
- assert(!target.pending.float);
- assert(!target.pending.fullscreen);
- seat.focused.view.pending_wm_stack_link.swapWith(&target.pending_wm_stack_link);
- seat.cursor.may_need_warp = true;
- server.root.applyPending();
- }
-}
-
-const TargetMode = enum { all, skip_float };
-fn getTarget(seat: *Seat, direction_str: []const u8, target_mode: TargetMode) !?*View {
- if (seat.focused != .view) return null;
- if (seat.focused.view.pending.fullscreen) return null;
- if (target_mode == .skip_float and seat.focused.view.pending.float) return null;
- const output = seat.focused_output orelse return null;
-
- // If no currently view is focused, focus the first in the stack.
- if (seat.focused != .view) {
- var it = output.pending.wm_stack.iterator(.forward);
- return it.next();
- }
-
- // Logical direction, based on the view stack.
- if (std.meta.stringToEnum(Direction, direction_str)) |direction| {
- switch (direction) {
- inline else => |dir| {
- const it_dir = comptime switch (dir) {
- .next => .forward,
- .previous => .reverse,
- };
- var it = output.pending.wm_stack.iterator(it_dir);
- while (it.next()) |view| {
- if (view == seat.focused.view) break;
- } else {
- unreachable;
- }
-
- // Return the next view in the stack matching the tags if any.
- while (it.next()) |view| {
- if (target_mode == .skip_float and view.pending.float) continue;
- if (output.pending.tags & view.pending.tags != 0) return view;
- }
-
- // Wrap and return the first view in the stack matching the tags if
- // any is found before completing the loop back to the focused view.
- while (it.next()) |view| {
- if (view == seat.focused.view) return null;
- if (target_mode == .skip_float and view.pending.float) continue;
- if (output.pending.tags & view.pending.tags != 0) return view;
- }
-
- unreachable;
- },
- }
- }
-
- // Spatial direction, based on view position.
- if (std.meta.stringToEnum(wlr.OutputLayout.Direction, direction_str)) |direction| {
- const focus_position = Vector.positionOfBox(seat.focused.view.current.box);
- var target: ?*View = null;
- var target_distance: usize = std.math.maxInt(usize);
- var it = output.pending.wm_stack.iterator(.forward);
- while (it.next()) |view| {
- if (output.pending.tags & view.pending.tags == 0) continue;
- if (target_mode == .skip_float and view.pending.float) continue;
- if (view == seat.focused.view) continue;
- const view_position = Vector.positionOfBox(view.current.box);
- const position_diff = focus_position.diff(view_position);
- if ((position_diff.direction() orelse continue) != direction) continue;
- const distance = position_diff.length();
- if (distance < target_distance) {
- target = view;
- target_distance = distance;
- }
- }
- return target;
- }
-
- return Error.InvalidDirection;
-}
diff --git a/river/command/xcursor_theme.zig b/river/command/xcursor_theme.zig
deleted file mode 100644
index 0e1e065..0000000
--- a/river/command/xcursor_theme.zig
+++ /dev/null
@@ -1,34 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-
-pub fn xcursorTheme(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len < 2) return Error.NotEnoughArguments;
- if (args.len > 3) return Error.TooManyArguments;
-
- const name = args[1];
- const size = if (args.len == 3) try std.fmt.parseInt(u32, args[2], 10) else null;
-
- try seat.cursor.setTheme(name, size);
-}
diff --git a/river/command/zoom.zig b/river/command/zoom.zig
deleted file mode 100644
index 7db3500..0000000
--- a/river/command/zoom.zig
+++ /dev/null
@@ -1,85 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const assert = std.debug.assert;
-
-const server = &@import("../main.zig").server;
-
-const Error = @import("../command.zig").Error;
-const Seat = @import("../Seat.zig");
-const View = @import("../View.zig");
-
-/// Bump the focused view to the top of the stack. If the view on the top of
-/// the stack is focused, bump the second view to the top.
-pub fn zoom(
- seat: *Seat,
- args: []const [:0]const u8,
- _: *?[]const u8,
-) Error!void {
- if (args.len > 1) return Error.TooManyArguments;
-
- if (seat.focused != .view) return;
- if (seat.focused.view.pending.float or seat.focused.view.pending.fullscreen) return;
-
- const output = seat.focused_output orelse return;
-
- const layout_first = blk: {
- var it = output.pending.wm_stack.iterator(.forward);
- while (it.next()) |view| {
- if (view.pending.tags & output.pending.tags != 0 and !view.pending.float) break :blk view;
- } else {
- // If we are focusing a view that is not fullscreen or floating
- // it must be visible and in the layout.
- unreachable;
- }
- };
-
- // If the first view that is part of the layout is focused, zoom
- // the next view in the layout if any. Otherwise zoom the focused view.
- const zoom_target = blk: {
- if (seat.focused.view == layout_first) {
- var it = output.pending.wm_stack.iterator(.forward);
- while (it.next()) |view| {
- if (view == seat.focused.view) break;
- } else {
- unreachable;
- }
-
- while (it.next()) |view| {
- if (view.pending.tags & output.pending.tags != 0 and !view.pending.float) break :blk view;
- } else {
- break :blk null;
- }
- } else {
- break :blk seat.focused.view;
- }
- };
-
- if (zoom_target) |target| {
- assert(!target.pending.float);
- assert(!target.pending.fullscreen);
-
- target.pending_wm_stack_link.remove();
- output.pending.wm_stack.prepend(target);
- seat.focus(target);
- // Focus may not actually change here so seat.focus() may not automatically warp the cursor.
- // Nevertheless, a cursor warp seems to be what users expect with `set-cursor-warp on-focus`
- // configured, especially in combination with focus-follows-cursor.
- seat.cursor.may_need_warp = true;
- server.root.applyPending();
- }
-}