Skip to content
54 changes: 46 additions & 8 deletions rift.default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ hot_reload = true

[settings.layout]
# Layout system
# - mode: "traditional" (i3/sway-like containers) or "bsp" (binary space partitioning)
# - mode: "traditional" (i3/sway-like containers), "bsp" (binary space partitioning),
# or "dwindle" (Hypr-style spiral BSP)
# defaults to "traditional" if omitted
mode = "traditional"

Expand All @@ -93,6 +94,34 @@ stack_offset = 40.0
# - "horizontal"/"vertical": force a specific orientation
default_orientation = "perpendicular"

# Dwindle-specific settings (only used when mode = "dwindle").
# Uncomment and adjust if you enable the dwindle layout mode.
# [settings.layout.dwindle]
# # Initial split ratio when creating a new parent (Hypr-compatible 0.1..1.9)
# default_split_ratio = 1.0
# # Heuristic for deciding horizontal vs vertical splits based on aspect ratio
# split_width_multiplier = 1.0
# # Use cursor quadrant to decide split orientation/direction
# smart_split = false
# # Keep existing split orientation unless overridden
# preserve_split = false
# # Force split side: 0 follow heuristic, 1 force first (left/top), 2 force second (right/bottom)
# force_split = 0
# # Bias split toward the newly inserted window
# split_bias = false
# # Prefer selection over cursor when choosing insertion target
# use_active_for_splits = true
# # Keep preselect direction sticky after one insert
# permanent_direction_override = false
# # Adjust ratios based on cursor extent when resizing
# smart_resizing = true
# # Keep floating size when tiled
# pseudotile = false
# # Enforce aspect when only one window is present; set second to 0 to disable
# single_window_aspect_ratio = [0.0, 0.0]
# # Fractional padding required before aspect enforcement kicks in
# single_window_aspect_ratio_tolerance = 0.1

[settings.layout.gaps]
# Gap configuration
# - outer: space between windows and screen edges
Expand All @@ -109,8 +138,10 @@ bottom = 0
right = 0

[settings.layout.gaps.inner]
horizontal = 0
vertical = 0
left = 0
right = 0
top = 0
bottom = 0

# Example per-display overrides:
# Replace the quoted key with your display's UUID. Only specify values you want to override.
Expand Down Expand Up @@ -306,7 +337,7 @@ comb1 = "Alt + Shift"
# - join_window = "left"|"right"|"up"|"down"
# - stack_windows / unstack_windows / unjoin_windows
# - toggle_focus_floating / toggle_window_floating / toggle_fullscreen
# - resize_window_grow / resize_window_shrink
# - resize_active = { delta = { x = { type = "pixels", value = N }, y = { type = "pixels", value = N } }, corner = "bottom_right" }
# - move_mouse_to_display = N or UUID
# - focus_display = { direction = "left"|"right"|"up"|"down" }
# - focus_display = { index = N }
Expand Down Expand Up @@ -350,10 +381,17 @@ comb1 = "Alt + Shift"
"Alt + F" = "toggle_fullscreen"
"Alt + Shift + F" = "toggle_fullscreen_within_gaps"
"comb1 + Ctrl + Space" = "toggle_focus_floating" # briefly bring focus to floating window

# smartly resize windows
"Alt + Shift + Equal" = "resize_window_grow"
"Alt + Shift + Minus" = "resize_window_shrink"
"Alt + Shift + T" = "toggle_split"
"Alt + Shift + S" = "swap_split"
"Alt + Shift + M" = { move_to_root = { stable = true } }
"Alt + Ctrl + Shift + H" = { preselect = "left" }
"Alt + Ctrl + Shift + L" = { preselect = "right" }
"Alt + Ctrl + Shift + K" = { preselect = "up" }
"Alt + Ctrl + Shift + J" = { preselect = "down" }

# smartly resize windows (Hyprland-style resizeactive)
"Alt + Shift + Equal" = { resize_active = { delta = { x = { type = "pixels", value = 50.0 }, y = { type = "pixels", value = 50.0 } } } }
"Alt + Shift + Minus" = { resize_active = { delta = { x = { type = "pixels", value = -50.0 }, y = { type = "pixels", value = -50.0 } } } }

# Move mouse cursor to display by index (0-based) or UUID
# Examples:
Expand Down
6 changes: 3 additions & 3 deletions src/actor/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ impl ConfigActor {
}
ConfigCommand::SetInnerGaps { horizontal, vertical } => {
if horizontal >= 0.0 && vertical >= 0.0 {
let gaps = &mut new_config.settings.layout.gaps.inner;
gaps.horizontal = horizontal;
gaps.vertical = vertical;
// Use symmetric gaps (same for both sides of each axis)
new_config.settings.layout.gaps.inner =
crate::common::config::InnerGaps::symmetric(horizontal, vertical);
config_changed = true;
info!(
"Updated inner gaps to: horizontal={}, vertical={}",
Expand Down
52 changes: 32 additions & 20 deletions src/bin/rift-cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use clap::{Parser, Subcommand};
use rift_wm::actor::reactor::{self, DisplaySelector};
use rift_wm::ipc::{RiftCommand, RiftMachClient, RiftRequest, RiftResponse};
use rift_wm::layout_engine as layout;
use rift_wm::layout_engine::{ResizeCorner, ResizeDelta, ResizeMode, ResizeValue};
use rift_wm::sys::window_server::WindowServerId;
use serde_json::Value;

Expand Down Expand Up @@ -132,17 +133,15 @@ enum WindowCommands {
ToggleFullscreen,
/// Toggle fullscreen within configured outer gaps (respects outer gaps / fills tiling area)
ToggleFullscreenWithinGaps,
/// Grow the current window size (increments by ~5%).
ResizeGrow,
/// Shrink the current window size (decrements by ~5%).
ResizeShrink,
/// Resize the selected window by a fractional amount.
/// - Pass a signed floating value: positive to grow, negative to shrink.
/// - The value is a fraction of the current size (e.g. `0.05` = 5%).
/// Examples:
/// rift-cli execute window resize-by --amount 0.05 # grow by 5%
/// rift-cli execute window resize-by --amount -0.10 # shrink by 10%
ResizeBy { amount: f64 },
/// Hyprland-style resizeactive: resize by pixel/percentage with optional corner/exact mode.
ResizeActive {
x: String,
y: String,
#[arg(long)]
corner: Option<String>,
#[arg(long, default_value_t = false)]
exact: bool,
},
/// Close a window by window server identifier
Close {
/// Window Id (window server id or idx from window id)
Expand Down Expand Up @@ -504,15 +503,28 @@ fn map_window_command(cmd: WindowCommands) -> Result<RiftCommand, String> {
WindowCommands::ToggleFullscreenWithinGaps => Ok(RiftCommand::Reactor(
reactor::Command::Layout(LC::ToggleFullscreenWithinGaps),
)),
WindowCommands::ResizeGrow => Ok(RiftCommand::Reactor(reactor::Command::Layout(
LC::ResizeWindowGrow,
))),
WindowCommands::ResizeShrink => Ok(RiftCommand::Reactor(reactor::Command::Layout(
LC::ResizeWindowShrink,
))),
WindowCommands::ResizeBy { amount } => Ok(RiftCommand::Reactor(reactor::Command::Layout(
LC::ResizeWindowBy { amount },
))),
WindowCommands::ResizeActive { x, y, corner, exact } => {
let corner = match corner.as_deref() {
Some("topleft") | Some("tl") => ResizeCorner::TopLeft,
Some("topright") | Some("tr") => ResizeCorner::TopRight,
Some("bottomleft") | Some("bl") => ResizeCorner::BottomLeft,
Some("bottomright") | Some("br") => ResizeCorner::BottomRight,
_ => ResizeCorner::None,
};
let mode = if exact {
ResizeMode::Exact
} else {
ResizeMode::Relative
};
let x_val = ResizeValue::parse(&x)
.ok_or_else(|| "Invalid x value for resizeactive".to_string())?;
let y_val = ResizeValue::parse(&y)
.ok_or_else(|| "Invalid y value for resizeactive".to_string())?;
let delta = ResizeDelta { x: x_val, y: y_val, mode };
Ok(RiftCommand::Reactor(reactor::Command::Layout(
LC::ResizeActive { delta, corner },
)))
}
WindowCommands::Close { window_id } => {
let wsid = parse_window_server_id(&window_id)?;
Ok(RiftCommand::Reactor(reactor::Command::Reactor(
Expand Down
Loading