Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eece44a59c | |||
| 2167484861 |
77 changed files with 433 additions and 8378 deletions
|
|
@ -77,20 +77,6 @@ You can also right-click any title in a container to toggle mono mode.
|
|||
In mono mode, scroll over the title bar to cycle between windows in the
|
||||
container.
|
||||
|
||||
## Autotiling
|
||||
|
||||
Autotiling makes newly tiled windows alternate split direction from the focused
|
||||
tiled window. The first split uses the containing group direction, then later
|
||||
windows wrap the focused tile in the opposite direction, producing a horizontal,
|
||||
vertical, horizontal pattern as the layout grows.
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-a = "toggle-autotile"
|
||||
```
|
||||
|
||||
Manual grouping and split commands still use the direction you request.
|
||||
|
||||
## Fullscreen
|
||||
|
||||
Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire
|
||||
|
|
|
|||
|
|
@ -640,22 +640,6 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetWindowWorkspace { window, workspace });
|
||||
}
|
||||
|
||||
pub fn seat_send_to_scratchpad(&self, seat: Seat, name: &str) {
|
||||
self.send(&ClientMessage::SeatSendToScratchpad { seat, name });
|
||||
}
|
||||
|
||||
pub fn seat_toggle_scratchpad(&self, seat: Seat, name: &str) {
|
||||
self.send(&ClientMessage::SeatToggleScratchpad { seat, name });
|
||||
}
|
||||
|
||||
pub fn seat_cycle_scratchpad(&self, seat: Seat, name: &str) {
|
||||
self.send(&ClientMessage::SeatCycleScratchpad { seat, name });
|
||||
}
|
||||
|
||||
pub fn window_send_to_scratchpad(&self, window: Window, name: &str) {
|
||||
self.send(&ClientMessage::WindowSendToScratchpad { window, name });
|
||||
}
|
||||
|
||||
pub fn seat_split(&self, seat: Seat) -> Axis {
|
||||
let res = self.send_with_response(&ClientMessage::GetSeatSplit { seat });
|
||||
get_response!(res, Axis::Horizontal, GetSplit { axis });
|
||||
|
|
@ -1039,26 +1023,6 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetUiDragThreshold { threshold });
|
||||
}
|
||||
|
||||
pub fn set_animations_enabled(&self, enabled: bool) {
|
||||
self.send(&ClientMessage::SetAnimationsEnabled { enabled });
|
||||
}
|
||||
|
||||
pub fn set_animation_duration_ms(&self, duration_ms: u32) {
|
||||
self.send(&ClientMessage::SetAnimationDurationMs { duration_ms });
|
||||
}
|
||||
|
||||
pub fn set_animation_curve(&self, curve: u32) {
|
||||
self.send(&ClientMessage::SetAnimationCurve { curve });
|
||||
}
|
||||
|
||||
pub fn set_animation_style(&self, style: u32) {
|
||||
self.send(&ClientMessage::SetAnimationStyle { style });
|
||||
}
|
||||
|
||||
pub fn set_animation_cubic_bezier(&self, x1: f32, y1: f32, x2: f32, y2: f32) {
|
||||
self.send(&ClientMessage::SetAnimationCubicBezier { x1, y1, x2, y2 });
|
||||
}
|
||||
|
||||
pub fn set_color_management_enabled(&self, enabled: bool) {
|
||||
self.send(&ClientMessage::SetColorManagementEnabled { enabled });
|
||||
}
|
||||
|
|
@ -1363,6 +1327,14 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetIdle { timeout })
|
||||
}
|
||||
|
||||
pub fn set_key_press_enables_dpms(&self, enabled: bool) {
|
||||
self.send(&ClientMessage::SetKeyPressEnablesDpms { enabled })
|
||||
}
|
||||
|
||||
pub fn set_mouse_move_enables_dpms(&self, enabled: bool) {
|
||||
self.send(&ClientMessage::SetMouseMoveEnablesDpms { enabled })
|
||||
}
|
||||
|
||||
pub fn set_idle_grace_period(&self, period: Duration) {
|
||||
self.send(&ClientMessage::SetIdleGracePeriod { period })
|
||||
}
|
||||
|
|
@ -2095,12 +2067,6 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetAutotile { enabled });
|
||||
}
|
||||
|
||||
pub fn get_autotile(&self) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::GetAutotile);
|
||||
get_response!(res, false, GetAutotile { enabled });
|
||||
enabled
|
||||
}
|
||||
|
||||
pub fn set_tab_title_align(&self, align: u32) {
|
||||
self.send(&ClientMessage::SetTabTitleAlign { align });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -286,18 +286,6 @@ pub enum ClientMessage<'a> {
|
|||
seat: Seat,
|
||||
workspace: Workspace,
|
||||
},
|
||||
SeatSendToScratchpad {
|
||||
seat: Seat,
|
||||
name: &'a str,
|
||||
},
|
||||
SeatToggleScratchpad {
|
||||
seat: Seat,
|
||||
name: &'a str,
|
||||
},
|
||||
SeatCycleScratchpad {
|
||||
seat: Seat,
|
||||
name: &'a str,
|
||||
},
|
||||
GetTimer {
|
||||
name: &'a str,
|
||||
},
|
||||
|
|
@ -487,6 +475,12 @@ pub enum ClientMessage<'a> {
|
|||
SetIdle {
|
||||
timeout: Duration,
|
||||
},
|
||||
SetKeyPressEnablesDpms {
|
||||
enabled: bool,
|
||||
},
|
||||
SetMouseMoveEnablesDpms {
|
||||
enabled: bool,
|
||||
},
|
||||
MoveToOutput {
|
||||
workspace: WorkspaceSource,
|
||||
connector: Connector,
|
||||
|
|
@ -557,24 +551,6 @@ pub enum ClientMessage<'a> {
|
|||
SetUiDragThreshold {
|
||||
threshold: i32,
|
||||
},
|
||||
SetAnimationsEnabled {
|
||||
enabled: bool,
|
||||
},
|
||||
SetAnimationDurationMs {
|
||||
duration_ms: u32,
|
||||
},
|
||||
SetAnimationCurve {
|
||||
curve: u32,
|
||||
},
|
||||
SetAnimationStyle {
|
||||
style: u32,
|
||||
},
|
||||
SetAnimationCubicBezier {
|
||||
x1: f32,
|
||||
y1: f32,
|
||||
x2: f32,
|
||||
y2: f32,
|
||||
},
|
||||
SetXScalingMode {
|
||||
mode: XScalingMode,
|
||||
},
|
||||
|
|
@ -699,10 +675,6 @@ pub enum ClientMessage<'a> {
|
|||
window: Window,
|
||||
workspace: Workspace,
|
||||
},
|
||||
WindowSendToScratchpad {
|
||||
window: Window,
|
||||
name: &'a str,
|
||||
},
|
||||
SetWindowFullscreen {
|
||||
window: Window,
|
||||
fullscreen: bool,
|
||||
|
|
@ -939,7 +911,6 @@ pub enum ClientMessage<'a> {
|
|||
SetAutotile {
|
||||
enabled: bool,
|
||||
},
|
||||
GetAutotile,
|
||||
SetTabTitleAlign {
|
||||
align: u32,
|
||||
},
|
||||
|
|
@ -1206,9 +1177,6 @@ pub enum Response {
|
|||
GetCornerRadius {
|
||||
radius: f32,
|
||||
},
|
||||
GetAutotile {
|
||||
enabled: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -466,33 +466,6 @@ impl Seat {
|
|||
get!().set_seat_workspace(self, workspace)
|
||||
}
|
||||
|
||||
/// Sends the currently focused window to a scratchpad.
|
||||
///
|
||||
/// Use an empty string for the default scratchpad.
|
||||
pub fn send_to_scratchpad(self, name: &str) {
|
||||
get!().seat_send_to_scratchpad(self, name)
|
||||
}
|
||||
|
||||
/// Toggles a scratchpad.
|
||||
///
|
||||
/// If the scratchpad has a visible window, that window is hidden. Otherwise, the
|
||||
/// most recently hidden window in the scratchpad is shown on the current workspace.
|
||||
/// Scratchpad windows are always shown floating.
|
||||
/// Use an empty string for the default scratchpad.
|
||||
pub fn toggle_scratchpad(self, name: &str) {
|
||||
get!().seat_toggle_scratchpad(self, name)
|
||||
}
|
||||
|
||||
/// Cycles through the windows of a scratchpad, one at a time.
|
||||
///
|
||||
/// With nothing shown, the first window is brought up; each further invocation
|
||||
/// hides the current window and shows the next; after the last window the
|
||||
/// scratchpad is hidden again. Scratchpad windows are always shown floating.
|
||||
/// Use an empty string for the default scratchpad.
|
||||
pub fn cycle_scratchpad(self, name: &str) {
|
||||
get!().seat_cycle_scratchpad(self, name)
|
||||
}
|
||||
|
||||
/// Toggles whether the currently focused window is fullscreen.
|
||||
pub fn toggle_fullscreen(self) {
|
||||
let c = get!();
|
||||
|
|
|
|||
|
|
@ -103,27 +103,6 @@ impl Axis {
|
|||
}
|
||||
}
|
||||
|
||||
/// The curve used for tiled window animations.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AnimationCurve(pub u32);
|
||||
|
||||
impl AnimationCurve {
|
||||
pub const LINEAR: Self = Self(0);
|
||||
pub const EASE: Self = Self(1);
|
||||
pub const EASE_IN: Self = Self(2);
|
||||
pub const EASE_OUT: Self = Self(3);
|
||||
pub const EASE_IN_OUT: Self = Self(4);
|
||||
}
|
||||
|
||||
/// The presentation style used for tiled window movement animations.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AnimationStyle(pub u32);
|
||||
|
||||
impl AnimationStyle {
|
||||
pub const PLAIN: Self = Self(0);
|
||||
pub const MULTIPHASE: Self = Self(1);
|
||||
}
|
||||
|
||||
/// Exits the compositor.
|
||||
pub fn quit() {
|
||||
get!().quit()
|
||||
|
|
@ -273,6 +252,20 @@ pub fn set_idle(timeout: Option<Duration>) {
|
|||
get!().set_idle(timeout.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Configures whether a key press turns monitors back on after `jay dpms off`.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub fn set_key_press_enables_dpms(enabled: bool) {
|
||||
get!().set_key_press_enables_dpms(enabled)
|
||||
}
|
||||
|
||||
/// Configures whether mouse movement turns monitors back on after `jay dpms off`.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub fn set_mouse_move_enables_dpms(enabled: bool) {
|
||||
get!().set_mouse_move_enables_dpms(enabled)
|
||||
}
|
||||
|
||||
/// Configures the idle grace period.
|
||||
///
|
||||
/// The grace period starts after the idle timeout expires. During the grace period, the
|
||||
|
|
@ -308,42 +301,6 @@ pub fn set_ui_drag_threshold(threshold: i32) {
|
|||
get!().set_ui_drag_threshold(threshold);
|
||||
}
|
||||
|
||||
/// Enables or disables tiled window animations.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub fn set_animations_enabled(enabled: bool) {
|
||||
get!().set_animations_enabled(enabled);
|
||||
}
|
||||
|
||||
/// Sets the duration of tiled window animations in milliseconds.
|
||||
///
|
||||
/// The default is `160`.
|
||||
pub fn set_animation_duration_ms(duration_ms: u32) {
|
||||
get!().set_animation_duration_ms(duration_ms);
|
||||
}
|
||||
|
||||
/// Sets the curve used by tiled window animations.
|
||||
///
|
||||
/// The default is [`AnimationCurve::EASE_OUT`].
|
||||
pub fn set_animation_curve(curve: AnimationCurve) {
|
||||
get!().set_animation_curve(curve.0);
|
||||
}
|
||||
|
||||
/// Sets the presentation style used for tiled window movement animations.
|
||||
///
|
||||
/// The default is [`AnimationStyle::MULTIPHASE`].
|
||||
pub fn set_animation_style(style: AnimationStyle) {
|
||||
get!().set_animation_style(style.0);
|
||||
}
|
||||
|
||||
/// Sets a custom cubic-bezier curve used by tiled window animations.
|
||||
///
|
||||
/// `x1` and `x2` must be between `0.0` and `1.0`. The curve starts at `(0, 0)`
|
||||
/// and ends at `(1, 1)`.
|
||||
pub fn set_animation_cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32) {
|
||||
get!().set_animation_cubic_bezier(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
/// Enables or disables the color-management protocol.
|
||||
///
|
||||
/// The default is `false`.
|
||||
|
|
@ -453,21 +410,14 @@ pub fn get_corner_radius() -> f32 {
|
|||
|
||||
/// Enables or disables autotiling.
|
||||
///
|
||||
/// When enabled, newly tiled windows alternate split orientation from the
|
||||
/// focused tiled window: the first split uses the containing group's direction,
|
||||
/// then subsequent splits wrap the focused window in the perpendicular
|
||||
/// direction.
|
||||
/// When enabled, new windows are automatically placed in a perpendicular
|
||||
/// sub-container if the predicted body would be narrower than tall (or vice versa).
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub fn set_autotile(enabled: bool) {
|
||||
get!().set_autotile(enabled)
|
||||
}
|
||||
|
||||
/// Returns whether autotiling is enabled.
|
||||
pub fn get_autotile() -> bool {
|
||||
get!(false).get_autotile()
|
||||
}
|
||||
|
||||
/// Sets the horizontal alignment of title text within tab buttons.
|
||||
///
|
||||
/// - `"start"` — left-aligned (default)
|
||||
|
|
|
|||
|
|
@ -205,13 +205,6 @@ impl Window {
|
|||
get!().set_window_workspace(self, workspace)
|
||||
}
|
||||
|
||||
/// Sends the window to a scratchpad.
|
||||
///
|
||||
/// Use an empty string for the default scratchpad.
|
||||
pub fn send_to_scratchpad(self, name: &str) {
|
||||
get!().window_send_to_scratchpad(self, name)
|
||||
}
|
||||
|
||||
/// Toggles whether the currently focused window is fullscreen.
|
||||
pub fn toggle_fullscreen(self) {
|
||||
self.set_fullscreen(!self.fullscreen())
|
||||
|
|
|
|||
1233
src/animation.rs
1233
src/animation.rs
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
17
src/cli.rs
17
src/cli.rs
|
|
@ -3,6 +3,7 @@ mod color;
|
|||
mod color_management;
|
||||
mod config;
|
||||
mod damage_tracking;
|
||||
mod dpms;
|
||||
mod duration;
|
||||
mod generate;
|
||||
mod idle;
|
||||
|
|
@ -85,6 +86,8 @@ pub enum Cmd {
|
|||
Screenshot(ScreenshotArgs),
|
||||
/// Inspect/modify the idle (screensaver) settings.
|
||||
Idle(IdleArgs),
|
||||
/// Turn monitors on or off.
|
||||
Dpms(DpmsArgs),
|
||||
/// Run a privileged program.
|
||||
RunPrivileged(RunPrivilegedArgs),
|
||||
/// Run a program with a connection tag.
|
||||
|
|
@ -131,6 +134,19 @@ pub struct IdleArgs {
|
|||
pub command: Option<IdleCmd>,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct DpmsArgs {
|
||||
/// Whether monitors should be on or off.
|
||||
#[clap(value_enum)]
|
||||
pub state: DpmsState,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum DpmsState {
|
||||
On,
|
||||
Off,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct RunPrivilegedArgs {
|
||||
/// The program to run
|
||||
|
|
@ -250,6 +266,7 @@ pub fn main() {
|
|||
Cmd::SetLogLevel(a) => set_log_level::main(cli.global, a),
|
||||
Cmd::Screenshot(a) => screenshot::main(cli.global, a),
|
||||
Cmd::Idle(a) => idle::main(cli.global, a),
|
||||
Cmd::Dpms(a) => dpms::main(cli.global, a),
|
||||
Cmd::Unlock => unlock::main(cli.global),
|
||||
Cmd::RunPrivileged(a) => run_privileged::main(cli.global, a),
|
||||
Cmd::RunTagged(a) => run_tagged::main(cli.global, a),
|
||||
|
|
|
|||
23
src/cli/dpms.rs
Normal file
23
src/cli/dpms.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::{DpmsArgs, DpmsState, GlobalArgs},
|
||||
tools::tool_client::{ToolClient, with_tool_client},
|
||||
wire::jay_compositor::SetDpms,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub fn main(global: GlobalArgs, args: DpmsArgs) {
|
||||
with_tool_client(global.log_level, |tc| async move {
|
||||
run(tc, args).await;
|
||||
});
|
||||
}
|
||||
|
||||
async fn run(tc: Rc<ToolClient>, args: DpmsArgs) {
|
||||
let comp = tc.jay_compositor().await;
|
||||
tc.send(SetDpms {
|
||||
self_id: comp,
|
||||
active: (args.state == DpmsState::On) as u32,
|
||||
});
|
||||
tc.round_trip().await;
|
||||
}
|
||||
|
|
@ -279,11 +279,14 @@ fn start_compositor2(
|
|||
change: Default::default(),
|
||||
timeout: Cell::new(Duration::from_secs(10 * 60)),
|
||||
grace_period: Cell::new(Duration::from_secs(5)),
|
||||
key_press_enables_dpms: Cell::new(false),
|
||||
mouse_move_enables_dpms: Cell::new(false),
|
||||
timeout_changed: Default::default(),
|
||||
inhibitors: Default::default(),
|
||||
inhibitors_changed: Default::default(),
|
||||
inhibited_idle_notifications: Default::default(),
|
||||
backend_idle: Cell::new(true),
|
||||
dpms_off_by_command: Cell::new(false),
|
||||
in_grace_period: Cell::new(false),
|
||||
},
|
||||
run_args,
|
||||
|
|
@ -360,13 +363,6 @@ fn start_compositor2(
|
|||
cpu_worker,
|
||||
ui_drag_enabled: Cell::new(true),
|
||||
ui_drag_threshold_squared: Cell::new(10),
|
||||
animations: Default::default(),
|
||||
layout_animations_requested: Default::default(),
|
||||
layout_animations_active: Default::default(),
|
||||
layout_animation_curve_override: Default::default(),
|
||||
layout_animation_style_override: Default::default(),
|
||||
layout_animation_batch: Default::default(),
|
||||
suppress_animations_for_next_layout: Default::default(),
|
||||
toplevels: Default::default(),
|
||||
const_40hz_latch: Default::default(),
|
||||
tray_item_ids: Default::default(),
|
||||
|
|
@ -403,7 +399,6 @@ fn start_compositor2(
|
|||
bo_drop_queue: Rc::new(ObjectDropQueue::new(&ring)),
|
||||
virtual_outputs: Default::default(),
|
||||
clean_logs_older_than: Default::default(),
|
||||
scratchpads: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
|
|||
|
|
@ -658,23 +658,17 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_seat_move(&self, seat: Seat, direction: Direction) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_focused(direction.into());
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_focused(direction.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_move(&self, window: Window, direction: Direction) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(float) = window.tl_data().float.get() {
|
||||
float.move_by_direction(direction.into());
|
||||
} else if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.move_child(window, direction.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.move_child(window, direction.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_repeat_rate(&self, seat: Seat) -> Result<(), CphError> {
|
||||
|
|
@ -992,31 +986,6 @@ impl ConfigProxyHandler {
|
|||
self.state.set_ui_drag_threshold(threshold.max(1));
|
||||
}
|
||||
|
||||
fn handle_set_animations_enabled(&self, enabled: bool) {
|
||||
self.state.set_animations_enabled(enabled);
|
||||
}
|
||||
|
||||
fn handle_set_animation_duration_ms(&self, duration_ms: u32) {
|
||||
self.state
|
||||
.set_animation_duration_ms(duration_ms.min(10_000));
|
||||
}
|
||||
|
||||
fn handle_set_animation_curve(&self, curve: u32) {
|
||||
self.state.set_animation_curve(curve);
|
||||
}
|
||||
|
||||
fn handle_set_animation_style(&self, style: u32) {
|
||||
if !self.state.set_animation_style(style) {
|
||||
log::warn!("Ignoring invalid animation style");
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_set_animation_cubic_bezier(&self, x1: f32, y1: f32, x2: f32, y2: f32) {
|
||||
if !self.state.set_animation_cubic_bezier(x1, y1, x2, y2) {
|
||||
log::warn!("Ignoring invalid animation cubic-bezier curve");
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_set_direct_scanout_enabled(
|
||||
&self,
|
||||
device: Option<DrmDevice>,
|
||||
|
|
@ -1100,32 +1069,6 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_send_to_scratchpad(&self, seat: Seat, name: &str) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
if let Some(toplevel) = seat.get_keyboard_node().node_toplevel() {
|
||||
self.state.send_to_scratchpad(name, toplevel);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_seat_toggle_scratchpad(&self, seat: Seat, name: &str) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.state.toggle_scratchpad(&seat, name);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_seat_cycle_scratchpad(&self, seat: Seat, name: &str) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.state.cycle_scratchpad(&seat, name);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_set_window_workspace(&self, window: Window, ws: Workspace) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
let name = self.get_workspace(ws)?;
|
||||
|
|
@ -1140,14 +1083,6 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_send_to_scratchpad(&self, window: Window, name: &str) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
self.state.send_to_scratchpad(name, window);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_get_device_name(&self, device: InputDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let name = dev.device.name();
|
||||
|
|
@ -1199,6 +1134,14 @@ impl ConfigProxyHandler {
|
|||
self.state.idle.set_timeout(&self.state, timeout);
|
||||
}
|
||||
|
||||
fn handle_set_key_press_enables_dpms(&self, enabled: bool) {
|
||||
self.state.idle.key_press_enables_dpms.set(enabled);
|
||||
}
|
||||
|
||||
fn handle_set_mouse_move_enables_dpms(&self, enabled: bool) {
|
||||
self.state.idle.mouse_move_enables_dpms.set(enabled);
|
||||
}
|
||||
|
||||
fn handle_set_idle_grace_period(&self, period: Duration) {
|
||||
self.state.idle.set_grace_period(&self.state, period);
|
||||
}
|
||||
|
|
@ -1789,11 +1732,9 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_set_seat_mono(&self, seat: Seat, mono: bool) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_mono(mono);
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_mono(mono);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_mono(&self, window: Window) -> Result<(), CphError> {
|
||||
|
|
@ -1807,13 +1748,11 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_set_window_mono(&self, window: Window, mono: bool) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_mono(mono.then_some(window.as_ref()));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_mono(mono.then_some(window.as_ref()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_seat_split(&self, seat: Seat) -> Result<(), CphError> {
|
||||
|
|
@ -1828,19 +1767,15 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_set_seat_split(&self, seat: Seat, axis: Axis) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_split(axis.into());
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_split(axis.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_toggle_tab(&self, seat: Seat) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.toggle_tab();
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.toggle_tab();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_make_group(
|
||||
|
|
@ -1849,35 +1784,27 @@ impl ConfigProxyHandler {
|
|||
axis: Axis,
|
||||
ephemeral: bool,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.make_group(axis.into(), ephemeral);
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.make_group(axis.into(), ephemeral);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_change_group_opposite(&self, seat: Seat) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.change_group_opposite();
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.change_group_opposite();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_equalize(&self, seat: Seat, recursive: bool) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.equalize(recursive);
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.equalize(recursive);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_move_tab(&self, seat: Seat, right: bool) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_tab(right);
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_tab(right);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_split(&self, window: Window) -> Result<(), CphError> {
|
||||
|
|
@ -1892,13 +1819,11 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_set_window_split(&self, window: Window, axis: Axis) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_split(axis.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_split(axis.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_add_shortcut(
|
||||
|
|
@ -2038,11 +1963,9 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_set_seat_floating(&self, seat: Seat, floating: bool) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_floating(floating);
|
||||
Ok(())
|
||||
})
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_floating(floating);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_floating(&self, window: Window) -> Result<(), CphError> {
|
||||
|
|
@ -2054,11 +1977,9 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_set_window_floating(&self, window: Window, floating: bool) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
toplevel_set_floating(&self.state, window, floating);
|
||||
Ok(())
|
||||
})
|
||||
let window = self.get_window(window)?;
|
||||
toplevel_set_floating(&self.state, window, floating);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_add_pollable(self: &Rc<Self>, fd: i32) -> Result<(), CphError> {
|
||||
|
|
@ -2808,10 +2729,8 @@ impl ConfigProxyHandler {
|
|||
dx2: i32,
|
||||
dy2: i32,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
self.get_window(window)?.tl_resize(dx1, dy1, dx2, dy2);
|
||||
Ok(())
|
||||
})
|
||||
self.get_window(window)?.tl_resize(dx1, dy1, dx2, dy2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_exists(&self, window: Window) {
|
||||
|
|
@ -3023,15 +2942,6 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetSeatWorkspace { seat, workspace } => self
|
||||
.handle_set_seat_workspace(seat, workspace)
|
||||
.wrn("set_seat_workspace")?,
|
||||
ClientMessage::SeatSendToScratchpad { seat, name } => self
|
||||
.handle_seat_send_to_scratchpad(seat, name)
|
||||
.wrn("seat_send_to_scratchpad")?,
|
||||
ClientMessage::SeatToggleScratchpad { seat, name } => self
|
||||
.handle_seat_toggle_scratchpad(seat, name)
|
||||
.wrn("seat_toggle_scratchpad")?,
|
||||
ClientMessage::SeatCycleScratchpad { seat, name } => self
|
||||
.handle_seat_cycle_scratchpad(seat, name)
|
||||
.wrn("seat_cycle_scratchpad")?,
|
||||
ClientMessage::GetConnector { ty, idx } => {
|
||||
self.handle_get_connector(ty, idx).wrn("get_connector")?
|
||||
}
|
||||
|
|
@ -3227,6 +3137,12 @@ impl ConfigProxyHandler {
|
|||
.handle_get_input_device_devnode(device)
|
||||
.wrn("get_input_device_devnode")?,
|
||||
ClientMessage::SetIdle { timeout } => self.handle_set_idle(timeout),
|
||||
ClientMessage::SetKeyPressEnablesDpms { enabled } => {
|
||||
self.handle_set_key_press_enables_dpms(enabled)
|
||||
}
|
||||
ClientMessage::SetMouseMoveEnablesDpms { enabled } => {
|
||||
self.handle_set_mouse_move_enables_dpms(enabled)
|
||||
}
|
||||
ClientMessage::MoveToOutput {
|
||||
workspace,
|
||||
connector,
|
||||
|
|
@ -3291,17 +3207,6 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetUiDragThreshold { threshold } => {
|
||||
self.handle_set_ui_drag_threshold(threshold)
|
||||
}
|
||||
ClientMessage::SetAnimationsEnabled { enabled } => {
|
||||
self.handle_set_animations_enabled(enabled)
|
||||
}
|
||||
ClientMessage::SetAnimationDurationMs { duration_ms } => {
|
||||
self.handle_set_animation_duration_ms(duration_ms)
|
||||
}
|
||||
ClientMessage::SetAnimationCurve { curve } => self.handle_set_animation_curve(curve),
|
||||
ClientMessage::SetAnimationStyle { style } => self.handle_set_animation_style(style),
|
||||
ClientMessage::SetAnimationCubicBezier { x1, y1, x2, y2 } => {
|
||||
self.handle_set_animation_cubic_bezier(x1, y1, x2, y2)
|
||||
}
|
||||
ClientMessage::SetXScalingMode { mode } => self
|
||||
.handle_set_x_scaling_mode(mode)
|
||||
.wrn("set_x_scaling_mode")?,
|
||||
|
|
@ -3416,9 +3321,6 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetWindowWorkspace { window, workspace } => self
|
||||
.handle_set_window_workspace(window, workspace)
|
||||
.wrn("set_window_workspace")?,
|
||||
ClientMessage::WindowSendToScratchpad { window, name } => self
|
||||
.handle_window_send_to_scratchpad(window, name)
|
||||
.wrn("window_send_to_scratchpad")?,
|
||||
ClientMessage::SetWindowFullscreen { window, fullscreen } => self
|
||||
.handle_set_window_fullscreen(window, fullscreen)
|
||||
.wrn("set_window_fullscreen")?,
|
||||
|
|
@ -3633,11 +3535,6 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetAutotile { enabled } => {
|
||||
self.state.theme.autotile_enabled.set(enabled);
|
||||
}
|
||||
ClientMessage::GetAutotile => {
|
||||
self.respond(Response::GetAutotile {
|
||||
enabled: self.state.theme.autotile_enabled.get(),
|
||||
});
|
||||
}
|
||||
ClientMessage::SeatToggleExpand { .. } => {
|
||||
// Removed feature; kept for binary protocol compatibility.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::transaction::BackendConnectorTransactionError,
|
||||
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError, ClientId},
|
||||
compositor::LogLevel,
|
||||
globals::{Global, GlobalName},
|
||||
|
|
@ -78,7 +79,7 @@ global_base!(JayCompositorGlobal, JayCompositor, JayCompositorError);
|
|||
|
||||
impl Global for JayCompositorGlobal {
|
||||
fn version(&self) -> u32 {
|
||||
30
|
||||
31
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
@ -542,6 +543,14 @@ impl JayCompositorRequestHandler for JayCompositor {
|
|||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_dpms(&self, req: SetDpms, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client
|
||||
.state
|
||||
.set_dpms_active(req.active != 0)
|
||||
.map_err(JayCompositorError::SetDpms)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
@ -559,5 +568,7 @@ pub enum JayCompositorError {
|
|||
ClientError(Box<ClientError>),
|
||||
#[error("Unknown log level {0}")]
|
||||
UnknownLogLevel(u32),
|
||||
#[error("Could not set DPMS state")]
|
||||
SetDpms(#[source] BackendConnectorTransactionError),
|
||||
}
|
||||
efrom!(JayCompositorError, ClientError);
|
||||
|
|
|
|||
|
|
@ -936,9 +936,6 @@ impl WlSeatGlobal {
|
|||
{
|
||||
c.move_child(tl, direction);
|
||||
self.maybe_schedule_warp_mouse_to_focus();
|
||||
} else if let Some(float) = data.float.get() {
|
||||
float.move_by_direction(direction);
|
||||
self.maybe_schedule_warp_mouse_to_focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1520,25 +1520,25 @@ impl WlSurface {
|
|||
let bounds = self.toplevel.get().and_then(|tl| tl.tl_render_bounds());
|
||||
let pos = self.buffer_abs_pos.get();
|
||||
let apply_damage = |pos: Rect| {
|
||||
let clip_damage = |mut damage: Rect| {
|
||||
damage = damage.intersect(pos);
|
||||
if pending.damage_full {
|
||||
let mut damage = pos;
|
||||
if let Some(bounds) = bounds {
|
||||
damage = damage.intersect(bounds);
|
||||
}
|
||||
damage
|
||||
};
|
||||
if pending.damage_full {
|
||||
self.client.state.damage(clip_damage(pos));
|
||||
self.client.state.damage(damage);
|
||||
} else {
|
||||
let matrix = self.damage_matrix.get();
|
||||
if let Some(buffer) = self.buffer.get() {
|
||||
for damage in &pending.buffer_damage {
|
||||
let damage = matrix.apply(
|
||||
let mut damage = matrix.apply(
|
||||
pos.x1(),
|
||||
pos.y1(),
|
||||
damage.intersect(buffer.buffer.buf.rect),
|
||||
);
|
||||
self.client.state.damage(clip_damage(damage));
|
||||
if let Some(bounds) = bounds {
|
||||
damage = damage.intersect(bounds);
|
||||
}
|
||||
self.client.state.damage(damage);
|
||||
}
|
||||
}
|
||||
for damage in &pending.surface_damage {
|
||||
|
|
@ -1550,7 +1550,8 @@ impl WlSurface {
|
|||
let y2 = (damage.y2() + scale - 1) / scale;
|
||||
damage = Rect::new_saturating(x1, y1, x2, y2);
|
||||
}
|
||||
self.client.state.damage(clip_damage(damage));
|
||||
damage = damage.intersect(bounds.unwrap_or(pos));
|
||||
self.client.state.damage(damage);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -628,11 +628,6 @@ fn schedule_async_upload(
|
|||
{
|
||||
back_tex_opt = None;
|
||||
}
|
||||
if let Some(back_tex) = &back_tex_opt
|
||||
&& Rc::strong_count(back_tex) > 1
|
||||
{
|
||||
back_tex_opt = None;
|
||||
}
|
||||
let damage_full = || {
|
||||
back.damage.clear();
|
||||
back.damage.damage(slice::from_ref(&buf.rect));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::wl_surface::{
|
||||
PendingState, SurfaceExt, WlSurface, WlSurfaceError,
|
||||
SurfaceExt, WlSurface, WlSurfaceError,
|
||||
x_surface::{xwayland_surface_v1::XwaylandSurfaceV1, xwindow::Xwindow},
|
||||
},
|
||||
leaks::Tracker,
|
||||
|
|
@ -30,22 +30,6 @@ impl SurfaceExt for XSurface {
|
|||
win.node_layer()
|
||||
}
|
||||
|
||||
fn before_apply_commit(
|
||||
self: Rc<Self>,
|
||||
pending: &mut PendingState,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
if pending
|
||||
.buffer
|
||||
.as_ref()
|
||||
.is_some_and(|buffer| buffer.is_none())
|
||||
&& self.surface.buffer.is_some()
|
||||
&& let Some(xwindow) = self.xwindow.get()
|
||||
{
|
||||
xwindow.queue_spawn_out();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn after_apply_commit(self: Rc<Self>) {
|
||||
if let Some(xwindow) = self.xwindow.get() {
|
||||
xwindow.map_status_changed();
|
||||
|
|
@ -61,7 +45,6 @@ impl SurfaceExt for XSurface {
|
|||
}
|
||||
self.surface.unset_ext();
|
||||
if let Some(xwindow) = self.xwindow.take() {
|
||||
xwindow.queue_spawn_out();
|
||||
xwindow.tl_destroy();
|
||||
xwindow.data.window.set(None);
|
||||
xwindow.data.surface_id.set(None);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use {
|
||||
crate::{
|
||||
animation::RetainedToplevel,
|
||||
client::Client,
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
|
|
@ -253,11 +252,6 @@ impl Xwindow {
|
|||
self.x.surface.buffer.is_some() && self.data.info.mapped.get()
|
||||
}
|
||||
|
||||
pub fn queue_spawn_out(&self) {
|
||||
self.toplevel_data
|
||||
.queue_spawn_out(self, self.tl_animation_snapshot());
|
||||
}
|
||||
|
||||
fn map_change(&self) -> Change {
|
||||
match (self.may_be_mapped(), self.is_mapped()) {
|
||||
(true, false) => Change::Map,
|
||||
|
|
@ -280,7 +274,6 @@ impl Xwindow {
|
|||
match map_change {
|
||||
Change::None => return,
|
||||
Change::Unmap => {
|
||||
self.queue_spawn_out();
|
||||
self.data
|
||||
.info
|
||||
.pending_extents
|
||||
|
|
@ -521,10 +514,6 @@ impl ToplevelNodeBase for Xwindow {
|
|||
Some(self.x.surface.clone())
|
||||
}
|
||||
|
||||
fn tl_animation_snapshot(&self) -> Option<Rc<RetainedToplevel>> {
|
||||
RetainedToplevel::capture_surface(&self.x.surface, (0, 0))
|
||||
}
|
||||
|
||||
fn tl_admits_children(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,10 +226,6 @@ pub trait XdgSurfaceExt: Debug {
|
|||
// nothing
|
||||
}
|
||||
|
||||
fn prepare_unmap(&self) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
fn extents_changed(&self) {
|
||||
// nothing
|
||||
}
|
||||
|
|
@ -668,15 +664,6 @@ impl SurfaceExt for XdgSurface {
|
|||
if let Some(serial) = pending.serial.take() {
|
||||
self.applied_serial.set(serial);
|
||||
}
|
||||
if pending
|
||||
.buffer
|
||||
.as_ref()
|
||||
.is_some_and(|buffer| buffer.is_none())
|
||||
&& self.surface.buffer.is_some()
|
||||
&& let Some(ext) = self.ext.get()
|
||||
{
|
||||
ext.prepare_unmap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ pub mod xdg_dialog_v1;
|
|||
|
||||
use {
|
||||
crate::{
|
||||
animation::RetainedToplevel,
|
||||
bugs,
|
||||
bugs::Bugs,
|
||||
client::{Client, ClientError},
|
||||
|
|
@ -260,7 +259,6 @@ impl XdgToplevelRequestHandler for XdgToplevel {
|
|||
type Error = XdgToplevelError;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.queue_spawn_out();
|
||||
self.tl_destroy();
|
||||
self.xdg.unset_ext();
|
||||
{
|
||||
|
|
@ -400,11 +398,6 @@ impl XdgToplevelRequestHandler for XdgToplevel {
|
|||
}
|
||||
|
||||
impl XdgToplevel {
|
||||
fn queue_spawn_out(&self) {
|
||||
self.toplevel_data
|
||||
.queue_spawn_out(self, self.tl_animation_snapshot());
|
||||
}
|
||||
|
||||
fn map(
|
||||
self: &Rc<Self>,
|
||||
parent: Option<&XdgToplevel>,
|
||||
|
|
@ -786,11 +779,6 @@ impl ToplevelNodeBase for XdgToplevel {
|
|||
Some(self.xdg.surface.clone())
|
||||
}
|
||||
|
||||
fn tl_animation_snapshot(&self) -> Option<Rc<RetainedToplevel>> {
|
||||
let geo = self.xdg.geometry();
|
||||
RetainedToplevel::capture_surface(&self.xdg.surface, (-geo.x1(), -geo.y1()))
|
||||
}
|
||||
|
||||
fn tl_restack_popups(&self) {
|
||||
self.xdg.restack_popups();
|
||||
}
|
||||
|
|
@ -830,10 +818,6 @@ impl XdgSurfaceExt for XdgToplevel {
|
|||
self.after_commit(None);
|
||||
}
|
||||
|
||||
fn prepare_unmap(&self) {
|
||||
self.queue_spawn_out();
|
||||
}
|
||||
|
||||
fn extents_changed(&self) {
|
||||
self.toplevel_data.pos.set(self.xdg.extents.get());
|
||||
self.tl_extents_changed();
|
||||
|
|
|
|||
|
|
@ -284,27 +284,6 @@ impl TestConfig {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn send_to_scratchpad(&self, seat: SeatId, name: &str) -> TestResult {
|
||||
self.send(ClientMessage::SeatSendToScratchpad {
|
||||
seat: Seat(seat.raw() as _),
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn toggle_scratchpad(&self, seat: SeatId, name: &str) -> TestResult {
|
||||
self.send(ClientMessage::SeatToggleScratchpad {
|
||||
seat: Seat(seat.raw() as _),
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cycle_scratchpad(&self, seat: SeatId, name: &str) -> TestResult {
|
||||
self.send(ClientMessage::SeatCycleScratchpad {
|
||||
seat: Seat(seat.raw() as _),
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
unsafe {
|
||||
if let Some(srv) = self.srv.take() {
|
||||
|
|
@ -352,10 +331,6 @@ impl TestConfig {
|
|||
pub fn set_show_titles(&self, show: bool) -> TestResult {
|
||||
self.send(ClientMessage::SetShowTitles { show })
|
||||
}
|
||||
|
||||
pub fn set_autotile(&self, enabled: bool) -> TestResult {
|
||||
self.send(ClientMessage::SetAutotile { enabled })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestConfig {
|
||||
|
|
|
|||
|
|
@ -29,17 +29,6 @@ impl TestViewport {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unset_source(&self) -> Result<(), TestError> {
|
||||
self.tran.send(SetSource {
|
||||
self_id: self.id,
|
||||
x: Fixed::from_int(-1),
|
||||
y: Fixed::from_int(-1),
|
||||
width: Fixed::from_int(-1),
|
||||
height: Fixed::from_int(-1),
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_destination(&self, width: i32, height: i32) -> Result<(), TestError> {
|
||||
self.tran.send(SetDestination {
|
||||
self_id: self.id,
|
||||
|
|
@ -48,15 +37,6 @@ impl TestViewport {
|
|||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unset_destination(&self) -> Result<(), TestError> {
|
||||
self.tran.send(SetDestination {
|
||||
self_id: self.id,
|
||||
width: -1,
|
||||
height: -1,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestViewport {
|
||||
|
|
|
|||
|
|
@ -85,8 +85,6 @@ mod t0051_pointer_warp;
|
|||
mod t0052_bar;
|
||||
mod t0053_theme;
|
||||
mod t0054_subsurface_already_attached;
|
||||
mod t0055_autotiling;
|
||||
mod t0055_scratchpad;
|
||||
|
||||
pub trait TestCase: Sync {
|
||||
fn name(&self) -> &'static str;
|
||||
|
|
@ -160,7 +158,5 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
|||
t0052_bar,
|
||||
t0053_theme,
|
||||
t0054_subsurface_already_attached,
|
||||
t0055_autotiling,
|
||||
t0055_scratchpad,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
it::{test_error::TestError, testrun::TestRun},
|
||||
rect::Rect,
|
||||
tree::Node,
|
||||
},
|
||||
std::rc::Rc,
|
||||
|
|
@ -10,19 +11,29 @@ testcase!();
|
|||
|
||||
/// Create and map a single surface
|
||||
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
||||
let ds = run.create_default_setup().await?;
|
||||
run.backend.install_default()?;
|
||||
|
||||
let client = run.create_client().await?;
|
||||
|
||||
let window = client.create_window().await?;
|
||||
window.map().await?;
|
||||
|
||||
let workspace_rect = ds.output.workspace_rect.get();
|
||||
tassert_eq!(window.tl.core.width.get(), 800);
|
||||
tassert_eq!(
|
||||
window.tl.core.height.get(),
|
||||
600 - 2 * run.state.theme.title_plus_underline_height()
|
||||
);
|
||||
|
||||
tassert_eq!(window.tl.core.width.get(), workspace_rect.width());
|
||||
tassert_eq!(window.tl.core.height.get(), workspace_rect.height());
|
||||
|
||||
tassert_eq!(window.tl.server.node_absolute_position(), workspace_rect);
|
||||
tassert_eq!(
|
||||
window.tl.server.node_absolute_position(),
|
||||
Rect::new_sized(
|
||||
0,
|
||||
2 * run.state.theme.title_plus_underline_height(),
|
||||
window.tl.core.width.get(),
|
||||
window.tl.core.height.get(),
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ testcase!();
|
|||
|
||||
/// Create and map two surfaces
|
||||
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
||||
let ds = run.create_default_setup().await?;
|
||||
run.backend.install_default()?;
|
||||
|
||||
let client = run.create_client().await?;
|
||||
|
||||
|
|
@ -21,30 +21,17 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
|||
let window2 = client.create_window().await?;
|
||||
window2.map().await?;
|
||||
|
||||
let workspace_rect = ds.output.workspace_rect.get();
|
||||
let otop = 2 * run.state.theme.title_plus_underline_height();
|
||||
let bw = run.state.theme.sizes.border_width.get();
|
||||
let child_width = (workspace_rect.width() - bw) / 2;
|
||||
|
||||
tassert_eq!(
|
||||
window.tl.server.node_absolute_position(),
|
||||
Rect::new_sized(
|
||||
workspace_rect.x1(),
|
||||
workspace_rect.y1(),
|
||||
child_width,
|
||||
workspace_rect.height(),
|
||||
)
|
||||
.unwrap()
|
||||
Rect::new_sized(0, otop, (800 - bw) / 2, 600 - otop).unwrap()
|
||||
);
|
||||
|
||||
tassert_eq!(
|
||||
window2.tl.server.node_absolute_position(),
|
||||
Rect::new_sized(
|
||||
workspace_rect.x1() + child_width + bw,
|
||||
workspace_rect.y1(),
|
||||
child_width,
|
||||
workspace_rect.height(),
|
||||
)
|
||||
.unwrap()
|
||||
Rect::new_sized((800 - bw) / 2 + bw, otop, (800 - bw) / 2, 600 - otop).unwrap()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -48,18 +48,13 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
|
||||
let mono_container = w_mono2.tl.container_parent()?;
|
||||
let container_pos = mono_container.tl_data().pos.get();
|
||||
let (tab_x, tab_y) = {
|
||||
let tab_bar = mono_container.tab_bar.borrow();
|
||||
let Some(tab_bar) = tab_bar.as_ref() else {
|
||||
bail!("no tab bar");
|
||||
};
|
||||
let w_mono1_title = &tab_bar.entries[0];
|
||||
(
|
||||
container_pos.x1() + w_mono1_title.x.get() + w_mono1_title.width.get() / 2,
|
||||
container_pos.y1() + tab_bar.height / 2,
|
||||
)
|
||||
};
|
||||
ds.mouse.abs(&ds.connector, tab_x as _, tab_y as _);
|
||||
let w_mono1_title = mono_container.render_data.borrow_mut().title_rects[0]
|
||||
.move_(container_pos.x1(), container_pos.y1());
|
||||
ds.mouse.abs(
|
||||
&ds.connector,
|
||||
w_mono1_title.x1() as _,
|
||||
w_mono1_title.y1() as _,
|
||||
);
|
||||
|
||||
client.sync().await;
|
||||
tassert!(enters.next().is_err());
|
||||
|
|
|
|||
|
|
@ -26,18 +26,12 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
|
||||
let container = w_mono2.tl.container_parent()?;
|
||||
let pos = container.tl_data().pos.get();
|
||||
let (tab_x, tab_y) = {
|
||||
let tab_bar = container.tab_bar.borrow();
|
||||
let Some(tab_bar) = tab_bar.as_ref() else {
|
||||
bail!("no tab bar");
|
||||
};
|
||||
let w_mono1_title = &tab_bar.entries[0];
|
||||
(
|
||||
pos.x1() + w_mono1_title.x.get() + w_mono1_title.width.get() / 2,
|
||||
pos.y1() + tab_bar.height / 2,
|
||||
)
|
||||
};
|
||||
ds.mouse.abs(&ds.connector, tab_x as f64, tab_y as f64);
|
||||
let w_mono1_title = container.render_data.borrow_mut().title_rects[0].move_(pos.x1(), pos.y1());
|
||||
ds.mouse.abs(
|
||||
&ds.connector,
|
||||
w_mono1_title.x1() as f64,
|
||||
w_mono1_title.y1() as f64,
|
||||
);
|
||||
client.sync().await;
|
||||
|
||||
let enters = dss.kb.enter.expect()?;
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::{
|
||||
ifs::wl_surface::xdg_surface::xdg_toplevel::STATE_SUSPENDED,
|
||||
it::{
|
||||
test_error::{TestErrorExt, TestResult},
|
||||
test_error::TestResult,
|
||||
test_utils::{
|
||||
test_ouput_node_ext::TestOutputNodeExt, test_toplevel_node_ext::TestToplevelNodeExt,
|
||||
},
|
||||
|
|
@ -10,7 +10,7 @@ use {
|
|||
},
|
||||
},
|
||||
isnt::std_1::collections::IsntHashSetExt,
|
||||
std::{rc::Rc, time::Duration},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
testcase!();
|
||||
|
|
@ -19,7 +19,6 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
let ds = run.create_default_setup().await?;
|
||||
|
||||
let client = run.create_client().await?;
|
||||
let default_seat = client.get_default_seat().await?;
|
||||
|
||||
let win1 = client.create_window().await?;
|
||||
win1.set_color(255, 0, 0, 255);
|
||||
|
|
@ -45,23 +44,5 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
client.sync().await;
|
||||
tassert!(win2.tl.core.states.borrow().not_contains(&STATE_SUSPENDED));
|
||||
|
||||
let leaves = default_seat.kb.leave.expect()?;
|
||||
let enters = default_seat.kb.enter.expect()?;
|
||||
|
||||
run.cfg.set_idle(Duration::from_micros(100))?;
|
||||
run.cfg.set_idle_grace_period(Duration::from_secs(0))?;
|
||||
run.state.wheel.timeout(3).await?;
|
||||
|
||||
client.sync().await;
|
||||
tassert!(win2.tl.core.states.borrow().contains(&STATE_SUSPENDED));
|
||||
let leave = leaves.next().with_context(|| "no leave on suspend")?;
|
||||
tassert_eq!(leave.surface, win2.surface.id);
|
||||
|
||||
ds.mouse.rel(1.0, 1.0);
|
||||
client.sync().await;
|
||||
tassert!(win2.tl.core.states.borrow().not_contains(&STATE_SUSPENDED));
|
||||
let enter = enters.next().with_context(|| "no enter on restore")?;
|
||||
tassert_eq!(enter.surface, win2.surface.id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -308,8 +308,9 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
let output_damage = connector_data.damage.borrow();
|
||||
tassert!(!output_damage.is_empty());
|
||||
|
||||
// The test window maps its 1x1 buffer through a viewport to the full window size.
|
||||
let expected_buffer_damage = surface_pos;
|
||||
// Buffer damage is transformed by the damage matrix which includes the surface position
|
||||
// The buffer damage (0,0,1,1) should be transformed to surface coordinates
|
||||
let expected_buffer_damage = buffer_damage.move_(surface_pos.x1(), surface_pos.y1());
|
||||
|
||||
// Find the exact output damage that matches our expected buffer damage
|
||||
let mut found_exact_buffer_damage = false;
|
||||
|
|
@ -330,12 +331,10 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
// Test 7: Check output damage from existing window's viewport (which already has scaling)
|
||||
connector_data.damage.borrow_mut().clear();
|
||||
|
||||
// The existing window was created with create_surface_ext() which automatically creates a viewport.
|
||||
// Commit the viewport size change separately; that commit intentionally damages the old/new extents.
|
||||
window.surface.viewport.set_destination(150, 100)?;
|
||||
window.surface.commit()?;
|
||||
client.sync().await;
|
||||
connector_data.damage.borrow_mut().clear();
|
||||
// The existing window was created with create_surface_ext() which automatically creates a viewport
|
||||
// Let's verify that the viewport's existing scaling affects buffer damage correctly
|
||||
// First, let's modify the viewport scaling that already exists on the window
|
||||
window.surface.viewport.set_destination(150, 100)?; // Change scaling to 150x100
|
||||
|
||||
// Add buffer damage to test viewport scaling coordinate transformation
|
||||
window.surface.damage_buffer(0, 0, 1, 1)?; // Damage entire 1x1 buffer
|
||||
|
|
@ -347,8 +346,8 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
let output_damage = connector_data.damage.borrow();
|
||||
tassert!(!output_damage.is_empty());
|
||||
|
||||
// With viewporter scaling, the 1x1 buffer damage should scale to the viewport destination.
|
||||
let surface_pos = window.surface.server.buffer_abs_pos.get();
|
||||
// With viewporter scaling, the 1x1 buffer damage should scale to 150x100
|
||||
// and be moved by surface position (0, 36) to get output coordinates (0, 36, 150, 136)
|
||||
let expected_scaled_damage = Rect::new_sized(0, 0, 150, 100).unwrap();
|
||||
let expected_output_damage =
|
||||
expected_scaled_damage.move_(surface_pos.x1(), surface_pos.y1());
|
||||
|
|
@ -403,9 +402,8 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
rotation_window.map().await?;
|
||||
client.sync().await;
|
||||
|
||||
// Disable viewporter to rely purely on buffer dimensions.
|
||||
rotation_window.surface.viewport.unset_source()?;
|
||||
rotation_window.surface.viewport.unset_destination()?;
|
||||
// Disable viewporter by setting destination to 0x0 to rely purely on buffer dimensions
|
||||
rotation_window.surface.viewport.set_destination(0, 0)?; // Disable viewporter
|
||||
|
||||
// Use a rectangular buffer (4x2) so rotation has a visible geometric effect
|
||||
// Attach AFTER mapping to avoid being overwritten by map()'s single-pixel buffer
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
it::{test_error::TestResult, testrun::TestRun},
|
||||
tree::{ContainerSplit, Node, ToplevelNodeBase},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
testcase!();
|
||||
|
||||
async fn test(run: Rc<TestRun>) -> TestResult {
|
||||
run.backend.install_default()?;
|
||||
run.cfg.set_autotile(true)?;
|
||||
|
||||
let client = run.create_client().await?;
|
||||
|
||||
let win1 = client.create_window().await?;
|
||||
win1.map().await?;
|
||||
let root = win1.tl.container_parent()?;
|
||||
tassert_eq!(root.split.get(), ContainerSplit::Horizontal);
|
||||
|
||||
let win2 = client.create_window().await?;
|
||||
win2.map().await?;
|
||||
client.sync().await;
|
||||
|
||||
tassert_eq!(root.split.get(), ContainerSplit::Horizontal);
|
||||
tassert_eq!(win1.tl.container_parent()?.node_id(), root.node_id());
|
||||
tassert_eq!(win2.tl.container_parent()?.node_id(), root.node_id());
|
||||
|
||||
let win3 = client.create_window().await?;
|
||||
win3.map().await?;
|
||||
client.sync().await;
|
||||
|
||||
let v_group = win3.tl.container_parent()?;
|
||||
tassert_eq!(root.split.get(), ContainerSplit::Horizontal);
|
||||
tassert_eq!(v_group.split.get(), ContainerSplit::Vertical);
|
||||
tassert_eq!(win2.tl.container_parent()?.node_id(), v_group.node_id());
|
||||
|
||||
let win4 = client.create_window().await?;
|
||||
win4.map().await?;
|
||||
client.sync().await;
|
||||
|
||||
let h_group = win4.tl.container_parent()?;
|
||||
tassert_eq!(h_group.split.get(), ContainerSplit::Horizontal);
|
||||
tassert_eq!(win3.tl.container_parent()?.node_id(), h_group.node_id());
|
||||
let h_parent = match h_group
|
||||
.tl_data()
|
||||
.parent
|
||||
.get()
|
||||
.and_then(|p| p.node_into_container())
|
||||
{
|
||||
Some(parent) => parent,
|
||||
None => bail!("autotile group does not have a container parent"),
|
||||
};
|
||||
tassert_eq!(h_parent.node_id(), v_group.node_id());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
it::{test_error::TestResult, testrun::TestRun},
|
||||
tree::{Node, ToplevelNodeBase},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
testcase!();
|
||||
|
||||
async fn test(run: Rc<TestRun>) -> TestResult {
|
||||
let ds = run.create_default_setup().await?;
|
||||
|
||||
let client = run.create_client().await?;
|
||||
let win1 = client.create_window().await?;
|
||||
win1.map2().await?;
|
||||
let win2 = client.create_window().await?;
|
||||
win2.map2().await?;
|
||||
|
||||
run.cfg.send_to_scratchpad(ds.seat.id(), "term")?;
|
||||
client.sync().await;
|
||||
tassert!(win1.tl.server.node_visible());
|
||||
tassert!(!win2.tl.server.node_visible());
|
||||
|
||||
run.cfg.show_workspace(ds.seat.id(), "2")?;
|
||||
run.cfg.toggle_scratchpad(ds.seat.id(), "term")?;
|
||||
client.sync().await;
|
||||
tassert!(win2.tl.server.node_visible());
|
||||
tassert_eq!(ds.output.workspace.get().unwrap().name.as_str(), "2");
|
||||
|
||||
run.cfg.toggle_scratchpad(ds.seat.id(), "term")?;
|
||||
client.sync().await;
|
||||
tassert!(!win2.tl.server.node_visible());
|
||||
|
||||
run.cfg.toggle_scratchpad(ds.seat.id(), "term")?;
|
||||
client.sync().await;
|
||||
tassert!(win2.tl.server.node_visible());
|
||||
tassert_eq!(ds.output.workspace.get().unwrap().name.as_str(), "2");
|
||||
|
||||
run.cfg.show_workspace(ds.seat.id(), "3")?;
|
||||
client.sync().await;
|
||||
tassert!(!win2.tl.server.node_visible());
|
||||
|
||||
run.cfg.toggle_scratchpad(ds.seat.id(), "term")?;
|
||||
client.sync().await;
|
||||
tassert!(win2.tl.server.node_visible());
|
||||
tassert_eq!(ds.output.workspace.get().unwrap().name.as_str(), "3");
|
||||
// Scratchpad windows are always shown floating.
|
||||
tassert!(win2.tl.server.tl_data().parent_is_float.get());
|
||||
|
||||
// Park win2 again, then build a multi-window scratchpad and cycle it.
|
||||
run.cfg.toggle_scratchpad(ds.seat.id(), "term")?;
|
||||
client.sync().await;
|
||||
tassert!(!win2.tl.server.node_visible());
|
||||
|
||||
// Build a three-window scratchpad. Each window is focused right after it is
|
||||
// mapped, so sending the focused window parks them in a known order.
|
||||
let cyc1 = client.create_window().await?;
|
||||
cyc1.map2().await?;
|
||||
run.cfg.send_to_scratchpad(ds.seat.id(), "cyc")?;
|
||||
let cyc2 = client.create_window().await?;
|
||||
cyc2.map2().await?;
|
||||
run.cfg.send_to_scratchpad(ds.seat.id(), "cyc")?;
|
||||
let cyc3 = client.create_window().await?;
|
||||
cyc3.map2().await?;
|
||||
run.cfg.send_to_scratchpad(ds.seat.id(), "cyc")?;
|
||||
client.sync().await;
|
||||
tassert!(!cyc1.tl.server.node_visible());
|
||||
tassert!(!cyc2.tl.server.node_visible());
|
||||
tassert!(!cyc3.tl.server.node_visible());
|
||||
|
||||
// Nothing shown: cycle brings up the first window (insertion order: cyc1).
|
||||
run.cfg.cycle_scratchpad(ds.seat.id(), "cyc")?;
|
||||
client.sync().await;
|
||||
tassert!(cyc1.tl.server.node_visible());
|
||||
tassert!(!cyc2.tl.server.node_visible());
|
||||
tassert!(!cyc3.tl.server.node_visible());
|
||||
// Scratchpad windows are always shown floating.
|
||||
tassert!(cyc1.tl.server.tl_data().parent_is_float.get());
|
||||
|
||||
// Cycle advances one at a time.
|
||||
run.cfg.cycle_scratchpad(ds.seat.id(), "cyc")?;
|
||||
client.sync().await;
|
||||
tassert!(!cyc1.tl.server.node_visible());
|
||||
tassert!(cyc2.tl.server.node_visible());
|
||||
tassert!(!cyc3.tl.server.node_visible());
|
||||
|
||||
run.cfg.cycle_scratchpad(ds.seat.id(), "cyc")?;
|
||||
client.sync().await;
|
||||
tassert!(!cyc1.tl.server.node_visible());
|
||||
tassert!(!cyc2.tl.server.node_visible());
|
||||
tassert!(cyc3.tl.server.node_visible());
|
||||
|
||||
// On the final window, the next cycle hides everything.
|
||||
run.cfg.cycle_scratchpad(ds.seat.id(), "cyc")?;
|
||||
client.sync().await;
|
||||
tassert!(!cyc1.tl.server.node_visible());
|
||||
tassert!(!cyc2.tl.server.node_visible());
|
||||
tassert!(!cyc3.tl.server.node_visible());
|
||||
|
||||
// And it wraps back to the first window.
|
||||
run.cfg.cycle_scratchpad(ds.seat.id(), "cyc")?;
|
||||
client.sync().await;
|
||||
tassert!(cyc1.tl.server.node_visible());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -48,7 +48,6 @@ mod leaks;
|
|||
mod tracy;
|
||||
mod acceptor;
|
||||
mod allocator;
|
||||
mod animation;
|
||||
mod async_engine;
|
||||
mod backend;
|
||||
mod backends;
|
||||
|
|
|
|||
367
src/renderer.rs
367
src/renderer.rs
|
|
@ -1,11 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
animation::{
|
||||
RetainedContent, RetainedExitFrame, RetainedExitLayer, RetainedSurface,
|
||||
RetainedToplevel,
|
||||
},
|
||||
cmm::cmm_render_intent::RenderIntent,
|
||||
gfx_api::{AcquireSync, AlphaMode, BufferResv, GfxApiOpt, ReleaseSync, SampleRect},
|
||||
gfx_api::{AcquireSync, AlphaMode, GfxApiOpt, ReleaseSync, SampleRect},
|
||||
ifs::wl_surface::{
|
||||
SurfaceBuffer, WlSurface,
|
||||
x_surface::xwindow::Xwindow,
|
||||
|
|
@ -18,8 +14,8 @@ use {
|
|||
state::State,
|
||||
theme::{Color, CornerRadius},
|
||||
tree::{
|
||||
ContainerNode, DisplayNode, FloatNode, Node, OutputNode, PlaceholderNode, ToplevelData,
|
||||
ToplevelNode, ToplevelNodeBase, WorkspaceNode, tab_bar::TabBar,
|
||||
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
||||
ToplevelNodeBase, WorkspaceNode, tab_bar::TabBar,
|
||||
},
|
||||
},
|
||||
std::{ops::Deref, rc::Rc, slice},
|
||||
|
|
@ -204,22 +200,14 @@ impl Renderer<'_> {
|
|||
self.render_workspace(&ws, x, y);
|
||||
}
|
||||
}
|
||||
let now = self.state.now_nsec();
|
||||
let exit_frames = self.state.animations.exit_frames(now);
|
||||
self.render_exit_frames(&exit_frames, RetainedExitLayer::Tiled, &opos);
|
||||
macro_rules! render_stacked {
|
||||
($stack:expr) => {
|
||||
for stacked in $stack.iter() {
|
||||
if stacked.node_visible() {
|
||||
self.base.sync();
|
||||
let pos = stacked.node_absolute_position();
|
||||
let visual = self.state.animations.visual_rect(
|
||||
stacked.node_id(),
|
||||
pos,
|
||||
self.state.now_nsec(),
|
||||
);
|
||||
if visual.intersects(&opos) {
|
||||
let (x, y) = opos.translate(visual.x1(), visual.y1());
|
||||
if pos.intersects(&opos) {
|
||||
let (x, y) = opos.translate(pos.x1(), pos.y1());
|
||||
stacked.node_render(self, x, y, None);
|
||||
}
|
||||
}
|
||||
|
|
@ -227,7 +215,6 @@ impl Renderer<'_> {
|
|||
};
|
||||
}
|
||||
render_stacked!(self.state.root.stacked);
|
||||
self.render_exit_frames(&exit_frames, RetainedExitLayer::Floating, &opos);
|
||||
// Flush RoundedFillRect ops from container/float borders so they don't
|
||||
// sort after (and render on top of) layer-shell CopyTexture ops.
|
||||
self.base.sync();
|
||||
|
|
@ -466,265 +453,6 @@ impl Renderer<'_> {
|
|||
.fill_boxes2(&rd.border_rects, &c, srgb, perceptual, x, y);
|
||||
}
|
||||
|
||||
fn presentation_child_body(
|
||||
&self,
|
||||
container: &ContainerNode,
|
||||
child: &Rc<dyn ToplevelNode>,
|
||||
body: Rect,
|
||||
) -> Rect {
|
||||
let abs = body.move_(container.abs_x1.get(), container.abs_y1.get());
|
||||
let visual = self
|
||||
.state
|
||||
.animations
|
||||
.visual_rect(child.node_id(), abs, self.state.now_nsec());
|
||||
visual.move_(-container.abs_x1.get(), -container.abs_y1.get())
|
||||
}
|
||||
|
||||
fn render_child_or_snapshot(
|
||||
&mut self,
|
||||
child: &Rc<dyn ToplevelNode>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
bounds: Option<&Rect>,
|
||||
) {
|
||||
if let Some(retained) = self
|
||||
.state
|
||||
.animations
|
||||
.retained_snapshot(child.node_id(), self.state.now_nsec())
|
||||
{
|
||||
self.render_retained_toplevel(&retained, x, y, bounds);
|
||||
} else {
|
||||
child.node_render(self, x, y, bounds);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_retained_toplevel(
|
||||
&mut self,
|
||||
retained: &RetainedToplevel,
|
||||
x: i32,
|
||||
y: i32,
|
||||
bounds: Option<&Rect>,
|
||||
) {
|
||||
let (x, y) = self
|
||||
.base
|
||||
.scale_point(x + retained.offset.0, y + retained.offset.1);
|
||||
self.render_retained_surface_scaled(&retained.surface, x, y, None, bounds);
|
||||
}
|
||||
|
||||
fn render_exit_frames(
|
||||
&mut self,
|
||||
frames: &[RetainedExitFrame],
|
||||
layer: RetainedExitLayer,
|
||||
output_rect: &Rect,
|
||||
) {
|
||||
for frame in frames {
|
||||
if frame.layer != layer || !frame.rect.intersects(output_rect) {
|
||||
continue;
|
||||
}
|
||||
self.render_exit_frame(frame, output_rect);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_exit_frame(&mut self, frame: &RetainedExitFrame, output_rect: &Rect) {
|
||||
let (x, y) = output_rect.translate(frame.rect.x1(), frame.rect.y1());
|
||||
let inset = frame.frame_inset;
|
||||
if inset > 0 {
|
||||
let color = if frame.active {
|
||||
self.state.theme.colors.active_border.get()
|
||||
} else {
|
||||
self.state.theme.colors.border.get()
|
||||
};
|
||||
self.render_rounded_frame(
|
||||
Rect::new_sized_saturating(0, 0, frame.rect.width(), frame.rect.height()),
|
||||
&color,
|
||||
self.state.theme.corner_radius.get(),
|
||||
inset,
|
||||
x,
|
||||
y,
|
||||
);
|
||||
}
|
||||
let body = Rect::new_sized_saturating(
|
||||
x + inset,
|
||||
y + inset,
|
||||
frame.rect.width() - 2 * inset,
|
||||
frame.rect.height() - 2 * inset,
|
||||
);
|
||||
if body.is_empty() {
|
||||
return;
|
||||
}
|
||||
if inset > 0 && !self.state.theme.corner_radius.get().is_zero() {
|
||||
let inner_cr = self.scale_corner_radius(
|
||||
self.state
|
||||
.theme
|
||||
.corner_radius
|
||||
.get()
|
||||
.expanded_by(-(inset as f32)),
|
||||
);
|
||||
self.corner_radius = Some(inner_cr);
|
||||
}
|
||||
self.render_window_body_background(body);
|
||||
let bounds = self.base.scale_rect(body);
|
||||
self.stretch = if frame.source_body_size != body.size() {
|
||||
Some(self.base.scale_point(body.width(), body.height()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.render_retained_toplevel(&frame.retained, body.x1(), body.y1(), Some(&bounds));
|
||||
self.stretch = None;
|
||||
self.corner_radius = None;
|
||||
}
|
||||
|
||||
fn render_window_body_background(&mut self, body: Rect) {
|
||||
if body.is_empty() {
|
||||
return;
|
||||
}
|
||||
let color = self.state.theme.colors.background.get();
|
||||
let srgb_srgb = self.state.color_manager.srgb_gamma22();
|
||||
let srgb = &srgb_srgb.linear;
|
||||
let perceptual = RenderIntent::Perceptual;
|
||||
self.base.sync();
|
||||
if let Some(cr) = self.corner_radius
|
||||
&& !cr.is_zero()
|
||||
{
|
||||
self.base
|
||||
.fill_rounded_rect(body, &color, None, srgb, perceptual, cr, 0.0);
|
||||
} else {
|
||||
let bounds = self.base.scale_rect(body);
|
||||
self.base
|
||||
.fill_scaled_boxes(slice::from_ref(&bounds), &color, None, srgb, perceptual);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_retained_surface_scaled(
|
||||
&mut self,
|
||||
retained: &RetainedSurface,
|
||||
x: i32,
|
||||
y: i32,
|
||||
pos_rel: Option<(i32, i32)>,
|
||||
bounds: Option<&Rect>,
|
||||
) {
|
||||
let stretch = self.stretch.take();
|
||||
let corner_radius = self.corner_radius.take();
|
||||
let mut size = retained.size;
|
||||
if let Some((x_rel, y_rel)) = pos_rel {
|
||||
let (x, y) = self.base.scale_point(x_rel, y_rel);
|
||||
let (w, h) = self.base.scale_point(x_rel + size.0, y_rel + size.1);
|
||||
size = (w - x, h - y);
|
||||
} else {
|
||||
size = self.base.scale_point(size.0, size.1);
|
||||
}
|
||||
let mut stretched_source = None;
|
||||
if let Some(s) = stretch {
|
||||
if let RetainedContent::Texture { source, .. } = &retained.content {
|
||||
let mut source = *source;
|
||||
if size.0 > 0 && size.1 > 0 {
|
||||
let sx = s.0 as f32 / size.0 as f32;
|
||||
let sy = s.1 as f32 / size.1 as f32;
|
||||
source.x2 *= sx;
|
||||
source.y2 *= sy;
|
||||
}
|
||||
stretched_source = Some(source);
|
||||
}
|
||||
size = s;
|
||||
}
|
||||
for child in &retained.below {
|
||||
let (x1, y1) = self.base.scale_point(child.offset.0, child.offset.1);
|
||||
self.render_retained_surface_scaled(child, x + x1, y + y1, Some(child.offset), bounds);
|
||||
}
|
||||
self.corner_radius = corner_radius;
|
||||
self.render_retained_content(retained, stretched_source, x, y, size, bounds);
|
||||
for child in &retained.above {
|
||||
let (x1, y1) = self.base.scale_point(child.offset.0, child.offset.1);
|
||||
self.render_retained_surface_scaled(child, x + x1, y + y1, Some(child.offset), bounds);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_retained_content(
|
||||
&mut self,
|
||||
retained: &RetainedSurface,
|
||||
stretched_source: Option<SampleRect>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
size: (i32, i32),
|
||||
bounds: Option<&Rect>,
|
||||
) {
|
||||
let corner_radius = self.corner_radius.take();
|
||||
match &retained.content {
|
||||
RetainedContent::Texture {
|
||||
texture,
|
||||
buffer,
|
||||
source,
|
||||
alpha,
|
||||
color_description,
|
||||
render_intent,
|
||||
alpha_mode,
|
||||
opaque,
|
||||
} => {
|
||||
let source = stretched_source.unwrap_or(*source);
|
||||
if let Some(cr) = corner_radius {
|
||||
self.base.render_rounded_texture(
|
||||
texture,
|
||||
*alpha,
|
||||
x,
|
||||
y,
|
||||
Some(source),
|
||||
Some(size),
|
||||
self.base.scale,
|
||||
bounds,
|
||||
Some(buffer.clone() as Rc<dyn BufferResv>),
|
||||
AcquireSync::Unnecessary,
|
||||
buffer.release_sync,
|
||||
color_description,
|
||||
*render_intent,
|
||||
*alpha_mode,
|
||||
cr,
|
||||
);
|
||||
} else {
|
||||
self.base.render_texture(
|
||||
texture,
|
||||
*alpha,
|
||||
x,
|
||||
y,
|
||||
Some(source),
|
||||
Some(size),
|
||||
self.base.scale,
|
||||
bounds,
|
||||
Some(buffer.clone() as Rc<dyn BufferResv>),
|
||||
AcquireSync::Unnecessary,
|
||||
buffer.release_sync,
|
||||
*opaque,
|
||||
color_description,
|
||||
*render_intent,
|
||||
*alpha_mode,
|
||||
);
|
||||
}
|
||||
}
|
||||
RetainedContent::Color {
|
||||
color,
|
||||
alpha,
|
||||
color_description,
|
||||
render_intent,
|
||||
} => {
|
||||
if let Some(rect) = Rect::new_sized(x, y, size.0, size.1) {
|
||||
let rect = match bounds {
|
||||
None => rect,
|
||||
Some(bounds) => rect.intersect(*bounds),
|
||||
};
|
||||
if !rect.is_empty() {
|
||||
self.base.sync();
|
||||
self.base.fill_scaled_boxes(
|
||||
&[rect],
|
||||
color,
|
||||
*alpha,
|
||||
&color_description.linear,
|
||||
*render_intent,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
||||
self.render_container_decorations(container, x, y);
|
||||
|
||||
|
|
@ -737,7 +465,6 @@ impl Renderer<'_> {
|
|||
}
|
||||
}
|
||||
let mb = container.mono_body.get();
|
||||
let visual_mb = self.presentation_child_body(container, &child.node, mb);
|
||||
if self.state.theme.sizes.gap.get() != 0 {
|
||||
let bw = self.state.theme.sizes.border_width.get();
|
||||
let border_color = self.state.theme.colors.border.get();
|
||||
|
|
@ -749,10 +476,10 @@ impl Renderer<'_> {
|
|||
};
|
||||
if !child.node.node_is_container() {
|
||||
let frame = Rect::new_sized_saturating(
|
||||
visual_mb.x1() - bw,
|
||||
visual_mb.y1() - bw,
|
||||
visual_mb.width() + 2 * bw,
|
||||
visual_mb.height() + 2 * bw,
|
||||
mb.x1() - bw,
|
||||
mb.y1() - bw,
|
||||
mb.width() + 2 * bw,
|
||||
mb.height() + 2 * bw,
|
||||
);
|
||||
self.render_rounded_frame(
|
||||
frame,
|
||||
|
|
@ -764,17 +491,14 @@ impl Renderer<'_> {
|
|||
);
|
||||
}
|
||||
}
|
||||
let body = visual_mb.move_(x, y);
|
||||
let content = container
|
||||
.mono_content
|
||||
.get()
|
||||
.at_point(visual_mb.x1(), visual_mb.y1());
|
||||
self.stretch =
|
||||
if content.width() != visual_mb.width() || content.height() != visual_mb.height() {
|
||||
Some(self.base.scale_point(visual_mb.width(), visual_mb.height()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let body = mb.move_(x, y);
|
||||
let body = self.base.scale_rect(body);
|
||||
let content = container.mono_content.get();
|
||||
self.stretch = if content.width() != mb.width() || content.height() != mb.height() {
|
||||
Some(self.base.scale_point(mb.width(), mb.height()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if self.state.theme.sizes.gap.get() != 0 && !child.node.node_is_container() {
|
||||
let cr = self.state.theme.corner_radius.get();
|
||||
if !cr.is_zero() {
|
||||
|
|
@ -783,16 +507,9 @@ impl Renderer<'_> {
|
|||
self.corner_radius = Some(inner_cr);
|
||||
}
|
||||
}
|
||||
if !child.node.node_is_container() {
|
||||
self.render_window_body_background(body);
|
||||
}
|
||||
let body = self.base.scale_rect(body);
|
||||
self.render_child_or_snapshot(
|
||||
&child.node,
|
||||
x + content.x1(),
|
||||
y + content.y1(),
|
||||
Some(&body),
|
||||
);
|
||||
child
|
||||
.node
|
||||
.node_render(self, x + content.x1(), y + content.y1(), Some(&body));
|
||||
self.stretch = None;
|
||||
self.corner_radius = None;
|
||||
} else {
|
||||
|
|
@ -807,13 +524,10 @@ impl Renderer<'_> {
|
|||
};
|
||||
let cr = self.state.theme.corner_radius.get();
|
||||
for child in container.children.iter() {
|
||||
let layout_body = child.body.get();
|
||||
if layout_body.x1() >= container.width.get()
|
||||
|| layout_body.y1() >= container.height.get()
|
||||
{
|
||||
let body = child.body.get();
|
||||
if body.x1() >= container.width.get() || body.y1() >= container.height.get() {
|
||||
break;
|
||||
}
|
||||
let body = self.presentation_child_body(container, &child.node, layout_body);
|
||||
if gap != 0 {
|
||||
let c = if child.border_color_is_focused.get() {
|
||||
&focused_border_color
|
||||
|
|
@ -830,7 +544,7 @@ impl Renderer<'_> {
|
|||
self.render_rounded_frame(frame, c, cr, bw, x, y);
|
||||
}
|
||||
}
|
||||
let content = child.content.get().at_point(body.x1(), body.y1());
|
||||
let content = child.content.get();
|
||||
self.stretch =
|
||||
if content.width() != body.width() || content.height() != body.height() {
|
||||
Some(self.base.scale_point(body.width(), body.height()))
|
||||
|
|
@ -842,16 +556,10 @@ impl Renderer<'_> {
|
|||
self.corner_radius = Some(inner_cr);
|
||||
}
|
||||
let body = body.move_(x, y);
|
||||
if !child.node.node_is_container() {
|
||||
self.render_window_body_background(body);
|
||||
}
|
||||
let body = self.base.scale_rect(body);
|
||||
self.render_child_or_snapshot(
|
||||
&child.node,
|
||||
x + content.x1(),
|
||||
y + content.y1(),
|
||||
Some(&body),
|
||||
);
|
||||
child
|
||||
.node
|
||||
.node_render(self, x + content.x1(), y + content.y1(), Some(&body));
|
||||
self.stretch = None;
|
||||
self.corner_radius = None;
|
||||
}
|
||||
|
|
@ -1085,10 +793,6 @@ impl Renderer<'_> {
|
|||
_ => return,
|
||||
};
|
||||
let pos = floating.position.get();
|
||||
let visual =
|
||||
self.state
|
||||
.animations
|
||||
.visual_rect(floating.node_id(), pos, self.state.now_nsec());
|
||||
let theme = &self.state.theme;
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let bc = if floating.active.get() {
|
||||
|
|
@ -1097,27 +801,16 @@ impl Renderer<'_> {
|
|||
theme.colors.border.get()
|
||||
};
|
||||
let cr = theme.corner_radius.get();
|
||||
let outer = Rect::new_sized_saturating(0, 0, visual.width(), visual.height());
|
||||
let outer = Rect::new_sized_saturating(0, 0, pos.width(), pos.height());
|
||||
self.render_rounded_frame(outer, &bc, cr, bw, x, y);
|
||||
let body = Rect::new_sized_saturating(
|
||||
x + bw,
|
||||
y + bw,
|
||||
visual.width() - 2 * bw,
|
||||
visual.height() - 2 * bw,
|
||||
);
|
||||
let body =
|
||||
Rect::new_sized_saturating(x + bw, y + bw, pos.width() - 2 * bw, pos.height() - 2 * bw);
|
||||
let scissor_body = self.base.scale_rect(body);
|
||||
self.stretch = if pos.width() != visual.width() || pos.height() != visual.height() {
|
||||
Some(self.base.scale_point(body.width(), body.height()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !cr.is_zero() {
|
||||
let inner_cr = self.scale_corner_radius(cr.expanded_by(-(bw as f32)));
|
||||
self.corner_radius = Some(inner_cr);
|
||||
}
|
||||
self.render_window_body_background(body);
|
||||
self.render_child_or_snapshot(&child, body.x1(), body.y1(), Some(&scissor_body));
|
||||
self.stretch = None;
|
||||
child.node_render(self, body.x1(), body.y1(), Some(&scissor_body));
|
||||
self.corner_radius = None;
|
||||
}
|
||||
|
||||
|
|
|
|||
1120
src/state.rs
1120
src/state.rs
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::transaction::{BackendConnectorTransactionError, ConnectorTransaction},
|
||||
backend::transaction::BackendConnectorTransactionError,
|
||||
state::State,
|
||||
utils::{
|
||||
errorfmt::ErrorFmt,
|
||||
|
|
@ -136,15 +136,7 @@ impl Idle {
|
|||
}
|
||||
|
||||
fn try_set_idle(&self, idle: bool) -> Result<(), BackendConnectorTransactionError> {
|
||||
let mut tran = ConnectorTransaction::new(&self.state);
|
||||
for connector in self.state.connectors.lock().values() {
|
||||
let mut state = connector.state.borrow().clone();
|
||||
state.active = !idle;
|
||||
tran.add(&connector.connector, state)?;
|
||||
}
|
||||
tran.prepare()?.apply()?.commit();
|
||||
self.state.set_backend_idle(idle);
|
||||
Ok(())
|
||||
self.state.set_connectors_active(!idle)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{InputDevice, InputDeviceCapability},
|
||||
backend::{InputDevice, InputDeviceCapability, InputEvent, KeyState},
|
||||
ifs::wl_seat::PX_PER_SCROLL,
|
||||
state::{DeviceHandlerData, InputDeviceData, State},
|
||||
tasks::udev_utils::{UdevProps, udev_props},
|
||||
|
|
@ -80,13 +80,21 @@ impl DeviceHandler {
|
|||
}
|
||||
if let Some(seat) = self.data.seat.get() {
|
||||
let mut any_events = false;
|
||||
let mut key_press = false;
|
||||
let mut mouse_move = false;
|
||||
while let Some(event) = self.dev.event() {
|
||||
let (is_key_press, is_mouse_move) = dpms_wake_triggers_for(&event);
|
||||
key_press |= is_key_press;
|
||||
mouse_move |= is_mouse_move;
|
||||
if is_key_press || is_mouse_move {
|
||||
self.state.input_occurred(is_key_press, is_mouse_move);
|
||||
}
|
||||
seat.event(&self.data, event);
|
||||
any_events = true;
|
||||
}
|
||||
if any_events {
|
||||
seat.mark_last_active();
|
||||
self.state.input_occurred();
|
||||
self.state.input_occurred(key_press, mouse_move);
|
||||
}
|
||||
} else {
|
||||
while self.dev.event().is_some() {
|
||||
|
|
@ -105,3 +113,16 @@ impl DeviceHandler {
|
|||
self.data.set_seat(&self.state, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn dpms_wake_triggers_for(event: &InputEvent) -> (bool, bool) {
|
||||
match event {
|
||||
InputEvent::Key {
|
||||
state: KeyState::Pressed,
|
||||
..
|
||||
} => (true, false),
|
||||
InputEvent::ConnectorPosition { .. }
|
||||
| InputEvent::Motion { .. }
|
||||
| InputEvent::MotionAbsolute { .. } => (false, true),
|
||||
_ => (false, false),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ impl ToolClient {
|
|||
self_id: s.registry,
|
||||
name: s.jay_compositor.0,
|
||||
interface: JayCompositor.name(),
|
||||
version: s.jay_compositor.1.min(30),
|
||||
version: s.jay_compositor.1.min(31),
|
||||
id: id.into(),
|
||||
});
|
||||
self.jay_compositor.set(Some(id));
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ use {
|
|||
numcell::NumCell,
|
||||
on_drop_event::OnDropEvent,
|
||||
rc_eq::rc_eq,
|
||||
scroller::Scroller,
|
||||
threshold_counter::ThresholdCounter,
|
||||
},
|
||||
},
|
||||
|
|
@ -132,8 +131,6 @@ pub struct ContainerNode {
|
|||
pub content_height: Cell<i32>,
|
||||
pub sum_factors: Cell<f64>,
|
||||
pub layout_scheduled: Cell<bool>,
|
||||
animate_next_layout: Cell<bool>,
|
||||
pub mono_transition_animation_pending: Cell<bool>,
|
||||
compute_render_positions_scheduled: Cell<bool>,
|
||||
num_children: NumCell<usize>,
|
||||
pub children: LinkedList<ContainerChild>,
|
||||
|
|
@ -151,7 +148,6 @@ pub struct ContainerNode {
|
|||
pub child_removed: Rc<LazyEventSource>,
|
||||
pub all_children_resized: Rc<LazyEventSource>,
|
||||
pub tab_bar: RefCell<Option<TabBar>>,
|
||||
scroll: Scroller,
|
||||
pub update_tab_textures_scheduled: Cell<bool>,
|
||||
pub ephemeral: Cell<Ephemeral>,
|
||||
}
|
||||
|
|
@ -242,8 +238,6 @@ impl ContainerNode {
|
|||
content_height: Cell::new(0),
|
||||
sum_factors: Cell::new(1.0),
|
||||
layout_scheduled: Cell::new(false),
|
||||
animate_next_layout: Cell::new(false),
|
||||
mono_transition_animation_pending: Cell::new(false),
|
||||
compute_render_positions_scheduled: Cell::new(false),
|
||||
num_children: NumCell::new(1),
|
||||
children,
|
||||
|
|
@ -268,7 +262,6 @@ impl ContainerNode {
|
|||
child_removed: state.lazy_event_sources.create_source(),
|
||||
all_children_resized: state.post_layout_event_sources.create_source(),
|
||||
tab_bar: RefCell::new(None),
|
||||
scroll: Default::default(),
|
||||
update_tab_textures_scheduled: Cell::new(false),
|
||||
ephemeral: Cell::new(Ephemeral::Off),
|
||||
});
|
||||
|
|
@ -293,47 +286,6 @@ impl ContainerNode {
|
|||
self.add_child_x(prev, new, |prev, new| self.add_child_after_(prev, new));
|
||||
}
|
||||
|
||||
pub fn add_tiled_child_after(self: &Rc<Self>, prev: &dyn Node, new: Rc<dyn ToplevelNode>) {
|
||||
if !self.state.theme.autotile_enabled.get()
|
||||
|| self.mono_child.is_some()
|
||||
|| self.num_children.get() <= 1
|
||||
{
|
||||
self.add_child_after(prev, new);
|
||||
return;
|
||||
}
|
||||
let focused = self
|
||||
.child_nodes
|
||||
.borrow()
|
||||
.get(&prev.node_id())
|
||||
.map(|n| n.to_ref());
|
||||
let Some(focused) = focused else {
|
||||
log::error!(
|
||||
"Tried to autotile a child into a container but the preceding node is not in the container"
|
||||
);
|
||||
return;
|
||||
};
|
||||
let focused_node = focused.node.clone();
|
||||
let focused_active = focused_node.tl_data().active();
|
||||
let sub = ContainerNode::new(
|
||||
&self.state,
|
||||
&self.workspace.get(),
|
||||
focused_node.clone(),
|
||||
self.split.get().other(),
|
||||
);
|
||||
// Autotile-created groups are structural and collapse once only one
|
||||
// child remains. Explicit make-group commands control their own
|
||||
// grouping through the regular manual paths.
|
||||
sub.ephemeral.set(Ephemeral::On);
|
||||
sub.append_child(new);
|
||||
let sub_id = sub.node_id();
|
||||
self.clone().cnode_replace_child(&*focused_node, sub);
|
||||
if focused_active
|
||||
&& let Some(group) = self.child_nodes.borrow().get(&sub_id).map(|n| n.to_ref())
|
||||
{
|
||||
self.update_child_active(&group, true, 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_child_before(self: &Rc<Self>, prev: &dyn Node, new: Rc<dyn ToplevelNode>) {
|
||||
self.add_child_x(prev, new, |prev, new| self.add_child_before_(prev, new));
|
||||
}
|
||||
|
|
@ -484,10 +436,6 @@ impl ContainerNode {
|
|||
}
|
||||
|
||||
fn schedule_layout(self: &Rc<Self>) {
|
||||
if self.state.layout_animations_requested.get() || self.state.layout_animations_active.get()
|
||||
{
|
||||
self.animate_next_layout.set(true);
|
||||
}
|
||||
if !self.layout_scheduled.replace(true) {
|
||||
self.state.pending_container_layout.push(self.clone());
|
||||
}
|
||||
|
|
@ -519,7 +467,6 @@ impl ContainerNode {
|
|||
fn perform_layout(self: &Rc<Self>) {
|
||||
self.layout_scheduled.set(false);
|
||||
if self.num_children.get() == 0 {
|
||||
self.mono_transition_animation_pending.set(false);
|
||||
return;
|
||||
}
|
||||
if let Some(child) = self.mono_child.get() {
|
||||
|
|
@ -537,7 +484,6 @@ impl ContainerNode {
|
|||
self.damage();
|
||||
}
|
||||
}
|
||||
self.mono_transition_animation_pending.set(false);
|
||||
}
|
||||
|
||||
fn perform_mono_layout(self: &Rc<Self>, child: &ContainerChild) {
|
||||
|
|
@ -710,7 +656,6 @@ impl ContainerNode {
|
|||
op.child.factor.set(child_factor);
|
||||
self.sum_factors.set(sum_factors);
|
||||
// log::info!("pointer_move");
|
||||
self.state.suppress_animations_for_next_layout.set(true);
|
||||
self.schedule_layout_immediate();
|
||||
}
|
||||
}
|
||||
|
|
@ -796,18 +741,6 @@ impl ContainerNode {
|
|||
self.activate_child2(child, false);
|
||||
}
|
||||
|
||||
fn activate_child_from_input(
|
||||
self: &Rc<Self>,
|
||||
child: &NodeRef<ContainerChild>,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
) {
|
||||
self.activate_child(child);
|
||||
child
|
||||
.node
|
||||
.clone()
|
||||
.node_do_focus(seat, Direction::Unspecified);
|
||||
}
|
||||
|
||||
fn activate_child2(self: &Rc<Self>, child: &NodeRef<ContainerChild>, preserve_focus: bool) {
|
||||
if let Some(mc) = self.mono_child.get() {
|
||||
if mc.node.node_id() == child.node.node_id() {
|
||||
|
|
@ -883,7 +816,6 @@ impl ContainerNode {
|
|||
}
|
||||
}
|
||||
self.mono_child.set(child.clone());
|
||||
self.mono_transition_animation_pending.set(true);
|
||||
if child.is_some() {
|
||||
self.rebuild_tab_bar();
|
||||
} else {
|
||||
|
|
@ -1425,6 +1357,42 @@ impl ContainerNode {
|
|||
}
|
||||
|
||||
pub fn insert_child(self: &Rc<Self>, node: Rc<dyn ToplevelNode>, direction: Direction) {
|
||||
// Autotile: if the container would become too narrow/tall, wrap the
|
||||
// focused child and new node in a perpendicular sub-container.
|
||||
if self.state.theme.autotile_enabled.get() && self.mono_child.is_none() {
|
||||
let (pw, ph) = self.predict_child_body_size();
|
||||
let opposite = match self.split.get() {
|
||||
ContainerSplit::Horizontal if pw > 0 && ph > 0 && pw < ph => {
|
||||
Some(ContainerSplit::Vertical)
|
||||
}
|
||||
ContainerSplit::Vertical if pw > 0 && ph > 0 && ph < pw => {
|
||||
Some(ContainerSplit::Horizontal)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(opp_split) = opposite {
|
||||
if let Some(focused) = self.focus_history.last() {
|
||||
if self.num_children.get() <= 1 {
|
||||
// Single child, autotile not applicable.
|
||||
} else {
|
||||
let focused_node = focused.node.clone();
|
||||
let was_ephemeral = self.ephemeral.replace(Ephemeral::Off);
|
||||
self.clone().cnode_remove_child2(&*focused_node, true);
|
||||
self.ephemeral.set(was_ephemeral);
|
||||
let sub = ContainerNode::new(
|
||||
&self.state,
|
||||
&self.workspace.get(),
|
||||
focused_node,
|
||||
opp_split,
|
||||
);
|
||||
sub.ephemeral.set(Ephemeral::On);
|
||||
sub.append_child(node);
|
||||
self.append_child(sub);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let (split, right) = direction_to_split(direction);
|
||||
if split != self.split.get() || right {
|
||||
self.append_child(node);
|
||||
|
|
@ -1534,7 +1502,7 @@ impl ContainerNode {
|
|||
fn button(
|
||||
self: Rc<Self>,
|
||||
id: CursorType,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
_seat: &Rc<WlSeatGlobal>,
|
||||
_time_usec: u64,
|
||||
pressed: bool,
|
||||
button: u32,
|
||||
|
|
@ -1564,7 +1532,7 @@ impl ContainerNode {
|
|||
if let Some(child) = children.get(&child_id) {
|
||||
let child_ref = child.to_ref();
|
||||
drop(children);
|
||||
self.activate_child_from_input(&child_ref, seat);
|
||||
self.activate_child(&child_ref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -1791,42 +1759,10 @@ enum SeatOpKind {
|
|||
|
||||
pub async fn container_layout(state: Rc<State>) {
|
||||
loop {
|
||||
let first = state.pending_container_layout.pop().await;
|
||||
let mut containers = vec![first];
|
||||
while let Some(container) = state.pending_container_layout.try_pop() {
|
||||
containers.push(container);
|
||||
let container = state.pending_container_layout.pop().await;
|
||||
if container.layout_scheduled.get() {
|
||||
container.perform_layout();
|
||||
}
|
||||
let mut animated = vec![];
|
||||
let mut immediate = vec![];
|
||||
for container in containers {
|
||||
if !container.layout_scheduled.get() {
|
||||
continue;
|
||||
}
|
||||
let animate = container.animate_next_layout.replace(false)
|
||||
&& !state.suppress_animations_for_next_layout.get();
|
||||
if animate {
|
||||
animated.push(container);
|
||||
} else {
|
||||
immediate.push(container);
|
||||
}
|
||||
}
|
||||
if !animated.is_empty() {
|
||||
let prev_active = state.layout_animations_active.replace(true);
|
||||
state.begin_layout_animation_batch();
|
||||
for container in animated {
|
||||
container.perform_layout();
|
||||
}
|
||||
state.finish_layout_animation_batch();
|
||||
state.layout_animations_active.set(prev_active);
|
||||
}
|
||||
if !immediate.is_empty() {
|
||||
let prev_active = state.layout_animations_active.replace(false);
|
||||
for container in immediate {
|
||||
container.perform_layout();
|
||||
}
|
||||
state.layout_animations_active.set(prev_active);
|
||||
}
|
||||
state.suppress_animations_for_next_layout.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2081,33 +2017,31 @@ impl Node for ContainerNode {
|
|||
self.button(id, seat, time_usec, state == ButtonState::Pressed, button);
|
||||
}
|
||||
|
||||
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
||||
fn node_on_axis_event(self: Rc<Self>, _seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
||||
if self.mono_child.is_none() {
|
||||
return;
|
||||
}
|
||||
let steps = match self.scroll.handle(event) {
|
||||
Some(steps) => steps,
|
||||
// Use vertical scroll (index 1) to switch tabs.
|
||||
let v = match event.v120[1].get() {
|
||||
Some(v) if v != 0 => v,
|
||||
_ => return,
|
||||
};
|
||||
let mut target = match self.mono_child.get() {
|
||||
let mono = match self.mono_child.get() {
|
||||
Some(m) => m,
|
||||
None => return,
|
||||
};
|
||||
let current_id = target.node.node_id();
|
||||
for _ in 0..steps.abs() {
|
||||
let next = if steps > 0 {
|
||||
target.next().or_else(|| self.children.first())
|
||||
} else {
|
||||
target.prev().or_else(|| self.children.last())
|
||||
};
|
||||
match next {
|
||||
Some(next) => target = next,
|
||||
None => break,
|
||||
let next = if v > 0 {
|
||||
// Scroll down → next tab.
|
||||
mono.next().or_else(|| self.children.first())
|
||||
} else {
|
||||
// Scroll up → previous tab.
|
||||
mono.prev().or_else(|| self.children.last())
|
||||
};
|
||||
if let Some(next) = next {
|
||||
if next.node.node_id() != mono.node.node_id() {
|
||||
self.activate_child(&next);
|
||||
}
|
||||
}
|
||||
if target.node.node_id() != current_id {
|
||||
self.activate_child_from_input(&target, seat);
|
||||
}
|
||||
}
|
||||
|
||||
fn node_on_leave(&self, seat: &WlSeatGlobal) {
|
||||
|
|
@ -2325,11 +2259,6 @@ impl ContainingNode for ContainerNode {
|
|||
}
|
||||
// log::info!("cnode_remove_child2");
|
||||
self.rebuild_tab_bar();
|
||||
if self.state.animations.enabled.get()
|
||||
&& !self.state.suppress_animations_for_next_layout.get()
|
||||
{
|
||||
self.animate_next_layout.set(true);
|
||||
}
|
||||
self.schedule_layout();
|
||||
self.cancel_seat_ops();
|
||||
self.child_removed.trigger();
|
||||
|
|
|
|||
|
|
@ -8,25 +8,18 @@ use {
|
|||
renderer::Renderer,
|
||||
state::State,
|
||||
tree::{
|
||||
Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink,
|
||||
NodeLocation, OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination,
|
||||
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink, NodeLocation,
|
||||
OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination,
|
||||
WorkspaceNodeId, walker::NodeVisitor,
|
||||
},
|
||||
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
mem,
|
||||
ops::Deref,
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
std::{cell::Cell, ops::Deref, rc::Rc},
|
||||
};
|
||||
|
||||
pub struct DisplayNode {
|
||||
pub id: NodeId,
|
||||
pub extents: Cell<Rect>,
|
||||
visible: Cell<bool>,
|
||||
suspend_restore_kb_foci: RefCell<Vec<(Rc<WlSeatGlobal>, Weak<dyn Node>)>>,
|
||||
pub outputs: CopyHashMap<ConnectorId, Rc<OutputNode>>,
|
||||
pub stacked: Rc<LinkedList<Rc<dyn StackedNode>>>,
|
||||
pub stacked_above_layers: Rc<LinkedList<Rc<dyn StackedNode>>>,
|
||||
|
|
@ -38,8 +31,6 @@ impl DisplayNode {
|
|||
let slf = Self {
|
||||
id,
|
||||
extents: Default::default(),
|
||||
visible: Default::default(),
|
||||
suspend_restore_kb_foci: Default::default(),
|
||||
outputs: Default::default(),
|
||||
stacked: Default::default(),
|
||||
stacked_above_layers: Default::default(),
|
||||
|
|
@ -80,17 +71,6 @@ impl DisplayNode {
|
|||
|
||||
pub fn update_visible(&self, state: &State) {
|
||||
let visible = state.root_visible();
|
||||
let was_visible = self.visible.replace(visible);
|
||||
if !visible && was_visible {
|
||||
let mut foci = self.suspend_restore_kb_foci.borrow_mut();
|
||||
foci.clear();
|
||||
for seat in state.globals.seats.lock().values() {
|
||||
let node = seat.get_keyboard_node();
|
||||
if node.node_id() != self.id {
|
||||
foci.push((seat.clone(), Rc::downgrade(&node)));
|
||||
}
|
||||
}
|
||||
}
|
||||
for output in self.outputs.lock().values() {
|
||||
output.update_visible();
|
||||
}
|
||||
|
|
@ -102,20 +82,6 @@ impl DisplayNode {
|
|||
for seat in state.globals.seats.lock().values() {
|
||||
seat.set_visible(visible);
|
||||
}
|
||||
if visible && !was_visible {
|
||||
for (seat, node) in mem::take(&mut *self.suspend_restore_kb_foci.borrow_mut()) {
|
||||
if seat.get_keyboard_node().node_id() == self.id {
|
||||
if let Some(node) = node.upgrade()
|
||||
&& node.node_visible()
|
||||
{
|
||||
seat.focus_node(node);
|
||||
} else {
|
||||
seat.get_fallback_output()
|
||||
.take_keyboard_navigation_focus(&seat, Direction::Unspecified);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if visible {
|
||||
state.damage(self.extents.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@ use {
|
|||
};
|
||||
|
||||
tree_id!(FloatNodeId);
|
||||
|
||||
const COMMAND_MOVE_DELTA: i32 = 100;
|
||||
|
||||
pub struct FloatNode {
|
||||
pub id: FloatNodeId,
|
||||
pub state: Rc<State>,
|
||||
|
|
@ -156,13 +153,6 @@ impl FloatNode {
|
|||
_ => return,
|
||||
};
|
||||
let pos = self.position.get();
|
||||
let spawn_in_pending = {
|
||||
let data = child.tl_data();
|
||||
data.spawn_in_pending.get() && data.kind.is_app_window() && !data.is_fullscreen.get()
|
||||
};
|
||||
if spawn_in_pending && self.visible.get() {
|
||||
self.state.queue_spawn_in_animation(self.id.into(), pos);
|
||||
}
|
||||
let theme = &self.state.theme;
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let cpos = Rect::new_sized_saturating(
|
||||
|
|
@ -373,50 +363,6 @@ impl FloatNode {
|
|||
y2 += y1 - pos.y1();
|
||||
}
|
||||
let new_pos = Rect::new_saturating(x1, y1, x2, y2);
|
||||
self.set_position(new_pos);
|
||||
}
|
||||
|
||||
pub fn move_by_direction(self: &Rc<Self>, direction: Direction) {
|
||||
let (dx, dy) = match direction {
|
||||
Direction::Left => (-COMMAND_MOVE_DELTA, 0),
|
||||
Direction::Down => (0, COMMAND_MOVE_DELTA),
|
||||
Direction::Up => (0, -COMMAND_MOVE_DELTA),
|
||||
Direction::Right => (COMMAND_MOVE_DELTA, 0),
|
||||
Direction::Unspecified => return,
|
||||
};
|
||||
self.set_position(self.position.get().move_(dx, dy));
|
||||
}
|
||||
|
||||
fn body_for_outer(&self, outer: Rect) -> Rect {
|
||||
let bw = self.state.theme.sizes.border_width.get();
|
||||
Rect::new_sized_saturating(
|
||||
outer.x1() + bw,
|
||||
outer.y1() + bw,
|
||||
outer.width() - 2 * bw,
|
||||
outer.height() - 2 * bw,
|
||||
)
|
||||
}
|
||||
|
||||
fn queue_position_animation(&self, old_pos: Rect, new_pos: Rect) {
|
||||
self.state
|
||||
.clone()
|
||||
.queue_tiled_animation(self.id.into(), old_pos, new_pos);
|
||||
let Some(child) = self.child.get() else {
|
||||
return;
|
||||
};
|
||||
self.state.clone().queue_tiled_animation(
|
||||
child.node_id(),
|
||||
self.body_for_outer(old_pos),
|
||||
self.body_for_outer(new_pos),
|
||||
);
|
||||
}
|
||||
|
||||
fn set_position(self: &Rc<Self>, new_pos: Rect) {
|
||||
let pos = self.position.get();
|
||||
if new_pos == pos {
|
||||
return;
|
||||
}
|
||||
self.queue_position_animation(pos, new_pos);
|
||||
self.position.set(new_pos);
|
||||
if self.visible.get() {
|
||||
self.state.damage(pos);
|
||||
|
|
@ -845,7 +791,13 @@ impl ContainingNode for FloatNode {
|
|||
let bw = theme.sizes.border_width.get();
|
||||
let (x, y) = (x - bw, y - bw);
|
||||
let pos = self.position.get();
|
||||
self.set_position(pos.at_point(x, y));
|
||||
if pos.position() != (x, y) {
|
||||
let new_pos = pos.at_point(x, y);
|
||||
self.position.set(new_pos);
|
||||
self.state.damage(pos);
|
||||
self.state.damage(new_pos);
|
||||
self.schedule_layout();
|
||||
}
|
||||
}
|
||||
|
||||
fn cnode_resize_child(
|
||||
|
|
@ -876,7 +828,14 @@ impl ContainingNode for FloatNode {
|
|||
y2 = (v + bw).max(y1 + bw + bw);
|
||||
}
|
||||
let new_pos = Rect::new_saturating(x1, y1, x2, y2);
|
||||
self.set_position(new_pos);
|
||||
if new_pos != pos {
|
||||
self.position.set(new_pos);
|
||||
if self.visible.get() {
|
||||
self.state.damage(pos);
|
||||
self.state.damage(new_pos);
|
||||
}
|
||||
self.schedule_layout();
|
||||
}
|
||||
}
|
||||
|
||||
fn cnode_pinned(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
use {
|
||||
crate::{
|
||||
animation::{
|
||||
RetainedExitLayer, RetainedToplevel,
|
||||
multiphase::{
|
||||
MultiphaseHierarchyPosition, MultiphaseHierarchyTransition,
|
||||
MultiphaseWindowHierarchy, PhaseAxis,
|
||||
},
|
||||
},
|
||||
client::{Client, ClientId},
|
||||
criteria::{
|
||||
CritDestroyListener, CritMatcherId,
|
||||
|
|
@ -124,7 +117,6 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
|||
let parent_was_none = data.parent.set(Some(parent.clone())).is_none();
|
||||
if parent_was_none {
|
||||
data.mapped_during_iteration.set(data.state.eng.iteration());
|
||||
data.spawn_in_pending.set(data.kind.is_app_window());
|
||||
data.property_changed(TL_CHANGED_NEW);
|
||||
}
|
||||
let was_floating = data.parent_is_float.get();
|
||||
|
|
@ -192,57 +184,6 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
|||
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
||||
let data = self.tl_data();
|
||||
let prev = data.desired_extents.replace(*rect);
|
||||
let target_hierarchy = self.tl_multiphase_hierarchy_position();
|
||||
let hierarchy = MultiphaseWindowHierarchy::new(
|
||||
data.layout_animation_position.replace(target_hierarchy),
|
||||
target_hierarchy,
|
||||
);
|
||||
let spawn_in_pending = data.spawn_in_pending.get();
|
||||
let spawn_in_eligible = spawn_in_pending
|
||||
&& !rect.is_empty()
|
||||
&& data.visible.get()
|
||||
&& !data.is_fullscreen.get()
|
||||
&& data.kind.is_app_window()
|
||||
&& !self.node_is_container();
|
||||
let parent_container = data
|
||||
.parent
|
||||
.get()
|
||||
.and_then(|parent| parent.node_into_container());
|
||||
let parent_is_mono = parent_container
|
||||
.as_ref()
|
||||
.is_some_and(|container| container.mono_child.is_some());
|
||||
let parent_mono_transition = parent_container
|
||||
.as_ref()
|
||||
.is_some_and(|container| container.mono_transition_animation_pending.get());
|
||||
let active_mono_boundary = matches!(
|
||||
hierarchy.transition,
|
||||
MultiphaseHierarchyTransition::EnteringMono
|
||||
| MultiphaseHierarchyTransition::ExitingMono
|
||||
) && parent_mono_transition
|
||||
&& (hierarchy.source.mono_active || hierarchy.target.mono_active);
|
||||
if prev != *rect
|
||||
&& !prev.is_empty()
|
||||
&& !rect.is_empty()
|
||||
&& data.visible.get()
|
||||
&& !data.parent_is_float.get()
|
||||
&& !self.node_is_container()
|
||||
&& (!parent_is_mono || active_mono_boundary)
|
||||
{
|
||||
data.state.clone().queue_tiled_animation_with_hierarchy(
|
||||
data.node_id,
|
||||
prev,
|
||||
*rect,
|
||||
hierarchy,
|
||||
);
|
||||
}
|
||||
if spawn_in_eligible {
|
||||
data.state
|
||||
.clone()
|
||||
.queue_spawn_in_animation(data.node_id, *rect);
|
||||
}
|
||||
if spawn_in_eligible {
|
||||
data.spawn_in_pending.set(false);
|
||||
}
|
||||
if prev.size() != rect.size() {
|
||||
for sc in data.jay_screencasts.lock().values() {
|
||||
sc.schedule_realloc_or_reconfigure();
|
||||
|
|
@ -334,35 +275,6 @@ pub trait ToplevelNodeBase: Node {
|
|||
true
|
||||
}
|
||||
|
||||
fn tl_multiphase_hierarchy_position(&self) -> MultiphaseHierarchyPosition {
|
||||
let data = self.tl_data();
|
||||
let Some(parent) = data.parent.get() else {
|
||||
return Default::default();
|
||||
};
|
||||
let mut position = MultiphaseHierarchyPosition {
|
||||
parent: Some(parent.node_id()),
|
||||
..Default::default()
|
||||
};
|
||||
populate_multiphase_ancestor_splits(&mut position, Some(parent.clone()));
|
||||
if let Some(container) = parent.node_into_container() {
|
||||
position.split_axis = Some(match container.split.get() {
|
||||
ContainerSplit::Horizontal => PhaseAxis::Horizontal,
|
||||
ContainerSplit::Vertical => PhaseAxis::Vertical,
|
||||
});
|
||||
if let Some(mono) = container.mono_child.get() {
|
||||
position.parent_is_mono = true;
|
||||
position.mono_active = mono.node.node_id() == data.node_id;
|
||||
}
|
||||
for (idx, child) in container.children.iter().enumerate() {
|
||||
if child.node.node_id() == data.node_id {
|
||||
position.sibling_index = Some(idx.min(u16::MAX as usize) as u16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
position
|
||||
}
|
||||
|
||||
fn tl_set_active(&self, active: bool) {
|
||||
let _ = active;
|
||||
}
|
||||
|
|
@ -387,11 +299,6 @@ pub trait ToplevelNodeBase: Node {
|
|||
fn tl_scanout_surface(&self) -> Option<Rc<WlSurface>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn tl_animation_snapshot(&self) -> Option<Rc<RetainedToplevel>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn tl_restack_popups(&self) {
|
||||
// nothing
|
||||
}
|
||||
|
|
@ -432,31 +339,6 @@ pub trait ToplevelNodeBase: Node {
|
|||
}
|
||||
}
|
||||
|
||||
fn populate_multiphase_ancestor_splits(
|
||||
position: &mut MultiphaseHierarchyPosition,
|
||||
mut parent: Option<Rc<dyn ContainingNode>>,
|
||||
) {
|
||||
let mut depth = 0u16;
|
||||
while let Some(node) = parent {
|
||||
let Some(toplevel) = node.clone().node_into_toplevel() else {
|
||||
break;
|
||||
};
|
||||
depth = depth.saturating_add(1);
|
||||
if let Some(container) = node.node_into_container() {
|
||||
match container.split.get() {
|
||||
ContainerSplit::Horizontal => {
|
||||
position.nearest_horizontal_split_depth.get_or_insert(depth);
|
||||
}
|
||||
ContainerSplit::Vertical => {
|
||||
position.nearest_vertical_split_depth.get_or_insert(depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
parent = toplevel.tl_data().parent.get();
|
||||
}
|
||||
position.depth = depth;
|
||||
}
|
||||
|
||||
pub struct FullscreenedData {
|
||||
pub placeholder: Rc<PlaceholderNode>,
|
||||
pub workspace: Rc<WorkspaceNode>,
|
||||
|
|
@ -495,13 +377,6 @@ impl ToplevelType {
|
|||
ToplevelType::XWindow { .. } => window::X_WINDOW,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_app_window(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ToplevelType::XdgToplevel(_) | ToplevelType::XWindow(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ToplevelData {
|
||||
|
|
@ -524,10 +399,8 @@ pub struct ToplevelData {
|
|||
pub title: RefCell<String>,
|
||||
pub parent: CloneCell<Option<Rc<dyn ContainingNode>>>,
|
||||
pub mapped_during_iteration: Cell<u64>,
|
||||
pub spawn_in_pending: Cell<bool>,
|
||||
pub pos: Cell<Rect>,
|
||||
pub desired_extents: Cell<Rect>,
|
||||
pub layout_animation_position: Cell<MultiphaseHierarchyPosition>,
|
||||
pub seat_state: NodeSeatState,
|
||||
pub wants_attention: Cell<bool>,
|
||||
pub requested_attention: Cell<bool>,
|
||||
|
|
@ -589,10 +462,8 @@ impl ToplevelData {
|
|||
title: RefCell::new(title),
|
||||
parent: Default::default(),
|
||||
mapped_during_iteration: Cell::new(0),
|
||||
spawn_in_pending: Cell::new(false),
|
||||
pos: Default::default(),
|
||||
desired_extents: Default::default(),
|
||||
layout_animation_position: Default::default(),
|
||||
seat_state: Default::default(),
|
||||
wants_attention: Cell::new(false),
|
||||
requested_attention: Cell::new(false),
|
||||
|
|
@ -979,7 +850,7 @@ impl ToplevelData {
|
|||
}
|
||||
fd.workspace.remove_fullscreen_node();
|
||||
if fd.placeholder.is_destroyed() {
|
||||
state.map_tiled_without_autotile(node);
|
||||
state.map_tiled(node);
|
||||
return;
|
||||
}
|
||||
let parent = fd.placeholder.tl_data().parent.take().unwrap();
|
||||
|
|
@ -1064,62 +935,6 @@ impl ToplevelData {
|
|||
self.mapped_during_iteration.get() == self.state.eng.iteration()
|
||||
}
|
||||
|
||||
pub fn queue_spawn_out(&self, node: &dyn ToplevelNode, retained: Option<Rc<RetainedToplevel>>) {
|
||||
if !self.kind.is_app_window()
|
||||
|| !self.visible.get()
|
||||
|| self.is_fullscreen.get()
|
||||
|| node.node_is_container()
|
||||
{
|
||||
return;
|
||||
}
|
||||
let Some(retained) = retained else {
|
||||
return;
|
||||
};
|
||||
let bw = self.state.theme.sizes.border_width.get().max(0);
|
||||
let now = self.state.now_nsec();
|
||||
let (outer, frame_inset, layer) = if self.parent_is_float.get() {
|
||||
let Some(float) = self.float.get() else {
|
||||
return;
|
||||
};
|
||||
(
|
||||
self.state
|
||||
.animations
|
||||
.visual_rect(float.node_id(), float.position.get(), now),
|
||||
bw,
|
||||
RetainedExitLayer::Floating,
|
||||
)
|
||||
} else {
|
||||
let body =
|
||||
self.state
|
||||
.animations
|
||||
.visual_rect(self.node_id, node.node_absolute_position(), now);
|
||||
if body.is_empty() {
|
||||
return;
|
||||
}
|
||||
if self.state.theme.sizes.gap.get() != 0 {
|
||||
(
|
||||
Rect::new_sized_saturating(
|
||||
body.x1() - bw,
|
||||
body.y1() - bw,
|
||||
body.width() + 2 * bw,
|
||||
body.height() + 2 * bw,
|
||||
),
|
||||
bw,
|
||||
RetainedExitLayer::Tiled,
|
||||
)
|
||||
} else {
|
||||
(body, 0, RetainedExitLayer::Tiled)
|
||||
}
|
||||
};
|
||||
self.state.clone().queue_spawn_out_animation(
|
||||
outer,
|
||||
frame_inset,
|
||||
retained,
|
||||
self.active(),
|
||||
layer,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_content_type(&self, content_type: Option<ContentType>) {
|
||||
if self.content_type.replace(content_type) != content_type {
|
||||
self.property_changed(TL_CHANGED_CONTENT_TY);
|
||||
|
|
@ -1228,26 +1043,6 @@ pub fn toplevel_create_split(state: &Rc<State>, tl: Rc<dyn ToplevelNode>, axis:
|
|||
}
|
||||
}
|
||||
|
||||
fn float_outer_for_body(state: &State, body: Rect) -> Rect {
|
||||
let bw = state.theme.sizes.border_width.get();
|
||||
Rect::new_sized_saturating(
|
||||
body.x1() - bw,
|
||||
body.y1() - bw,
|
||||
body.width() + 2 * bw,
|
||||
body.height() + 2 * bw,
|
||||
)
|
||||
}
|
||||
|
||||
fn float_body_for_outer(state: &State, outer: Rect) -> Rect {
|
||||
let bw = state.theme.sizes.border_width.get();
|
||||
Rect::new_sized_saturating(
|
||||
outer.x1() + bw,
|
||||
outer.y1() + bw,
|
||||
outer.width() - 2 * bw,
|
||||
outer.height() - 2 * bw,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn toplevel_set_floating(state: &Rc<State>, tl: Rc<dyn ToplevelNode>, floating: bool) {
|
||||
let data = tl.tl_data();
|
||||
if data.is_fullscreen.get() {
|
||||
|
|
@ -1262,21 +1057,11 @@ pub fn toplevel_set_floating(state: &Rc<State>, tl: Rc<dyn ToplevelNode>, floati
|
|||
};
|
||||
if !floating {
|
||||
parent.cnode_remove_child2(&*tl, true);
|
||||
state.map_tiled_without_autotile(tl);
|
||||
state.map_tiled(tl);
|
||||
} else if let Some(ws) = data.workspace.get() {
|
||||
let node_id = data.node_id;
|
||||
let old_body =
|
||||
state
|
||||
.animations
|
||||
.visual_rect(node_id, tl.node_absolute_position(), state.now_nsec());
|
||||
let old_outer = float_outer_for_body(state, old_body);
|
||||
parent.cnode_remove_child2(&*tl, true);
|
||||
let (width, height) = data.float_size(&ws);
|
||||
let floater = state.map_floating(tl, width, height, &ws, None);
|
||||
let new_outer = floater.position.get();
|
||||
let new_body = float_body_for_outer(state, new_outer);
|
||||
state.queue_linear_layout_animation(floater.node_id(), old_outer, new_outer);
|
||||
state.queue_linear_layout_animation(node_id, old_body, new_body);
|
||||
state.map_floating(tl, width, height, &ws, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1323,54 +1108,3 @@ pub fn toplevel_set_workspace(state: &Rc<State>, tl: Rc<dyn ToplevelNode>, ws: &
|
|||
tl.tl_set_fullscreen(true, Some(ws.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a toplevel from the tree so it can be parked in a scratchpad.
|
||||
///
|
||||
/// Returns `true` if the window was hidden. A placeholder, a window without a
|
||||
/// parent, or a window that refuses to leave fullscreen cannot be parked.
|
||||
pub fn toplevel_hide_for_scratchpad(tl: Rc<dyn ToplevelNode>) -> bool {
|
||||
if tl.node_is_placeholder() {
|
||||
return false;
|
||||
}
|
||||
let data = tl.tl_data();
|
||||
let workspace = data.workspace.get();
|
||||
if data.is_fullscreen.get() {
|
||||
tl.clone().tl_set_fullscreen(false, None);
|
||||
if data.is_fullscreen.get() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let Some(parent) = data.parent.get() else {
|
||||
return false;
|
||||
};
|
||||
let kb_foci = collect_kb_foci(tl.clone());
|
||||
parent.cnode_remove_child2(&*tl, true);
|
||||
data.parent.take();
|
||||
data.float.take();
|
||||
if data.parent_is_float.replace(false) {
|
||||
data.property_changed(TL_CHANGED_FLOATING);
|
||||
}
|
||||
if data.workspace.take().is_some() {
|
||||
data.property_changed(TL_CHANGED_WORKSPACE);
|
||||
}
|
||||
tl.tl_set_visible(false);
|
||||
if let Some(workspace) = &workspace {
|
||||
for seat in kb_foci {
|
||||
workspace
|
||||
.clone()
|
||||
.node_do_focus(&seat, Direction::Unspecified);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Maps a parked scratchpad window back onto `ws`. Scratchpad windows always
|
||||
/// return floating, regardless of how they were laid out before parking.
|
||||
pub fn toplevel_restore_from_scratchpad(
|
||||
state: &Rc<State>,
|
||||
tl: Rc<dyn ToplevelNode>,
|
||||
ws: &Rc<WorkspaceNode>,
|
||||
) {
|
||||
let (width, height) = tl.tl_data().float_size(ws);
|
||||
state.map_floating(tl.clone(), width, height, ws, None);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,10 +197,10 @@ impl WorkspaceNode {
|
|||
}
|
||||
self.pull_child_properties(&**container);
|
||||
let pos = self.position.get();
|
||||
container.clone().tl_change_extents(&pos);
|
||||
container.tl_set_parent(self.clone());
|
||||
container.tl_set_visible(self.container_visible());
|
||||
self.container.set(Some(container.clone()));
|
||||
container.clone().tl_change_extents(&pos);
|
||||
self.state.damage(self.position.get());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2034,7 +2034,6 @@ impl Wm {
|
|||
self.windows_by_surface_serial.remove(&serial);
|
||||
}
|
||||
if let Some(window) = data.window.take() {
|
||||
window.queue_spawn_out();
|
||||
window.destroy();
|
||||
}
|
||||
if let Some(parent) = data.parent.take() {
|
||||
|
|
|
|||
|
|
@ -64,9 +64,6 @@ pub enum SimpleCommand {
|
|||
SetFloating(bool),
|
||||
ToggleFullscreen,
|
||||
SetFullscreen(bool),
|
||||
SendToScratchpad,
|
||||
ToggleScratchpad,
|
||||
CycleScratchpad,
|
||||
Forward(bool),
|
||||
EnableWindowManagement(bool),
|
||||
SetFloatAboveFullscreen(bool),
|
||||
|
|
@ -133,15 +130,6 @@ pub enum Action {
|
|||
MoveToWorkspace {
|
||||
name: String,
|
||||
},
|
||||
SendToScratchpad {
|
||||
name: String,
|
||||
},
|
||||
ToggleScratchpad {
|
||||
name: String,
|
||||
},
|
||||
CycleScratchpad {
|
||||
name: String,
|
||||
},
|
||||
Multi {
|
||||
actions: Vec<Action>,
|
||||
},
|
||||
|
|
@ -278,20 +266,6 @@ pub struct UiDrag {
|
|||
pub threshold: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Animations {
|
||||
pub enabled: Option<bool>,
|
||||
pub duration_ms: Option<u32>,
|
||||
pub style: Option<String>,
|
||||
pub curve: Option<AnimationCurveConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum AnimationCurveConfig {
|
||||
Preset(String),
|
||||
CubicBezier([f32; 4]),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OutputMatch {
|
||||
Any(Vec<OutputMatch>),
|
||||
|
|
@ -586,6 +560,8 @@ pub struct Config {
|
|||
pub inputs: Vec<Input>,
|
||||
pub idle: Option<Duration>,
|
||||
pub grace_period: Option<Duration>,
|
||||
pub key_press_enables_dpms: Option<bool>,
|
||||
pub mouse_move_enables_dpms: Option<bool>,
|
||||
pub explicit_sync_enabled: Option<bool>,
|
||||
pub focus_follows_mouse: bool,
|
||||
pub window_management_key: Option<ModifiedKeySym>,
|
||||
|
|
@ -593,7 +569,6 @@ pub struct Config {
|
|||
pub tearing: Option<Tearing>,
|
||||
pub libei: Libei,
|
||||
pub ui_drag: UiDrag,
|
||||
pub animations: Animations,
|
||||
pub xwayland: Option<Xwayland>,
|
||||
pub color_management: Option<ColorManagement>,
|
||||
pub float: Option<Float>,
|
||||
|
|
@ -612,14 +587,6 @@ pub struct Config {
|
|||
pub simple_im: Option<SimpleIm>,
|
||||
pub fallback_output_mode: Option<FallbackOutputMode>,
|
||||
pub mouse_follows_focus: Option<bool>,
|
||||
pub scratchpads: Vec<Scratchpad>,
|
||||
pub autotile: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Scratchpad {
|
||||
pub name: String,
|
||||
pub exec: Option<Exec>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -686,26 +653,3 @@ fn default_config_parses() {
|
|||
let input = include_bytes!("default-config.toml");
|
||||
parse_config(input, &Default::default(), |_| ()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_animation_curve_parses() {
|
||||
let input = b"
|
||||
[animations]
|
||||
curve = [0.25, 0.1, 0.25, 1.0]
|
||||
";
|
||||
let config = parse_config(input, &Default::default(), |_| ()).unwrap();
|
||||
assert_eq!(
|
||||
config.animations.curve,
|
||||
Some(AnimationCurveConfig::CubicBezier([0.25, 0.1, 0.25, 1.0]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn animation_style_parses() {
|
||||
let input = b"
|
||||
[animations]
|
||||
style = \"plain\"
|
||||
";
|
||||
let config = parse_config(input, &Default::default(), |_| ()).unwrap();
|
||||
assert_eq!(config.animations.style.as_deref(), Some("plain"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use {
|
|||
|
||||
pub mod action;
|
||||
mod actions;
|
||||
mod animations;
|
||||
mod capabilities;
|
||||
mod clean_logs_older_than;
|
||||
mod client_match;
|
||||
|
|
@ -41,7 +40,6 @@ pub mod modified_keysym;
|
|||
mod output;
|
||||
mod output_match;
|
||||
mod repeat_rate;
|
||||
mod scratchpad;
|
||||
pub mod shortcuts;
|
||||
mod simple_im;
|
||||
mod status;
|
||||
|
|
|
|||
|
|
@ -117,9 +117,6 @@ impl ActionParser<'_> {
|
|||
"toggle-fullscreen" => ToggleFullscreen,
|
||||
"enter-fullscreen" => SetFullscreen(true),
|
||||
"exit-fullscreen" => SetFullscreen(false),
|
||||
"send-to-scratchpad" => SendToScratchpad,
|
||||
"toggle-scratchpad" => ToggleScratchpad,
|
||||
"cycle-scratchpad" => CycleScratchpad,
|
||||
"focus-parent" => FocusParent,
|
||||
"close" => Close,
|
||||
"disable-pointer-constraint" => DisablePointerConstraint,
|
||||
|
|
@ -225,33 +222,6 @@ impl ActionParser<'_> {
|
|||
Ok(Action::MoveToWorkspace { name })
|
||||
}
|
||||
|
||||
fn parse_send_to_scratchpad(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||
let name = ext
|
||||
.extract(opt(str("name")))?
|
||||
.map(|name| name.value)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
Ok(Action::SendToScratchpad { name })
|
||||
}
|
||||
|
||||
fn parse_toggle_scratchpad(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||
let name = ext
|
||||
.extract(opt(str("name")))?
|
||||
.map(|name| name.value)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
Ok(Action::ToggleScratchpad { name })
|
||||
}
|
||||
|
||||
fn parse_cycle_scratchpad(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||
let name = ext
|
||||
.extract(opt(str("name")))?
|
||||
.map(|name| name.value)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
Ok(Action::CycleScratchpad { name })
|
||||
}
|
||||
|
||||
fn parse_configure_connector(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||
let con = ext
|
||||
.extract(val("connector"))?
|
||||
|
|
@ -581,9 +551,6 @@ impl Parser for ActionParser<'_> {
|
|||
"switch-to-vt" => self.parse_switch_to_vt(&mut ext),
|
||||
"show-workspace" => self.parse_show_workspace(&mut ext),
|
||||
"move-to-workspace" => self.parse_move_to_workspace(&mut ext),
|
||||
"send-to-scratchpad" => self.parse_send_to_scratchpad(&mut ext),
|
||||
"toggle-scratchpad" => self.parse_toggle_scratchpad(&mut ext),
|
||||
"cycle-scratchpad" => self.parse_cycle_scratchpad(&mut ext),
|
||||
"configure-connector" => self.parse_configure_connector(&mut ext),
|
||||
"configure-input" => self.parse_configure_input(&mut ext),
|
||||
"configure-output" => self.parse_configure_output(&mut ext),
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
config::{
|
||||
AnimationCurveConfig, Animations,
|
||||
context::Context,
|
||||
extractor::{Extractor, ExtractorError, bol, n32, opt, recover, str, val},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
},
|
||||
toml::{
|
||||
toml_span::{DespanExt, Span, Spanned, SpannedExt},
|
||||
toml_value::Value,
|
||||
},
|
||||
},
|
||||
indexmap::IndexMap,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AnimationsParserError {
|
||||
#[error(transparent)]
|
||||
Expected(#[from] UnexpectedDataType),
|
||||
#[error(transparent)]
|
||||
Extract(#[from] ExtractorError),
|
||||
#[error("Expected animation curve to be a string or an array")]
|
||||
CurveType,
|
||||
#[error("Cubic-bezier animation curves must contain exactly four values")]
|
||||
CubicBezierLen,
|
||||
#[error("Cubic-bezier animation curve entries must be finite floats or integers")]
|
||||
CubicBezierValue,
|
||||
#[error("Cubic-bezier x control points must be between 0 and 1")]
|
||||
CubicBezierXRange,
|
||||
}
|
||||
|
||||
pub struct AnimationsParser<'a>(pub &'a Context<'a>);
|
||||
|
||||
impl Parser for AnimationsParser<'_> {
|
||||
type Value = Animations;
|
||||
type Error = AnimationsParserError;
|
||||
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||
|
||||
fn parse_table(
|
||||
&mut self,
|
||||
span: Span,
|
||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
let mut ext = Extractor::new(self.0, span, table);
|
||||
let (enabled, duration_ms, style, curve) = ext.extract((
|
||||
recover(opt(bol("enabled"))),
|
||||
recover(opt(n32("duration-ms"))),
|
||||
recover(opt(str("style"))),
|
||||
opt(val("curve")),
|
||||
))?;
|
||||
let curve = match curve {
|
||||
Some(curve) => Some(parse_curve(curve)?),
|
||||
None => None,
|
||||
};
|
||||
Ok(Animations {
|
||||
enabled: enabled.despan(),
|
||||
duration_ms: duration_ms.despan(),
|
||||
style: style.despan().map(|style| style.to_string()),
|
||||
curve,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_curve(
|
||||
curve: Spanned<&Value>,
|
||||
) -> Result<AnimationCurveConfig, Spanned<AnimationsParserError>> {
|
||||
match curve.value {
|
||||
Value::String(s) => Ok(AnimationCurveConfig::Preset(s.clone())),
|
||||
Value::Array(values) => parse_cubic_bezier(curve.span, values),
|
||||
_ => Err(AnimationsParserError::CurveType.spanned(curve.span)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_cubic_bezier(
|
||||
span: Span,
|
||||
values: &[Spanned<Value>],
|
||||
) -> Result<AnimationCurveConfig, Spanned<AnimationsParserError>> {
|
||||
if values.len() != 4 {
|
||||
return Err(AnimationsParserError::CubicBezierLen.spanned(span));
|
||||
}
|
||||
let mut points = [0.0; 4];
|
||||
for (idx, value) in values.iter().enumerate() {
|
||||
let f = match value.value {
|
||||
Value::Float(f) => f,
|
||||
Value::Integer(i) => i as f64,
|
||||
_ => return Err(AnimationsParserError::CubicBezierValue.spanned(value.span)),
|
||||
};
|
||||
if !f.is_finite() {
|
||||
return Err(AnimationsParserError::CubicBezierValue.spanned(value.span));
|
||||
}
|
||||
points[idx] = f as f32;
|
||||
}
|
||||
if !(0.0..=1.0).contains(&points[0]) || !(0.0..=1.0).contains(&points[2]) {
|
||||
return Err(AnimationsParserError::CubicBezierXRange.spanned(span));
|
||||
}
|
||||
Ok(AnimationCurveConfig::CubicBezier(points))
|
||||
}
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
use {
|
||||
crate::{
|
||||
config::{
|
||||
Action, Animations, Config, Libei, Theme, UiDrag,
|
||||
Action, Config, Libei, Theme, UiDrag,
|
||||
context::Context,
|
||||
extractor::{Extractor, ExtractorError, arr, bol, int, opt, recover, str, val},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
parsers::{
|
||||
action::ActionParser,
|
||||
actions::ActionsParser,
|
||||
animations::AnimationsParser,
|
||||
clean_logs_older_than::CleanLogsOlderThanParser,
|
||||
client_rule::ClientRulesParser,
|
||||
color_management::ColorManagementParser,
|
||||
|
|
@ -28,7 +27,6 @@ use {
|
|||
log_level::LogLevelParser,
|
||||
output::OutputsParser,
|
||||
repeat_rate::RepeatRateParser,
|
||||
scratchpad::ScratchpadsParser,
|
||||
shortcuts::{
|
||||
ComplexShortcutsParser, ShortcutsParser, ShortcutsParserError,
|
||||
parse_modified_keysym_str,
|
||||
|
|
@ -155,9 +153,7 @@ impl Parser for ConfigParser<'_> {
|
|||
fallback_output_mode_val,
|
||||
clean_logs_older_than_val,
|
||||
mouse_follows_focus,
|
||||
animations_val,
|
||||
),
|
||||
(scratchpads_val, autotile),
|
||||
) = ext.extract((
|
||||
(
|
||||
opt(val("keymap")),
|
||||
|
|
@ -217,9 +213,7 @@ impl Parser for ConfigParser<'_> {
|
|||
opt(val("fallback-output-mode")),
|
||||
opt(val("clean-logs-older-than")),
|
||||
recover(opt(bol("unstable-mouse-follows-focus"))),
|
||||
opt(val("animations")),
|
||||
),
|
||||
(opt(val("scratchpads")), recover(opt(bol("autotile")))),
|
||||
))?;
|
||||
let mut keymap = None;
|
||||
if let Some(value) = keymap_val {
|
||||
|
|
@ -373,11 +367,15 @@ impl Parser for ConfigParser<'_> {
|
|||
}
|
||||
let mut idle = None;
|
||||
let mut grace_period = None;
|
||||
let mut key_press_enables_dpms = None;
|
||||
let mut mouse_move_enables_dpms = None;
|
||||
if let Some(value) = idle_val {
|
||||
match value.parse(&mut IdleParser(self.0)) {
|
||||
Ok(v) => {
|
||||
idle = v.timeout;
|
||||
grace_period = v.grace_period;
|
||||
key_press_enables_dpms = v.key_press_enables_dpms;
|
||||
mouse_move_enables_dpms = v.mouse_move_enables_dpms;
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Could not parse the idle timeout: {}", self.0.error(e));
|
||||
|
|
@ -435,15 +433,6 @@ impl Parser for ConfigParser<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
let mut animations = Animations::default();
|
||||
if let Some(value) = animations_val {
|
||||
match value.parse(&mut AnimationsParser(self.0)) {
|
||||
Ok(v) => animations = v,
|
||||
Err(e) => {
|
||||
log::warn!("Could not parse animations setting: {}", self.0.error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut xwayland = None;
|
||||
if let Some(value) = xwayland_val {
|
||||
match value.parse(&mut XwaylandParser(self.0)) {
|
||||
|
|
@ -571,13 +560,6 @@ impl Parser for ConfigParser<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
let mut scratchpads = vec![];
|
||||
if let Some(value) = scratchpads_val {
|
||||
match value.parse(&mut ScratchpadsParser(self.0)) {
|
||||
Ok(v) => scratchpads = v,
|
||||
Err(e) => log::warn!("Could not parse the scratchpads: {}", self.0.error(e)),
|
||||
}
|
||||
}
|
||||
Ok(Config {
|
||||
keymap,
|
||||
repeat_rate,
|
||||
|
|
@ -603,13 +585,14 @@ impl Parser for ConfigParser<'_> {
|
|||
inputs,
|
||||
idle,
|
||||
grace_period,
|
||||
key_press_enables_dpms,
|
||||
mouse_move_enables_dpms,
|
||||
focus_follows_mouse: focus_follows_mouse.despan().unwrap_or(true),
|
||||
window_management_key,
|
||||
vrr,
|
||||
tearing,
|
||||
libei,
|
||||
ui_drag,
|
||||
animations,
|
||||
xwayland,
|
||||
color_management,
|
||||
float,
|
||||
|
|
@ -628,8 +611,6 @@ impl Parser for ConfigParser<'_> {
|
|||
simple_im,
|
||||
fallback_output_mode,
|
||||
mouse_follows_focus: mouse_follows_focus.despan(),
|
||||
scratchpads,
|
||||
autotile: autotile.despan(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::{
|
||||
config::{
|
||||
context::Context,
|
||||
extractor::{Extractor, ExtractorError, n64, opt, val},
|
||||
extractor::{Extractor, ExtractorError, bol, n64, opt, recover, val},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
},
|
||||
toml::{
|
||||
|
|
@ -28,6 +28,8 @@ pub struct IdleParser<'a>(pub &'a Context<'a>);
|
|||
pub struct Idle {
|
||||
pub timeout: Option<Duration>,
|
||||
pub grace_period: Option<Duration>,
|
||||
pub key_press_enables_dpms: Option<bool>,
|
||||
pub mouse_move_enables_dpms: Option<bool>,
|
||||
}
|
||||
|
||||
impl Parser for IdleParser<'_> {
|
||||
|
|
@ -41,10 +43,18 @@ impl Parser for IdleParser<'_> {
|
|||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
let mut ext = Extractor::new(self.0, span, table);
|
||||
let (minutes, seconds, grace_period_val) = ext.extract((
|
||||
let (
|
||||
minutes,
|
||||
seconds,
|
||||
grace_period_val,
|
||||
key_press_enables_dpms,
|
||||
mouse_move_enables_dpms,
|
||||
) = ext.extract((
|
||||
opt(n64("minutes")),
|
||||
opt(n64("seconds")),
|
||||
opt(val("grace-period")),
|
||||
recover(opt(bol("key-press-enables-dpms"))),
|
||||
recover(opt(bol("mouse-move-enables-dpms"))),
|
||||
))?;
|
||||
let mut timeout = None;
|
||||
if minutes.is_some() || seconds.is_some() {
|
||||
|
|
@ -57,6 +67,8 @@ impl Parser for IdleParser<'_> {
|
|||
Ok(Idle {
|
||||
timeout,
|
||||
grace_period,
|
||||
key_press_enables_dpms: key_press_enables_dpms.despan(),
|
||||
mouse_move_enables_dpms: mouse_move_enables_dpms.despan(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
config::{
|
||||
Scratchpad,
|
||||
context::Context,
|
||||
extractor::{Extractor, ExtractorError, opt, str, val},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
parsers::exec::{ExecParser, ExecParserError},
|
||||
},
|
||||
toml::{
|
||||
toml_span::{Span, Spanned},
|
||||
toml_value::Value,
|
||||
},
|
||||
},
|
||||
indexmap::IndexMap,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ScratchpadParserError {
|
||||
#[error(transparent)]
|
||||
Expected(#[from] UnexpectedDataType),
|
||||
#[error(transparent)]
|
||||
Extract(#[from] ExtractorError),
|
||||
#[error(transparent)]
|
||||
Exec(#[from] ExecParserError),
|
||||
}
|
||||
|
||||
pub struct ScratchpadParser<'a>(pub &'a Context<'a>);
|
||||
|
||||
impl Parser for ScratchpadParser<'_> {
|
||||
type Value = Scratchpad;
|
||||
type Error = ScratchpadParserError;
|
||||
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||
|
||||
fn parse_table(
|
||||
&mut self,
|
||||
span: Span,
|
||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
let mut ext = Extractor::new(self.0, span, table);
|
||||
let (name, exec_val) = ext.extract((str("name"), opt(val("exec"))))?;
|
||||
let exec = match exec_val {
|
||||
None => None,
|
||||
Some(e) => Some(e.parse_map(&mut ExecParser(self.0))?),
|
||||
};
|
||||
Ok(Scratchpad {
|
||||
name: name.value.to_string(),
|
||||
exec,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScratchpadsParser<'a>(pub &'a Context<'a>);
|
||||
|
||||
impl Parser for ScratchpadsParser<'_> {
|
||||
type Value = Vec<Scratchpad>;
|
||||
type Error = ScratchpadParserError;
|
||||
const EXPECTED: &'static [DataType] = &[DataType::Table, DataType::Array];
|
||||
|
||||
fn parse_array(&mut self, _span: Span, array: &[Spanned<Value>]) -> ParseResult<Self> {
|
||||
let mut res = vec![];
|
||||
for el in array {
|
||||
match el.parse(&mut ScratchpadParser(self.0)) {
|
||||
Ok(o) => res.push(o),
|
||||
Err(e) => {
|
||||
log::warn!("Could not parse scratchpad: {}", self.0.error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn parse_table(
|
||||
&mut self,
|
||||
span: Span,
|
||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
log::warn!(
|
||||
"`scratchpads` value should be an array: {}",
|
||||
self.0.error3(span)
|
||||
);
|
||||
ScratchpadParser(self.0)
|
||||
.parse_table(span, table)
|
||||
.map(|v| vec![v])
|
||||
}
|
||||
}
|
||||
|
|
@ -13,9 +13,9 @@ mod toml;
|
|||
use {
|
||||
crate::{
|
||||
config::{
|
||||
Action, AnimationCurveConfig, ClientRule, Config, ConfigConnector, ConfigDrmDevice,
|
||||
ConfigKeymap, ConnectorMatch, DrmDeviceMatch, Exec, Input, InputMatch, Output,
|
||||
OutputMatch, SimpleCommand, Status, Theme, WindowMatch, WindowRule, parse_config,
|
||||
Action, ClientRule, Config, ConfigConnector, ConfigDrmDevice, ConfigKeymap,
|
||||
ConnectorMatch, DrmDeviceMatch, Exec, Input, InputMatch, Output, OutputMatch,
|
||||
SimpleCommand, Status, Theme, WindowRule, parse_config,
|
||||
},
|
||||
rules::{MatcherTemp, RuleMapper},
|
||||
shortcuts::ModeState,
|
||||
|
|
@ -23,11 +23,11 @@ use {
|
|||
ahash::{AHashMap, AHashSet},
|
||||
error_reporter::Report,
|
||||
jay_config::{
|
||||
AnimationCurve, AnimationStyle, Axis,
|
||||
Axis,
|
||||
client::Client,
|
||||
config, config_dir,
|
||||
exec::{Command, set_env, unset_env},
|
||||
get_autotile, get_workspace,
|
||||
get_workspace,
|
||||
input::{
|
||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, capability::CAP_SWITCH,
|
||||
get_seat, input_devices, on_input_device_removed, on_new_input_device,
|
||||
|
|
@ -37,13 +37,12 @@ use {
|
|||
is_reload,
|
||||
keyboard::Keymap,
|
||||
logging::{clean_logs_older_than, set_log_level},
|
||||
on_devices_enumerated, on_idle, on_unload, quit, reload, set_animation_cubic_bezier,
|
||||
set_animation_curve, set_animation_duration_ms, set_animation_style,
|
||||
set_animations_enabled, set_autotile, set_color_management_enabled, set_corner_radius,
|
||||
set_default_workspace_capture, set_explicit_sync_enabled, set_float_above_fullscreen,
|
||||
set_floating_titles, set_idle, set_idle_grace_period, set_middle_click_paste_enabled,
|
||||
set_show_bar, set_show_float_pin_icon, set_show_titles, set_tab_title_align,
|
||||
set_ui_drag_enabled, set_ui_drag_threshold,
|
||||
on_devices_enumerated, on_idle, on_unload, quit, reload, set_autotile,
|
||||
set_color_management_enabled, set_corner_radius, set_default_workspace_capture,
|
||||
set_explicit_sync_enabled, set_float_above_fullscreen, set_floating_titles, set_idle,
|
||||
set_idle_grace_period, set_key_press_enables_dpms, set_middle_click_paste_enabled,
|
||||
set_mouse_move_enables_dpms, set_show_bar, set_show_float_pin_icon, set_show_titles,
|
||||
set_tab_title_align, set_ui_drag_enabled, set_ui_drag_threshold,
|
||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||
switch_to_vt,
|
||||
tasks::{self, JoinHandle},
|
||||
|
|
@ -173,9 +172,6 @@ impl Action {
|
|||
SimpleCommand::Move(dir) => window_or_seat!(s, s.move_(dir)),
|
||||
SimpleCommand::ToggleFullscreen => window_or_seat!(s, s.toggle_fullscreen()),
|
||||
SimpleCommand::SetFullscreen(b) => window_or_seat!(s, s.set_fullscreen(b)),
|
||||
SimpleCommand::SendToScratchpad => window_or_seat!(s, s.send_to_scratchpad("")),
|
||||
SimpleCommand::ToggleScratchpad => b.new(move || s.toggle_scratchpad("")),
|
||||
SimpleCommand::CycleScratchpad => b.new(move || s.cycle_scratchpad("")),
|
||||
SimpleCommand::FocusParent => b.new(move || s.focus_parent()),
|
||||
SimpleCommand::Close => window_or_seat!(s, s.close()),
|
||||
SimpleCommand::DisablePointerConstraint => {
|
||||
|
|
@ -272,7 +268,12 @@ impl Action {
|
|||
SimpleCommand::MoveTabLeft => b.new(move || s.move_tab(false)),
|
||||
SimpleCommand::MoveTabRight => b.new(move || s.move_tab(true)),
|
||||
SimpleCommand::SetAutotile(enabled) => b.new(move || set_autotile(enabled)),
|
||||
SimpleCommand::ToggleAutotile => b.new(move || set_autotile(!get_autotile())),
|
||||
SimpleCommand::ToggleAutotile => {
|
||||
b.new(move || {
|
||||
// Toggle not directly supported; set to true
|
||||
set_autotile(true)
|
||||
})
|
||||
}
|
||||
},
|
||||
Action::Multi { actions } => {
|
||||
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
||||
|
|
@ -309,9 +310,6 @@ impl Action {
|
|||
let workspace = get_workspace(&name);
|
||||
window_or_seat!(s, s.set_workspace(workspace))
|
||||
}
|
||||
Action::SendToScratchpad { name } => window_or_seat!(s, s.send_to_scratchpad(&name)),
|
||||
Action::ToggleScratchpad { name } => b.new(move || s.toggle_scratchpad(&name)),
|
||||
Action::CycleScratchpad { name } => b.new(move || s.cycle_scratchpad(&name)),
|
||||
Action::ConfigureConnector { con } => b.new(move || {
|
||||
for c in connectors() {
|
||||
if con.match_.matches(c) {
|
||||
|
|
@ -1463,43 +1461,6 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
|
|||
window: Default::default(),
|
||||
});
|
||||
state.clear_modes_after_reload();
|
||||
// Desugar `[[scratchpads]]` into spawn-on-graphics-init plus an internal
|
||||
// window rule that parks the spawned window. Each spawned process gets a
|
||||
// unique tag so only its own windows are captured, never other windows of
|
||||
// the same application.
|
||||
if !config.scratchpads.is_empty() {
|
||||
let mut spawn_actions = vec![];
|
||||
for (i, sp) in config.scratchpads.drain(..).enumerate() {
|
||||
let Some(mut exec) = sp.exec else {
|
||||
continue;
|
||||
};
|
||||
let tag = exec
|
||||
.tag
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("__scratchpad.{i}.{}", sp.name));
|
||||
exec.tag = Some(tag.clone());
|
||||
spawn_actions.push(Action::Exec { exec });
|
||||
config.window_rules.push(WindowRule {
|
||||
name: None,
|
||||
match_: WindowMatch {
|
||||
tag: Some(tag),
|
||||
..Default::default()
|
||||
},
|
||||
action: Some(Action::SendToScratchpad { name: sp.name }),
|
||||
latch: None,
|
||||
auto_focus: None,
|
||||
initial_tile_state: None,
|
||||
});
|
||||
}
|
||||
if !spawn_actions.is_empty() {
|
||||
let mut actions = Vec::with_capacity(spawn_actions.len() + 1);
|
||||
if let Some(existing) = config.on_graphics_initialized.take() {
|
||||
actions.push(existing);
|
||||
}
|
||||
actions.extend(spawn_actions);
|
||||
config.on_graphics_initialized = Some(Action::Multi { actions });
|
||||
}
|
||||
}
|
||||
let (client_rules, client_rule_mapper) = state.create_rules(&config.client_rules);
|
||||
persistent.client_rules.set(client_rules);
|
||||
*state.persistent.client_rule_mapper.borrow_mut() = Some(client_rule_mapper);
|
||||
|
|
@ -1688,38 +1649,6 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
|
|||
if let Some(threshold) = config.ui_drag.threshold {
|
||||
set_ui_drag_threshold(threshold);
|
||||
}
|
||||
set_animations_enabled(config.animations.enabled.unwrap_or(false));
|
||||
set_animation_duration_ms(config.animations.duration_ms.unwrap_or(160));
|
||||
match config.animations.style.as_deref().unwrap_or("multiphase") {
|
||||
"plain" => set_animation_style(AnimationStyle::PLAIN),
|
||||
"multiphase" => set_animation_style(AnimationStyle::MULTIPHASE),
|
||||
style_name => log::warn!("Unknown animation style: {style_name}"),
|
||||
}
|
||||
match config
|
||||
.animations
|
||||
.curve
|
||||
.unwrap_or_else(|| AnimationCurveConfig::Preset("ease-out".to_string()))
|
||||
{
|
||||
AnimationCurveConfig::Preset(curve_name) => {
|
||||
let curve = match curve_name.as_str() {
|
||||
"linear" => Some(AnimationCurve::LINEAR),
|
||||
"ease" => Some(AnimationCurve::EASE),
|
||||
"ease-in" => Some(AnimationCurve::EASE_IN),
|
||||
"ease-out" => Some(AnimationCurve::EASE_OUT),
|
||||
"ease-in-out" => Some(AnimationCurve::EASE_IN_OUT),
|
||||
_ => {
|
||||
log::warn!("Unknown animation curve: {curve_name}");
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(curve) = curve {
|
||||
set_animation_curve(curve);
|
||||
}
|
||||
}
|
||||
AnimationCurveConfig::CubicBezier([x1, y1, x2, y2]) => {
|
||||
set_animation_cubic_bezier(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
if let Some(xwayland) = config.xwayland {
|
||||
if let Some(enabled) = xwayland.enabled {
|
||||
set_x_wayland_enabled(enabled);
|
||||
|
|
@ -1728,6 +1657,8 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
|
|||
set_x_scaling_mode(mode);
|
||||
}
|
||||
}
|
||||
set_key_press_enables_dpms(config.key_press_enables_dpms.unwrap_or(false));
|
||||
set_mouse_move_enables_dpms(config.mouse_move_enables_dpms.unwrap_or(false));
|
||||
if let Some(cm) = config.color_management
|
||||
&& let Some(enabled) = cm.enabled
|
||||
{
|
||||
|
|
@ -1784,9 +1715,6 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
|
|||
.seat
|
||||
.unstable_set_mouse_follows_focus(mouse_follows_focus);
|
||||
}
|
||||
if let Some(v) = config.autotile {
|
||||
set_autotile(v);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_command(exec: &Exec) -> Command {
|
||||
|
|
|
|||
|
|
@ -162,54 +162,6 @@
|
|||
"name"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Sends the currently focused window to a scratchpad and hides it.\n\nA scratchpad can hold any number of windows. If `name` is omitted, the\ndefault scratchpad is used.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-shift-minus = { type = \"send-to-scratchpad\", name = \"terminal\" }\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "send-to-scratchpad"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the scratchpad."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Toggles a scratchpad.\n\nIf the scratchpad has a visible window, that window is hidden. Otherwise, the\nmost recently hidden window in the scratchpad is shown on the current workspace.\nOnly one window of a scratchpad is shown at a time, and scratchpad windows are\nalways shown floating. If `name` is omitted, the default scratchpad is used.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-minus = { type = \"toggle-scratchpad\", name = \"terminal\" }\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "toggle-scratchpad"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the scratchpad."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Cycles through the windows of a scratchpad, one at a time.\n\nWith no window shown, the first window is brought up. Each further invocation\nhides the current window and shows the next; after the last window the\nscratchpad is hidden again. Scratchpad windows are always shown floating.\nIf `name` is omitted, the default scratchpad is used.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-minus = { type = \"cycle-scratchpad\", name = \"terminal\" }\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "cycle-scratchpad"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the scratchpad."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Moves a workspace to a different output.\n\n- Example 1: Move a specific workspace to a named output\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"move-to-output\", workspace = \"1\", output.name = \"right\" }\n ```\n\n- Example 2: Move the current workspace to a named output\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"move-to-output\", output.name = \"right\" }\n ```\n\n- Example 3: Move the current workspace to the output on the right (directional)\n\n ```toml\n [shortcuts]\n \"logo+ctrl+shift+Right\" = { type = \"move-to-output\", direction = \"right\" }\n \"logo+ctrl+shift+Left\" = { type = \"move-to-output\", direction = \"left\" }\n \"logo+ctrl+shift+Up\" = { type = \"move-to-output\", direction = \"up\" }\n \"logo+ctrl+shift+Down\" = { type = \"move-to-output\", direction = \"down\" }\n ```\n",
|
||||
"type": "object",
|
||||
|
|
@ -689,61 +641,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"AnimationCurve": {
|
||||
"description": "Describes a window animation curve.\n",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "One of the supported curve presets.\n",
|
||||
"enum": [
|
||||
"linear",
|
||||
"ease",
|
||||
"ease-in",
|
||||
"ease-out",
|
||||
"ease-in-out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"description": "A custom CSS-style cubic-bezier curve as four numbers:\n`x1`, `y1`, `x2`, and `y2`.\n\nThe implicit endpoints are `(0, 0)` and `(1, 1)`. `x1` and `x2` must\nbe between `0` and `1`.\n",
|
||||
"items": {
|
||||
"type": "number",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"AnimationStyle": {
|
||||
"type": "string",
|
||||
"description": "Describes a tiled window movement animation style.\n",
|
||||
"enum": [
|
||||
"plain",
|
||||
"multiphase"
|
||||
]
|
||||
},
|
||||
"Animations": {
|
||||
"description": "Describes window animation settings.\n\n- Example:\n\n ```toml\n [animations]\n enabled = true\n duration-ms = 160\n style = \"multiphase\"\n curve = [0.25, 0.1, 0.25, 1.0]\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enables or disables window animations.\n\nThe default is `false`.\n"
|
||||
},
|
||||
"duration-ms": {
|
||||
"type": "integer",
|
||||
"description": "Sets the animation duration in milliseconds.\n\nThe default is `160`.\n"
|
||||
},
|
||||
"style": {
|
||||
"description": "Sets the animation style used for tiled window movement animations.\n\nThe default is `multiphase`.\n",
|
||||
"$ref": "#/$defs/AnimationStyle"
|
||||
},
|
||||
"curve": {
|
||||
"description": "Sets the animation curve.\n\nThe default is `ease-out`.\n",
|
||||
"$ref": "#/$defs/AnimationCurve"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"BarPosition": {
|
||||
"type": "string",
|
||||
"description": "The position of the bar.",
|
||||
|
|
@ -1188,10 +1085,6 @@
|
|||
"description": "Configures the ui-drag settings.\n\n- Example:\n\n ```toml\n ui-drag = { enabled = false, threshold = 20 }\n ```\n",
|
||||
"$ref": "#/$defs/UiDrag"
|
||||
},
|
||||
"animations": {
|
||||
"description": "Configures window animations.\n\nAnimations are disabled by default.\n\n- Example:\n\n ```toml\n [animations]\n enabled = true\n duration-ms = 160\n style = \"multiphase\"\n curve = \"ease-out\"\n ```\n",
|
||||
"$ref": "#/$defs/Animations"
|
||||
},
|
||||
"xwayland": {
|
||||
"description": "Configures the Xwayland settings.\n\n- Example:\n\n ```toml\n xwayland = { scaling-mode = \"downscaled\" }\n ```\n",
|
||||
"$ref": "#/$defs/Xwayland"
|
||||
|
|
@ -1257,10 +1150,6 @@
|
|||
"type": "boolean",
|
||||
"description": "Configures whether middle-click pasting is enabled.\n\nChanging this has no effect on running applications.\n\nThe default is `true`.\n"
|
||||
},
|
||||
"autotile": {
|
||||
"type": "boolean",
|
||||
"description": "Configures whether autotiling is enabled by default.\n\nWhen enabled, newly mapped tiled windows alternate their split\norientation automatically. This can also be toggled at runtime via the\n`enable-autotile`, `disable-autotile`, and `toggle-autotile` actions.\n\nThe default is `false`.\n"
|
||||
},
|
||||
"modes": {
|
||||
"description": "Configures the input modes.\n\nModes can be used to define shortcuts that are only active when the mode is\nactive.\n\n- Example\n\n ```toml\n [modes.\"navigation\".shortcuts]\n w = \"focus-up\"\n a = \"focus-left\"\n s = \"focus-down\"\n d = \"focus-right\"\n r = \"focus-above\"\n f = \"focus-below\"\n q = \"focus-prev\"\n e = \"focus-next\"\n ```\n\nModes can be activated with the `push-mode` and `latch-mode` actions.\n",
|
||||
"type": "object",
|
||||
|
|
@ -1288,14 +1177,6 @@
|
|||
"egui": {
|
||||
"description": "Sets the egui settings of the compositor.\n",
|
||||
"$ref": "#/$defs/Egui"
|
||||
},
|
||||
"scratchpads": {
|
||||
"type": "array",
|
||||
"description": "An array of pre-configured scratchpads.\n\nEach entry launches a program when the graphics are first initialized and\nimmediately parks its window in the named scratchpad. The window is captured\nvia a unique tag attached to the spawned process, so other windows of the\nsame application are never affected.\n\nUse a `toggle-scratchpad` or `cycle-scratchpad` action to bring the windows\nup; they are always shown floating.\n\n- Example:\n\n ```toml\n [[scratchpads]]\n name = \"term\"\n exec = \"foot\"\n\n [[scratchpads]]\n name = \"notes\"\n exec = [\"obsidian\"]\n ```\n",
|
||||
"items": {
|
||||
"description": "",
|
||||
"$ref": "#/$defs/Scratchpad"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
|
@ -2110,23 +1991,6 @@
|
|||
},
|
||||
"required": []
|
||||
},
|
||||
"Scratchpad": {
|
||||
"description": "A pre-configured scratchpad whose program is launched at startup and parked\nin the scratchpad.\n\n- Example:\n\n ```toml\n [[scratchpads]]\n name = \"term\"\n exec = \"foot\"\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the scratchpad that the spawned window is parked in."
|
||||
},
|
||||
"exec": {
|
||||
"description": "The program to launch when the graphics are first initialized.\n\nIf omitted, no program is launched and the scratchpad is only created on\ndemand by `send-to-scratchpad`.\n",
|
||||
"$ref": "#/$defs/Exec"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"SimpleActionName": {
|
||||
"type": "string",
|
||||
"description": "The name of a `simple` Action.\n\nWhen used inside a window rule, the following actions apply to the matched window\ninstead fo the focused window:\n\n- `move-left`\n- `move-down`\n- `move-up`\n- `move-right`\n- `toggle-fullscreen`\n- `enter-fullscreen`\n- `exit-fullscreen`\n- `close`\n- `toggle-floating`\n- `float`\n- `tile`\n- `toggle-float-pinned`\n- `pin-float`\n- `unpin-float`\n\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = \"quit\"\n ```\n",
|
||||
|
|
@ -2145,15 +2009,9 @@
|
|||
"make-group-tab",
|
||||
"change-group-opposite",
|
||||
"toggle-tab",
|
||||
"enable-autotile",
|
||||
"disable-autotile",
|
||||
"toggle-autotile",
|
||||
"toggle-fullscreen",
|
||||
"enter-fullscreen",
|
||||
"exit-fullscreen",
|
||||
"send-to-scratchpad",
|
||||
"toggle-scratchpad",
|
||||
"cycle-scratchpad",
|
||||
"focus-parent",
|
||||
"close",
|
||||
"disable-pointer-constraint",
|
||||
|
|
|
|||
|
|
@ -286,76 +286,6 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
|||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `send-to-scratchpad`:
|
||||
|
||||
Sends the currently focused window to a scratchpad and hides it.
|
||||
|
||||
A scratchpad can hold any number of windows. If `name` is omitted, the
|
||||
default scratchpad is used.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-shift-minus = { type = "send-to-scratchpad", name = "terminal" }
|
||||
```
|
||||
|
||||
The table has the following fields:
|
||||
|
||||
- `name` (optional):
|
||||
|
||||
The name of the scratchpad.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `toggle-scratchpad`:
|
||||
|
||||
Toggles a scratchpad.
|
||||
|
||||
If the scratchpad has a visible window, that window is hidden. Otherwise, the
|
||||
most recently hidden window in the scratchpad is shown on the current workspace.
|
||||
Only one window of a scratchpad is shown at a time, and scratchpad windows are
|
||||
always shown floating. If `name` is omitted, the default scratchpad is used.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-minus = { type = "toggle-scratchpad", name = "terminal" }
|
||||
```
|
||||
|
||||
The table has the following fields:
|
||||
|
||||
- `name` (optional):
|
||||
|
||||
The name of the scratchpad.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `cycle-scratchpad`:
|
||||
|
||||
Cycles through the windows of a scratchpad, one at a time.
|
||||
|
||||
With no window shown, the first window is brought up. Each further invocation
|
||||
hides the current window and shows the next; after the last window the
|
||||
scratchpad is hidden again. Scratchpad windows are always shown floating.
|
||||
If `name` is omitted, the default scratchpad is used.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-minus = { type = "cycle-scratchpad", name = "terminal" }
|
||||
```
|
||||
|
||||
The table has the following fields:
|
||||
|
||||
- `name` (optional):
|
||||
|
||||
The name of the scratchpad.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `move-to-output`:
|
||||
|
||||
Moves a workspace to a different output.
|
||||
|
|
@ -1012,126 +942,6 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
|||
The numbers should be integers.
|
||||
|
||||
|
||||
<a name="types-AnimationCurve"></a>
|
||||
### `AnimationCurve`
|
||||
|
||||
Describes a window animation curve.
|
||||
|
||||
Values of this type should have one of the following forms:
|
||||
|
||||
#### A string
|
||||
|
||||
One of the supported curve presets.
|
||||
|
||||
The string should have one of the following values:
|
||||
|
||||
- `linear`:
|
||||
|
||||
No easing.
|
||||
|
||||
- `ease`:
|
||||
|
||||
The CSS `ease` curve.
|
||||
|
||||
- `ease-in`:
|
||||
|
||||
The CSS `ease-in` curve.
|
||||
|
||||
- `ease-out`:
|
||||
|
||||
The CSS `ease-out` curve.
|
||||
|
||||
- `ease-in-out`:
|
||||
|
||||
The CSS `ease-in-out` curve.
|
||||
|
||||
|
||||
#### An array
|
||||
|
||||
A custom CSS-style cubic-bezier curve as four numbers:
|
||||
`x1`, `y1`, `x2`, and `y2`.
|
||||
|
||||
The implicit endpoints are `(0, 0)` and `(1, 1)`. `x1` and `x2` must
|
||||
be between `0` and `1`.
|
||||
|
||||
Each element of this array should be a number.
|
||||
|
||||
|
||||
<a name="types-AnimationStyle"></a>
|
||||
### `AnimationStyle`
|
||||
|
||||
Describes a tiled window movement animation style.
|
||||
|
||||
Values of this type should be strings.
|
||||
|
||||
The string should have one of the following values:
|
||||
|
||||
- `plain`:
|
||||
|
||||
Uses a single interpolated movement from each window's current visual
|
||||
rectangle to its destination rectangle.
|
||||
|
||||
- `multiphase`:
|
||||
|
||||
Uses the no-overlap multiphase planner for tiled window movement when a
|
||||
supported plan exists.
|
||||
|
||||
|
||||
|
||||
<a name="types-Animations"></a>
|
||||
### `Animations`
|
||||
|
||||
Describes window animation settings.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[animations]
|
||||
enabled = true
|
||||
duration-ms = 160
|
||||
style = "multiphase"
|
||||
curve = [0.25, 0.1, 0.25, 1.0]
|
||||
```
|
||||
|
||||
Values of this type should be tables.
|
||||
|
||||
The table has the following fields:
|
||||
|
||||
- `enabled` (optional):
|
||||
|
||||
Enables or disables window animations.
|
||||
|
||||
The default is `false`.
|
||||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `duration-ms` (optional):
|
||||
|
||||
Sets the animation duration in milliseconds.
|
||||
|
||||
The default is `160`.
|
||||
|
||||
The value of this field should be a number.
|
||||
|
||||
The numbers should be integers.
|
||||
|
||||
- `style` (optional):
|
||||
|
||||
Sets the animation style used for tiled window movement animations.
|
||||
|
||||
The default is `multiphase`.
|
||||
|
||||
The value of this field should be a [AnimationStyle](#types-AnimationStyle).
|
||||
|
||||
- `curve` (optional):
|
||||
|
||||
Sets the animation curve.
|
||||
|
||||
The default is `ease-out`.
|
||||
|
||||
The value of this field should be a [AnimationCurve](#types-AnimationCurve).
|
||||
|
||||
|
||||
<a name="types-BarPosition"></a>
|
||||
### `BarPosition`
|
||||
|
||||
|
|
@ -2359,24 +2169,6 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a [UiDrag](#types-UiDrag).
|
||||
|
||||
- `animations` (optional):
|
||||
|
||||
Configures window animations.
|
||||
|
||||
Animations are disabled by default.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[animations]
|
||||
enabled = true
|
||||
duration-ms = 160
|
||||
style = "multiphase"
|
||||
curve = "ease-out"
|
||||
```
|
||||
|
||||
The value of this field should be a [Animations](#types-Animations).
|
||||
|
||||
- `xwayland` (optional):
|
||||
|
||||
Configures the Xwayland settings.
|
||||
|
|
@ -2560,18 +2352,6 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `autotile` (optional):
|
||||
|
||||
Configures whether autotiling is enabled by default.
|
||||
|
||||
When enabled, newly mapped tiled windows alternate their split
|
||||
orientation automatically. This can also be toggled at runtime via the
|
||||
`enable-autotile`, `disable-autotile`, and `toggle-autotile` actions.
|
||||
|
||||
The default is `false`.
|
||||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `modes` (optional):
|
||||
|
||||
Configures the input modes.
|
||||
|
|
@ -2672,32 +2452,6 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a [Egui](#types-Egui).
|
||||
|
||||
- `scratchpads` (optional):
|
||||
|
||||
An array of pre-configured scratchpads.
|
||||
|
||||
Each entry launches a program when the graphics are first initialized and
|
||||
immediately parks its window in the named scratchpad. The window is captured
|
||||
via a unique tag attached to the spawned process, so other windows of the
|
||||
same application are never affected.
|
||||
|
||||
Use a `toggle-scratchpad` or `cycle-scratchpad` action to bring the windows
|
||||
up; they are always shown floating.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[[scratchpads]]
|
||||
name = "term"
|
||||
exec = "foot"
|
||||
|
||||
[[scratchpads]]
|
||||
name = "notes"
|
||||
exec = ["obsidian"]
|
||||
```
|
||||
|
||||
The value of this field should be an array of [Scratchpads](#types-Scratchpad).
|
||||
|
||||
|
||||
<a name="types-Connector"></a>
|
||||
### `Connector`
|
||||
|
|
@ -4631,40 +4385,6 @@ The table has the following fields:
|
|||
The value of this field should be a string.
|
||||
|
||||
|
||||
<a name="types-Scratchpad"></a>
|
||||
### `Scratchpad`
|
||||
|
||||
A pre-configured scratchpad whose program is launched at startup and parked
|
||||
in the scratchpad.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[[scratchpads]]
|
||||
name = "term"
|
||||
exec = "foot"
|
||||
```
|
||||
|
||||
Values of this type should be tables.
|
||||
|
||||
The table has the following fields:
|
||||
|
||||
- `name` (required):
|
||||
|
||||
The name of the scratchpad that the spawned window is parked in.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `exec` (optional):
|
||||
|
||||
The program to launch when the graphics are first initialized.
|
||||
|
||||
If omitted, no program is launched and the scratchpad is only created on
|
||||
demand by `send-to-scratchpad`.
|
||||
|
||||
The value of this field should be a [Exec](#types-Exec).
|
||||
|
||||
|
||||
<a name="types-SimpleActionName"></a>
|
||||
### `SimpleActionName`
|
||||
|
||||
|
|
@ -4756,18 +4476,6 @@ The string should have one of the following values:
|
|||
|
||||
Toggles the current group between tabbed and split mode.
|
||||
|
||||
- `enable-autotile`:
|
||||
|
||||
Enables alternating split orientation for newly tiled windows.
|
||||
|
||||
- `disable-autotile`:
|
||||
|
||||
Disables alternating split orientation for newly tiled windows.
|
||||
|
||||
- `toggle-autotile`:
|
||||
|
||||
Toggles alternating split orientation for newly tiled windows.
|
||||
|
||||
- `toggle-fullscreen`:
|
||||
|
||||
Toggle the currently focused window between fullscreen and windowed.
|
||||
|
|
@ -4780,18 +4488,6 @@ The string should have one of the following values:
|
|||
|
||||
Makes the currently focused window windowed.
|
||||
|
||||
- `send-to-scratchpad`:
|
||||
|
||||
Sends the currently focused window to the default scratchpad.
|
||||
|
||||
- `toggle-scratchpad`:
|
||||
|
||||
Toggles the default scratchpad.
|
||||
|
||||
- `cycle-scratchpad`:
|
||||
|
||||
Cycles through the windows of the default scratchpad.
|
||||
|
||||
- `focus-parent`:
|
||||
|
||||
Focus the parent of the currently focused window.
|
||||
|
|
|
|||
|
|
@ -345,64 +345,6 @@ Action:
|
|||
description: The name of the workspace.
|
||||
required: true
|
||||
kind: string
|
||||
send-to-scratchpad:
|
||||
description: |
|
||||
Sends the currently focused window to a scratchpad and hides it.
|
||||
|
||||
A scratchpad can hold any number of windows. If `name` is omitted, the
|
||||
default scratchpad is used.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-shift-minus = { type = "send-to-scratchpad", name = "terminal" }
|
||||
```
|
||||
fields:
|
||||
name:
|
||||
description: The name of the scratchpad.
|
||||
required: false
|
||||
kind: string
|
||||
toggle-scratchpad:
|
||||
description: |
|
||||
Toggles a scratchpad.
|
||||
|
||||
If the scratchpad has a visible window, that window is hidden. Otherwise, the
|
||||
most recently hidden window in the scratchpad is shown on the current workspace.
|
||||
Only one window of a scratchpad is shown at a time, and scratchpad windows are
|
||||
always shown floating. If `name` is omitted, the default scratchpad is used.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-minus = { type = "toggle-scratchpad", name = "terminal" }
|
||||
```
|
||||
fields:
|
||||
name:
|
||||
description: The name of the scratchpad.
|
||||
required: false
|
||||
kind: string
|
||||
cycle-scratchpad:
|
||||
description: |
|
||||
Cycles through the windows of a scratchpad, one at a time.
|
||||
|
||||
With no window shown, the first window is brought up. Each further invocation
|
||||
hides the current window and shows the next; after the last window the
|
||||
scratchpad is hidden again. Scratchpad windows are always shown floating.
|
||||
If `name` is omitted, the default scratchpad is used.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-minus = { type = "cycle-scratchpad", name = "terminal" }
|
||||
```
|
||||
fields:
|
||||
name:
|
||||
description: The name of the scratchpad.
|
||||
required: false
|
||||
kind: string
|
||||
move-to-output:
|
||||
description: |
|
||||
Moves a workspace to a different output.
|
||||
|
|
@ -1122,24 +1064,12 @@ SimpleActionName:
|
|||
description: Toggles the current group's direction.
|
||||
- value: toggle-tab
|
||||
description: Toggles the current group between tabbed and split mode.
|
||||
- value: enable-autotile
|
||||
description: Enables alternating split orientation for newly tiled windows.
|
||||
- value: disable-autotile
|
||||
description: Disables alternating split orientation for newly tiled windows.
|
||||
- value: toggle-autotile
|
||||
description: Toggles alternating split orientation for newly tiled windows.
|
||||
- value: toggle-fullscreen
|
||||
description: Toggle the currently focused window between fullscreen and windowed.
|
||||
- value: enter-fullscreen
|
||||
description: Makes the currently focused window fullscreen.
|
||||
- value: exit-fullscreen
|
||||
description: Makes the currently focused window windowed.
|
||||
- value: send-to-scratchpad
|
||||
description: Sends the currently focused window to the default scratchpad.
|
||||
- value: toggle-scratchpad
|
||||
description: Toggles the default scratchpad.
|
||||
- value: cycle-scratchpad
|
||||
description: Cycles through the windows of the default scratchpad.
|
||||
- value: focus-parent
|
||||
description: Focus the parent of the currently focused window.
|
||||
- value: close
|
||||
|
|
@ -3012,23 +2942,6 @@ Config:
|
|||
```toml
|
||||
ui-drag = { enabled = false, threshold = 20 }
|
||||
```
|
||||
animations:
|
||||
ref: Animations
|
||||
required: false
|
||||
description: |
|
||||
Configures window animations.
|
||||
|
||||
Animations are disabled by default.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[animations]
|
||||
enabled = true
|
||||
duration-ms = 160
|
||||
style = "multiphase"
|
||||
curve = "ease-out"
|
||||
```
|
||||
xwayland:
|
||||
ref: Xwayland
|
||||
required: false
|
||||
|
|
@ -3203,17 +3116,6 @@ Config:
|
|||
Changing this has no effect on running applications.
|
||||
|
||||
The default is `true`.
|
||||
autotile:
|
||||
kind: boolean
|
||||
required: false
|
||||
description: |
|
||||
Configures whether autotiling is enabled by default.
|
||||
|
||||
When enabled, newly mapped tiled windows alternate their split
|
||||
orientation automatically. This can also be toggled at runtime via the
|
||||
`enable-autotile`, `disable-autotile`, and `toggle-autotile` actions.
|
||||
|
||||
The default is `false`.
|
||||
modes:
|
||||
kind: map
|
||||
values:
|
||||
|
|
@ -3310,61 +3212,6 @@ Config:
|
|||
required: false
|
||||
description: |
|
||||
Sets the egui settings of the compositor.
|
||||
scratchpads:
|
||||
kind: array
|
||||
items:
|
||||
ref: Scratchpad
|
||||
required: false
|
||||
description: |
|
||||
An array of pre-configured scratchpads.
|
||||
|
||||
Each entry launches a program when the graphics are first initialized and
|
||||
immediately parks its window in the named scratchpad. The window is captured
|
||||
via a unique tag attached to the spawned process, so other windows of the
|
||||
same application are never affected.
|
||||
|
||||
Use a `toggle-scratchpad` or `cycle-scratchpad` action to bring the windows
|
||||
up; they are always shown floating.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[[scratchpads]]
|
||||
name = "term"
|
||||
exec = "foot"
|
||||
|
||||
[[scratchpads]]
|
||||
name = "notes"
|
||||
exec = ["obsidian"]
|
||||
```
|
||||
|
||||
|
||||
Scratchpad:
|
||||
kind: table
|
||||
description: |
|
||||
A pre-configured scratchpad whose program is launched at startup and parked
|
||||
in the scratchpad.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[[scratchpads]]
|
||||
name = "term"
|
||||
exec = "foot"
|
||||
```
|
||||
fields:
|
||||
name:
|
||||
kind: string
|
||||
required: true
|
||||
description: The name of the scratchpad that the spawned window is parked in.
|
||||
exec:
|
||||
ref: Exec
|
||||
required: false
|
||||
description: |
|
||||
The program to launch when the graphics are first initialized.
|
||||
|
||||
If omitted, no program is launched and the scratchpad is only created on
|
||||
demand by `send-to-scratchpad`.
|
||||
|
||||
|
||||
Idle:
|
||||
|
|
@ -3808,97 +3655,6 @@ UiDrag:
|
|||
The default is `10`.
|
||||
|
||||
|
||||
Animations:
|
||||
kind: table
|
||||
description: |
|
||||
Describes window animation settings.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[animations]
|
||||
enabled = true
|
||||
duration-ms = 160
|
||||
style = "multiphase"
|
||||
curve = [0.25, 0.1, 0.25, 1.0]
|
||||
```
|
||||
fields:
|
||||
enabled:
|
||||
kind: boolean
|
||||
required: false
|
||||
description: |
|
||||
Enables or disables window animations.
|
||||
|
||||
The default is `false`.
|
||||
duration-ms:
|
||||
kind: number
|
||||
integer_only: true
|
||||
required: false
|
||||
description: |
|
||||
Sets the animation duration in milliseconds.
|
||||
|
||||
The default is `160`.
|
||||
style:
|
||||
ref: AnimationStyle
|
||||
required: false
|
||||
description: |
|
||||
Sets the animation style used for tiled window movement animations.
|
||||
|
||||
The default is `multiphase`.
|
||||
curve:
|
||||
ref: AnimationCurve
|
||||
required: false
|
||||
description: |
|
||||
Sets the animation curve.
|
||||
|
||||
The default is `ease-out`.
|
||||
|
||||
|
||||
AnimationStyle:
|
||||
kind: string
|
||||
description: |
|
||||
Describes a tiled window movement animation style.
|
||||
values:
|
||||
- value: plain
|
||||
description: |
|
||||
Uses a single interpolated movement from each window's current visual
|
||||
rectangle to its destination rectangle.
|
||||
- value: multiphase
|
||||
description: |
|
||||
Uses the no-overlap multiphase planner for tiled window movement when a
|
||||
supported plan exists.
|
||||
|
||||
|
||||
AnimationCurve:
|
||||
kind: variable
|
||||
description: |
|
||||
Describes a window animation curve.
|
||||
variants:
|
||||
- kind: string
|
||||
description: |
|
||||
One of the supported curve presets.
|
||||
values:
|
||||
- value: linear
|
||||
description: No easing.
|
||||
- value: ease
|
||||
description: The CSS `ease` curve.
|
||||
- value: ease-in
|
||||
description: The CSS `ease-in` curve.
|
||||
- value: ease-out
|
||||
description: The CSS `ease-out` curve.
|
||||
- value: ease-in-out
|
||||
description: The CSS `ease-in-out` curve.
|
||||
- kind: array
|
||||
items:
|
||||
kind: number
|
||||
description: |
|
||||
A custom CSS-style cubic-bezier curve as four numbers:
|
||||
`x1`, `y1`, `x2`, and `y2`.
|
||||
|
||||
The implicit endpoints are `(0, 0)` and `(1, 1)`. `x1` and `x2` must
|
||||
be between `0` and `1`.
|
||||
|
||||
|
||||
Xwayland:
|
||||
kind: table
|
||||
description: |
|
||||
|
|
|
|||
|
|
@ -135,6 +135,10 @@ request get_pid (since = 27) {
|
|||
|
||||
}
|
||||
|
||||
request set_dpms (since = 31) {
|
||||
active: u32,
|
||||
}
|
||||
|
||||
# events
|
||||
|
||||
event client_id {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue