idle: add a grace period
This commit is contained in:
parent
1ad3d11616
commit
e8be15a26c
29 changed files with 405 additions and 79 deletions
|
|
@ -893,6 +893,10 @@ impl Client {
|
||||||
self.send(&ClientMessage::SetIdle { timeout })
|
self.send(&ClientMessage::SetIdle { timeout })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_idle_grace_period(&self, period: Duration) {
|
||||||
|
self.send(&ClientMessage::SetIdleGracePeriod { period })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_explicit_sync_enabled(&self, enabled: bool) {
|
pub fn set_explicit_sync_enabled(&self, enabled: bool) {
|
||||||
self.send(&ClientMessage::SetExplicitSyncEnabled { enabled })
|
self.send(&ClientMessage::SetExplicitSyncEnabled { enabled })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -527,6 +527,9 @@ pub enum ClientMessage<'a> {
|
||||||
SetXScalingMode {
|
SetXScalingMode {
|
||||||
mode: XScalingMode,
|
mode: XScalingMode,
|
||||||
},
|
},
|
||||||
|
SetIdleGracePeriod {
|
||||||
|
period: Duration,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -224,10 +224,24 @@ pub fn workspaces() -> Vec<Workspace> {
|
||||||
/// Configures the idle timeout.
|
/// Configures the idle timeout.
|
||||||
///
|
///
|
||||||
/// `None` disables the timeout.
|
/// `None` disables the timeout.
|
||||||
|
///
|
||||||
|
/// The default is 10 minutes.
|
||||||
pub fn set_idle(timeout: Option<Duration>) {
|
pub fn set_idle(timeout: Option<Duration>) {
|
||||||
get!().set_idle(timeout.unwrap_or_default())
|
get!().set_idle(timeout.unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configures the idle grace period.
|
||||||
|
///
|
||||||
|
/// The grace period starts after the idle timeout expires. During the grace period, the
|
||||||
|
/// screen goes black but the displays are not yet disabled and the idle callback (set
|
||||||
|
/// with [`on_idle`]) is not yet called. This is a purely visual effect to inform the user
|
||||||
|
/// that the machine will soon go idle.
|
||||||
|
///
|
||||||
|
/// The default is 5 seconds.
|
||||||
|
pub fn set_idle_grace_period(timeout: Duration) {
|
||||||
|
get!().set_idle_grace_period(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
/// Enables or disables explicit sync.
|
/// Enables or disables explicit sync.
|
||||||
///
|
///
|
||||||
/// Calling this after the compositor has started has no effect.
|
/// Calling this after the compositor has started has no effect.
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
- Implement wl-fixes.
|
- Implement wl-fixes.
|
||||||
- Implement ei_touchscreen v2.
|
- Implement ei_touchscreen v2.
|
||||||
- Implement idle-notification v2.
|
- Implement idle-notification v2.
|
||||||
|
- Add an idle grace period. During the grace period, the screen goes black but is neither
|
||||||
|
disabled nor locked. This is similar to how android handles going idle. The default is
|
||||||
|
5 seconds.
|
||||||
|
|
||||||
# 1.7.0 (2024-10-25)
|
# 1.7.0 (2024-10-25)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -504,6 +504,7 @@ impl MetalConnector {
|
||||||
true,
|
true,
|
||||||
render_hw_cursor,
|
render_hw_cursor,
|
||||||
node.has_fullscreen(),
|
node.has_fullscreen(),
|
||||||
|
true,
|
||||||
node.global.persistent.transform.get(),
|
node.global.persistent.transform.get(),
|
||||||
Some(&self.state.damage_visualizer),
|
Some(&self.state.damage_visualizer),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
34
src/cli.rs
34
src/cli.rs
|
|
@ -17,7 +17,7 @@ mod xwayland;
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{
|
cli::{
|
||||||
damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs,
|
damage_tracking::DamageTrackingArgs, idle::IdleCmd, input::InputArgs, randr::RandrArgs,
|
||||||
xwayland::XwaylandArgs,
|
xwayland::XwaylandArgs,
|
||||||
},
|
},
|
||||||
compositor::start_compositor,
|
compositor::start_compositor,
|
||||||
|
|
@ -101,38 +101,6 @@ pub struct RunPrivilegedArgs {
|
||||||
pub program: Vec<String>,
|
pub program: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
|
||||||
pub enum IdleCmd {
|
|
||||||
/// Print the idle status.
|
|
||||||
Status,
|
|
||||||
/// Set the idle interval.
|
|
||||||
Set(IdleSetArgs),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for IdleCmd {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
|
||||||
pub struct IdleSetArgs {
|
|
||||||
/// The interval of inactivity after which to disable the screens.
|
|
||||||
///
|
|
||||||
/// This can be either a number in minutes and seconds or the keyword `disabled` to
|
|
||||||
/// disable the screensaver.
|
|
||||||
///
|
|
||||||
/// Minutes and seconds can be specified in any of the following formats:
|
|
||||||
///
|
|
||||||
/// * 1m
|
|
||||||
/// * 1m5s
|
|
||||||
/// * 1m 5s
|
|
||||||
/// * 1min 5sec
|
|
||||||
/// * 1 minute 5 seconds
|
|
||||||
#[clap(verbatim_doc_comment, required = true)]
|
|
||||||
pub interval: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ValueEnum, Debug, Copy, Clone, Hash, Default, PartialEq)]
|
#[derive(ValueEnum, Debug, Copy, Clone, Hash, Default, PartialEq)]
|
||||||
pub enum ScreenshotFormat {
|
pub enum ScreenshotFormat {
|
||||||
/// The PNG image format.
|
/// The PNG image format.
|
||||||
|
|
|
||||||
127
src/cli/idle.rs
127
src/cli/idle.rs
|
|
@ -1,13 +1,60 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{duration::parse_duration, GlobalArgs, IdleArgs, IdleCmd, IdleSetArgs},
|
cli::{duration::parse_duration, GlobalArgs, IdleArgs},
|
||||||
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
||||||
utils::stack::Stack,
|
utils::{debug_fn::debug_fn, stack::Stack},
|
||||||
wire::{jay_compositor, jay_idle, JayIdleId, WlSurfaceId},
|
wire::{jay_compositor, jay_idle, JayIdleId, WlSurfaceId},
|
||||||
},
|
},
|
||||||
|
clap::{Args, Subcommand},
|
||||||
std::{cell::Cell, rc::Rc},
|
std::{cell::Cell, rc::Rc},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
pub enum IdleCmd {
|
||||||
|
/// Print the idle status.
|
||||||
|
Status,
|
||||||
|
/// Set the idle interval.
|
||||||
|
Set(IdleSetArgs),
|
||||||
|
/// Set the idle grace period.
|
||||||
|
SetGracePeriod(IdleSetGracePeriodArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IdleCmd {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub struct IdleSetArgs {
|
||||||
|
/// The interval of inactivity after which to disable the screens.
|
||||||
|
///
|
||||||
|
/// This can be either a number in minutes and seconds or the keyword `disabled` to
|
||||||
|
/// disable the screensaver.
|
||||||
|
///
|
||||||
|
/// Minutes and seconds can be specified in any of the following formats:
|
||||||
|
///
|
||||||
|
/// * 1m
|
||||||
|
/// * 1m5s
|
||||||
|
/// * 1m 5s
|
||||||
|
/// * 1min 5sec
|
||||||
|
/// * 1 minute 5 seconds
|
||||||
|
#[clap(verbatim_doc_comment, required = true)]
|
||||||
|
pub interval: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub struct IdleSetGracePeriodArgs {
|
||||||
|
/// The grace period after the idle timeout expires.
|
||||||
|
///
|
||||||
|
/// During this period, after the idle timeout expires, the screen only goes black
|
||||||
|
/// but is not yet disabled or locked.
|
||||||
|
///
|
||||||
|
/// This uses the same formatting options as the idle timeout itself.
|
||||||
|
#[clap(verbatim_doc_comment, required = true)]
|
||||||
|
pub period: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main(global: GlobalArgs, args: IdleArgs) {
|
pub fn main(global: GlobalArgs, args: IdleArgs) {
|
||||||
with_tool_client(global.log_level.into(), |tc| async move {
|
with_tool_client(global.log_level.into(), |tc| async move {
|
||||||
let idle = Idle { tc: tc.clone() };
|
let idle = Idle { tc: tc.clone() };
|
||||||
|
|
@ -31,16 +78,21 @@ impl Idle {
|
||||||
match args.command.unwrap_or_default() {
|
match args.command.unwrap_or_default() {
|
||||||
IdleCmd::Status => self.status(idle).await,
|
IdleCmd::Status => self.status(idle).await,
|
||||||
IdleCmd::Set(args) => self.set(idle, args).await,
|
IdleCmd::Set(args) => self.set(idle, args).await,
|
||||||
|
IdleCmd::SetGracePeriod(args) => self.set_grace_period(idle, args).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn status(self, idle: JayIdleId) {
|
async fn status(self, idle: JayIdleId) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
tc.send(jay_idle::GetStatus { self_id: idle });
|
tc.send(jay_idle::GetStatus { self_id: idle });
|
||||||
let interval = Rc::new(Cell::new(0u64));
|
let timeout = Rc::new(Cell::new(0u64));
|
||||||
jay_idle::Interval::handle(tc, idle, interval.clone(), |iv, msg| {
|
jay_idle::Interval::handle(tc, idle, timeout.clone(), |iv, msg| {
|
||||||
iv.set(msg.interval);
|
iv.set(msg.interval);
|
||||||
});
|
});
|
||||||
|
let grace = Rc::new(Cell::new(0u64));
|
||||||
|
jay_idle::GracePeriod::handle(tc, idle, grace.clone(), |iv, msg| {
|
||||||
|
iv.set(msg.period);
|
||||||
|
});
|
||||||
struct Inhibitor {
|
struct Inhibitor {
|
||||||
surface: WlSurfaceId,
|
surface: WlSurfaceId,
|
||||||
_client_id: u64,
|
_client_id: u64,
|
||||||
|
|
@ -57,26 +109,31 @@ impl Idle {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
let minutes = interval.get() / 60;
|
let interval = |iv: u64| {
|
||||||
let seconds = interval.get() % 60;
|
debug_fn(move |f| {
|
||||||
print!("Interval:");
|
let minutes = iv / 60;
|
||||||
if minutes == 0 && seconds == 0 {
|
let seconds = iv % 60;
|
||||||
print!(" disabled");
|
if minutes == 0 && seconds == 0 {
|
||||||
} else {
|
write!(f, " disabled")?;
|
||||||
if minutes > 0 {
|
} else {
|
||||||
print!(" {} minute", minutes);
|
if minutes > 0 {
|
||||||
if minutes > 1 {
|
write!(f, " {} minute", minutes)?;
|
||||||
print!("s");
|
if minutes > 1 {
|
||||||
|
write!(f, "s")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if seconds > 0 {
|
||||||
|
write!(f, " {} second", seconds)?;
|
||||||
|
if seconds > 1 {
|
||||||
|
write!(f, "s")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Ok(())
|
||||||
if seconds > 0 {
|
})
|
||||||
print!(" {} second", seconds);
|
};
|
||||||
if seconds > 1 {
|
println!("Interval:{}", interval(timeout.get()));
|
||||||
print!("s");
|
println!("Grace period:{}", interval(grace.get()));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
let mut inhibitors = inhibitors.take();
|
let mut inhibitors = inhibitors.take();
|
||||||
inhibitors.sort_by_key(|i| i.pid);
|
inhibitors.sort_by_key(|i| i.pid);
|
||||||
inhibitors.sort_by_key(|i| i.surface);
|
inhibitors.sort_by_key(|i| i.surface);
|
||||||
|
|
@ -93,15 +150,27 @@ impl Idle {
|
||||||
|
|
||||||
async fn set(self, idle: JayIdleId, args: IdleSetArgs) {
|
async fn set(self, idle: JayIdleId, args: IdleSetArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
let interval = if args.interval.len() == 1 && args.interval[0] == "disabled" {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
parse_duration(&args.interval).as_secs() as u64
|
|
||||||
};
|
|
||||||
tc.send(jay_idle::SetInterval {
|
tc.send(jay_idle::SetInterval {
|
||||||
self_id: idle,
|
self_id: idle,
|
||||||
interval,
|
interval: parse_idle_time(&args.interval),
|
||||||
|
});
|
||||||
|
tc.round_trip().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_grace_period(self, idle: JayIdleId, args: IdleSetGracePeriodArgs) {
|
||||||
|
let tc = &self.tc;
|
||||||
|
tc.send(jay_idle::SetGracePeriod {
|
||||||
|
self_id: idle,
|
||||||
|
period: parse_idle_time(&args.period),
|
||||||
});
|
});
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_idle_time(time: &[String]) -> u64 {
|
||||||
|
if time.len() == 1 && time[0] == "disabled" {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
parse_duration(time).as_secs() as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -200,11 +200,13 @@ fn start_compositor2(
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
change: Default::default(),
|
change: Default::default(),
|
||||||
timeout: Cell::new(Duration::from_secs(10 * 60)),
|
timeout: Cell::new(Duration::from_secs(10 * 60)),
|
||||||
|
grace_period: Cell::new(Duration::from_secs(5)),
|
||||||
timeout_changed: Default::default(),
|
timeout_changed: Default::default(),
|
||||||
inhibitors: Default::default(),
|
inhibitors: Default::default(),
|
||||||
inhibitors_changed: Default::default(),
|
inhibitors_changed: Default::default(),
|
||||||
inhibited_idle_notifications: Default::default(),
|
inhibited_idle_notifications: Default::default(),
|
||||||
backend_idle: Cell::new(true),
|
backend_idle: Cell::new(true),
|
||||||
|
in_grace_period: Cell::new(false),
|
||||||
},
|
},
|
||||||
run_args,
|
run_args,
|
||||||
xwayland: XWaylandState {
|
xwayland: XWaylandState {
|
||||||
|
|
|
||||||
|
|
@ -919,6 +919,10 @@ impl ConfigProxyHandler {
|
||||||
self.state.idle.set_timeout(timeout);
|
self.state.idle.set_timeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_set_idle_grace_period(&self, period: Duration) {
|
||||||
|
self.state.idle.set_grace_period(period);
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_set_explicit_sync_enabled(&self, enabled: bool) {
|
fn handle_set_explicit_sync_enabled(&self, enabled: bool) {
|
||||||
self.state.explicit_sync_enabled.set(enabled);
|
self.state.explicit_sync_enabled.set(enabled);
|
||||||
}
|
}
|
||||||
|
|
@ -1980,6 +1984,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetXScalingMode { mode } => self
|
ClientMessage::SetXScalingMode { mode } => self
|
||||||
.handle_set_x_scaling_mode(mode)
|
.handle_set_x_scaling_mode(mode)
|
||||||
.wrn("set_x_scaling_mode")?,
|
.wrn("set_x_scaling_mode")?,
|
||||||
|
ClientMessage::SetIdleGracePeriod { period } => {
|
||||||
|
self.handle_set_idle_grace_period(period)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,7 @@ impl dyn GfxFramebuffer {
|
||||||
render_cursor: bool,
|
render_cursor: bool,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
black_background: bool,
|
black_background: bool,
|
||||||
|
fill_black_in_grace_period: bool,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
visualizer: Option<&DamageVisualizer>,
|
visualizer: Option<&DamageVisualizer>,
|
||||||
) -> GfxRenderPass {
|
) -> GfxRenderPass {
|
||||||
|
|
@ -383,6 +384,7 @@ impl dyn GfxFramebuffer {
|
||||||
render_cursor,
|
render_cursor,
|
||||||
render_hardware_cursor,
|
render_hardware_cursor,
|
||||||
black_background,
|
black_background,
|
||||||
|
fill_black_in_grace_period,
|
||||||
transform,
|
transform,
|
||||||
visualizer,
|
visualizer,
|
||||||
)
|
)
|
||||||
|
|
@ -406,6 +408,7 @@ impl dyn GfxFramebuffer {
|
||||||
cursor_rect: Option<Rect>,
|
cursor_rect: Option<Rect>,
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
|
fill_black_in_grace_period: bool,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.render_node(
|
self.render_node(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
|
|
@ -417,6 +420,7 @@ impl dyn GfxFramebuffer {
|
||||||
true,
|
true,
|
||||||
render_hardware_cursor,
|
render_hardware_cursor,
|
||||||
node.has_fullscreen(),
|
node.has_fullscreen(),
|
||||||
|
fill_black_in_grace_period,
|
||||||
node.global.persistent.transform.get(),
|
node.global.persistent.transform.get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -432,6 +436,7 @@ impl dyn GfxFramebuffer {
|
||||||
render_cursor: bool,
|
render_cursor: bool,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
black_background: bool,
|
black_background: bool,
|
||||||
|
fill_black_in_grace_period: bool,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let pass = self.create_render_pass(
|
let pass = self.create_render_pass(
|
||||||
|
|
@ -442,6 +447,7 @@ impl dyn GfxFramebuffer {
|
||||||
render_cursor,
|
render_cursor,
|
||||||
render_hardware_cursor,
|
render_hardware_cursor,
|
||||||
black_background,
|
black_background,
|
||||||
|
fill_black_in_grace_period,
|
||||||
transform,
|
transform,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
@ -722,9 +728,16 @@ pub fn create_render_pass(
|
||||||
render_cursor: bool,
|
render_cursor: bool,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
black_background: bool,
|
black_background: bool,
|
||||||
|
fill_black_in_grace_period: bool,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
visualizer: Option<&DamageVisualizer>,
|
visualizer: Option<&DamageVisualizer>,
|
||||||
) -> GfxRenderPass {
|
) -> GfxRenderPass {
|
||||||
|
if fill_black_in_grace_period && state.idle.in_grace_period.get() {
|
||||||
|
return GfxRenderPass {
|
||||||
|
ops: vec![],
|
||||||
|
clear: Some(Color::SOLID_BLACK),
|
||||||
|
};
|
||||||
|
}
|
||||||
let mut ops = vec![];
|
let mut ops = vec![];
|
||||||
let mut renderer = Renderer {
|
let mut renderer = Renderer {
|
||||||
base: renderer_base(physical_size, &mut ops, scale, transform),
|
base: renderer_base(physical_size, &mut ops, scale, transform),
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
jay_config::video::Transform::None,
|
jay_config::video::Transform::None,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ impl Global for JayCompositorGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
12
|
13
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_caps(&self) -> ClientCaps {
|
fn required_caps(&self) -> ClientCaps {
|
||||||
|
|
@ -213,6 +213,7 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
id: req.id,
|
id: req.id,
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
version: self.version,
|
||||||
});
|
});
|
||||||
track!(self.client, idle);
|
track!(self.client, idle);
|
||||||
self.client.add_client_obj(&idle)?;
|
self.client.add_client_obj(&idle)?;
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,11 @@ pub struct JayIdle {
|
||||||
pub id: JayIdleId,
|
pub id: JayIdleId,
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GRACE_PERIOD_SINCE: Version = Version(13);
|
||||||
|
|
||||||
impl JayIdle {
|
impl JayIdle {
|
||||||
fn send_interval(&self) {
|
fn send_interval(&self) {
|
||||||
let to = self.client.state.idle.timeout.get();
|
let to = self.client.state.idle.timeout.get();
|
||||||
|
|
@ -25,6 +28,14 @@ impl JayIdle {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_grace_period(&self) {
|
||||||
|
let to = self.client.state.idle.grace_period.get();
|
||||||
|
self.client.event(GracePeriod {
|
||||||
|
self_id: self.id,
|
||||||
|
period: to.as_secs(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn send_inhibitor(&self, surface: &ZwpIdleInhibitorV1) {
|
fn send_inhibitor(&self, surface: &ZwpIdleInhibitorV1) {
|
||||||
let surface = &surface.surface;
|
let surface = &surface.surface;
|
||||||
self.client.event(Inhibitor {
|
self.client.event(Inhibitor {
|
||||||
|
|
@ -42,6 +53,9 @@ impl JayIdleRequestHandler for JayIdle {
|
||||||
|
|
||||||
fn get_status(&self, _req: GetStatus, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn get_status(&self, _req: GetStatus, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.send_interval();
|
self.send_interval();
|
||||||
|
if self.version >= GRACE_PERIOD_SINCE {
|
||||||
|
self.send_grace_period();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
let inhibitors = self.client.state.idle.inhibitors.lock();
|
let inhibitors = self.client.state.idle.inhibitors.lock();
|
||||||
for inhibitor in inhibitors.values() {
|
for inhibitor in inhibitors.values() {
|
||||||
|
|
@ -56,11 +70,17 @@ impl JayIdleRequestHandler for JayIdle {
|
||||||
self.client.state.idle.set_timeout(interval);
|
self.client.state.idle.set_timeout(interval);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_grace_period(&self, req: SetGracePeriod, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let period = Duration::from_secs(req.period);
|
||||||
|
self.client.state.idle.set_grace_period(period);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
self = JayIdle;
|
self = JayIdle;
|
||||||
version = Version(1);
|
version = self.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Object for JayIdle {}
|
impl Object for JayIdle {}
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ impl JayScreencast {
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
);
|
);
|
||||||
match res {
|
match res {
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,10 @@ impl TestConfig {
|
||||||
self.send(ClientMessage::SetIdle { timeout })
|
self.send(ClientMessage::SetIdle { timeout })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_idle_grace_period(&self, period: Duration) -> TestResult {
|
||||||
|
self.send(ClientMessage::SetIdleGracePeriod { period })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_floating(&self, seat: SeatId, floating: bool) -> TestResult {
|
pub fn set_floating(&self, seat: SeatId, floating: bool) -> TestResult {
|
||||||
self.send(ClientMessage::SetFloating {
|
self.send(ClientMessage::SetFloating {
|
||||||
seat: Seat(seat.raw() as _),
|
seat: Seat(seat.raw() as _),
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
let ds = run.create_default_setup().await?;
|
let ds = run.create_default_setup().await?;
|
||||||
|
|
||||||
run.cfg.set_idle(Duration::from_micros(100))?;
|
run.cfg.set_idle(Duration::from_micros(100))?;
|
||||||
|
run.cfg.set_idle_grace_period(Duration::from_secs(0))?;
|
||||||
|
|
||||||
let idle = run.backend.idle.expect()?;
|
let idle = run.backend.idle.expect()?;
|
||||||
tassert!(idle.next().is_err());
|
tassert!(idle.next().is_err());
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ pub fn take_screenshot(
|
||||||
include_cursor,
|
include_cursor,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
)?;
|
)?;
|
||||||
let drm = match allocator.drm() {
|
let drm = match allocator.drm() {
|
||||||
|
|
|
||||||
13
src/state.rs
13
src/state.rs
|
|
@ -261,12 +261,14 @@ pub struct IdleState {
|
||||||
pub input: Cell<bool>,
|
pub input: Cell<bool>,
|
||||||
pub change: AsyncEvent,
|
pub change: AsyncEvent,
|
||||||
pub timeout: Cell<Duration>,
|
pub timeout: Cell<Duration>,
|
||||||
|
pub grace_period: Cell<Duration>,
|
||||||
pub timeout_changed: Cell<bool>,
|
pub timeout_changed: Cell<bool>,
|
||||||
pub inhibitors: CopyHashMap<IdleInhibitorId, Rc<ZwpIdleInhibitorV1>>,
|
pub inhibitors: CopyHashMap<IdleInhibitorId, Rc<ZwpIdleInhibitorV1>>,
|
||||||
pub inhibitors_changed: Cell<bool>,
|
pub inhibitors_changed: Cell<bool>,
|
||||||
pub backend_idle: Cell<bool>,
|
pub backend_idle: Cell<bool>,
|
||||||
pub inhibited_idle_notifications:
|
pub inhibited_idle_notifications:
|
||||||
CopyHashMap<(ClientId, ExtIdleNotificationV1Id), Rc<ExtIdleNotificationV1>>,
|
CopyHashMap<(ClientId, ExtIdleNotificationV1Id), Rc<ExtIdleNotificationV1>>,
|
||||||
|
pub in_grace_period: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdleState {
|
impl IdleState {
|
||||||
|
|
@ -276,6 +278,12 @@ impl IdleState {
|
||||||
self.change.trigger();
|
self.change.trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_grace_period(&self, grace_period: Duration) {
|
||||||
|
self.grace_period.set(grace_period);
|
||||||
|
self.timeout_changed.set(true);
|
||||||
|
self.change.trigger();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_inhibitor(&self, inhibitor: &Rc<ZwpIdleInhibitorV1>) {
|
pub fn add_inhibitor(&self, inhibitor: &Rc<ZwpIdleInhibitorV1>) {
|
||||||
self.inhibitors.set(inhibitor.inhibit_id, inhibitor.clone());
|
self.inhibitors.set(inhibitor.inhibit_id, inhibitor.clone());
|
||||||
self.inhibitors_changed.set(true);
|
self.inhibitors_changed.set(true);
|
||||||
|
|
@ -937,6 +945,10 @@ impl State {
|
||||||
output: &Rc<OutputNode>,
|
output: &Rc<OutputNode>,
|
||||||
hc: &mut dyn HardwareCursorUpdate,
|
hc: &mut dyn HardwareCursorUpdate,
|
||||||
) {
|
) {
|
||||||
|
if self.idle.in_grace_period.get() {
|
||||||
|
hc.set_enabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let Some(g) = self.cursor_user_group_hardware_cursor.get() else {
|
let Some(g) = self.cursor_user_group_hardware_cursor.get() else {
|
||||||
hc.set_enabled(false);
|
hc.set_enabled(false);
|
||||||
return;
|
return;
|
||||||
|
|
@ -968,6 +980,7 @@ impl State {
|
||||||
Some(output.global.pos.get()),
|
Some(output.global.pos.get()),
|
||||||
output.global.persistent.scale.get(),
|
output.global.persistent.scale.get(),
|
||||||
render_hw_cursor,
|
render_hw_cursor,
|
||||||
|
true,
|
||||||
)?;
|
)?;
|
||||||
output.latched(false);
|
output.latched(false);
|
||||||
output.perform_screencopies(
|
output.perform_screencopies(
|
||||||
|
|
|
||||||
|
|
@ -61,9 +61,12 @@ impl Idle {
|
||||||
self.dead = true;
|
self.dead = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let grace_period = self.state.idle.grace_period.get();
|
||||||
let timeout = self.state.idle.timeout.get();
|
let timeout = self.state.idle.timeout.get();
|
||||||
|
let after_grace = timeout.saturating_add(grace_period);
|
||||||
let since = duration_since(self.last_input);
|
let since = duration_since(self.last_input);
|
||||||
if since >= timeout {
|
if since >= after_grace {
|
||||||
|
self.set_in_grace_period(false);
|
||||||
if !timeout.is_zero() && !self.is_inhibited {
|
if !timeout.is_zero() && !self.is_inhibited {
|
||||||
if let Some(config) = self.state.config.get() {
|
if let Some(config) = self.state.config.get() {
|
||||||
config.idle();
|
config.idle();
|
||||||
|
|
@ -71,17 +74,31 @@ impl Idle {
|
||||||
self.backend.set_idle(true);
|
self.backend.set_idle(true);
|
||||||
self.idle = true;
|
self.idle = true;
|
||||||
}
|
}
|
||||||
|
} else if since >= timeout {
|
||||||
|
if !timeout.is_zero() && !self.is_inhibited {
|
||||||
|
self.set_in_grace_period(true);
|
||||||
|
}
|
||||||
|
self.program_timer2(after_grace - since);
|
||||||
} else {
|
} else {
|
||||||
self.program_timer2(timeout - since);
|
self.program_timer2(timeout - since);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_in_grace_period(&mut self, val: bool) {
|
||||||
|
if self.state.idle.in_grace_period.replace(val) == val {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.state.damage(self.state.root.extents.get());
|
||||||
|
self.state.damage_hardware_cursors(false);
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_idle_changes(&mut self) {
|
fn handle_idle_changes(&mut self) {
|
||||||
if self.state.idle.inhibitors_changed.replace(false) {
|
if self.state.idle.inhibitors_changed.replace(false) {
|
||||||
let is_inhibited = self.state.idle.inhibitors.len() > 0;
|
let is_inhibited = self.state.idle.inhibitors.len() > 0;
|
||||||
if self.is_inhibited != is_inhibited {
|
if self.is_inhibited != is_inhibited {
|
||||||
self.is_inhibited = is_inhibited;
|
self.is_inhibited = is_inhibited;
|
||||||
if !self.is_inhibited {
|
if !self.is_inhibited {
|
||||||
|
self.last_input = now();
|
||||||
self.program_timer();
|
self.program_timer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,6 +108,7 @@ impl Idle {
|
||||||
}
|
}
|
||||||
if self.state.idle.input.replace(false) {
|
if self.state.idle.input.replace(false) {
|
||||||
self.last_input = now();
|
self.last_input = now();
|
||||||
|
self.set_in_grace_period(false);
|
||||||
if self.idle {
|
if self.idle {
|
||||||
self.backend.set_idle(false);
|
self.backend.set_idle(false);
|
||||||
self.idle = false;
|
self.idle = false;
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ impl ToolClient {
|
||||||
self_id: s.registry,
|
self_id: s.registry,
|
||||||
name: s.jay_compositor.0,
|
name: s.jay_compositor.0,
|
||||||
interface: JayCompositor.name(),
|
interface: JayCompositor.name(),
|
||||||
version: s.jay_compositor.1.min(11),
|
version: s.jay_compositor.1.min(13),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
});
|
});
|
||||||
self.jay_compositor.set(Some(id));
|
self.jay_compositor.set(Some(id));
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ pub enum Action {
|
||||||
dev: ConfigDrmDevice,
|
dev: ConfigDrmDevice,
|
||||||
},
|
},
|
||||||
ConfigureIdle {
|
ConfigureIdle {
|
||||||
idle: Duration,
|
idle: Option<Duration>,
|
||||||
|
grace_period: Option<Duration>,
|
||||||
},
|
},
|
||||||
ConfigureInput {
|
ConfigureInput {
|
||||||
input: Box<Input>,
|
input: Box<Input>,
|
||||||
|
|
@ -348,6 +349,7 @@ pub struct Config {
|
||||||
pub render_device: Option<DrmDeviceMatch>,
|
pub render_device: Option<DrmDeviceMatch>,
|
||||||
pub inputs: Vec<Input>,
|
pub inputs: Vec<Input>,
|
||||||
pub idle: Option<Duration>,
|
pub idle: Option<Duration>,
|
||||||
|
pub grace_period: Option<Duration>,
|
||||||
pub explicit_sync_enabled: Option<bool>,
|
pub explicit_sync_enabled: Option<bool>,
|
||||||
pub focus_follows_mouse: bool,
|
pub focus_follows_mouse: bool,
|
||||||
pub window_management_key: Option<ModifiedKeySym>,
|
pub window_management_key: Option<ModifiedKeySym>,
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,10 @@ impl ActionParser<'_> {
|
||||||
.extract(val("idle"))?
|
.extract(val("idle"))?
|
||||||
.parse_map(&mut IdleParser(self.0))
|
.parse_map(&mut IdleParser(self.0))
|
||||||
.map_spanned_err(ActionParserError::ConfigureIdle)?;
|
.map_spanned_err(ActionParserError::ConfigureIdle)?;
|
||||||
Ok(Action::ConfigureIdle { idle })
|
Ok(Action::ConfigureIdle {
|
||||||
|
idle: idle.timeout,
|
||||||
|
grace_period: idle.grace_period,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_configure_output(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
fn parse_configure_output(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||||
|
|
|
||||||
|
|
@ -294,9 +294,13 @@ impl Parser for ConfigParser<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut idle = None;
|
let mut idle = None;
|
||||||
|
let mut grace_period = None;
|
||||||
if let Some(value) = idle_val {
|
if let Some(value) = idle_val {
|
||||||
match value.parse(&mut IdleParser(self.0)) {
|
match value.parse(&mut IdleParser(self.0)) {
|
||||||
Ok(v) => idle = Some(v),
|
Ok(v) => {
|
||||||
|
idle = v.timeout;
|
||||||
|
grace_period = v.grace_period;
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Could not parse the idle timeout: {}", self.0.error(e));
|
log::warn!("Could not parse the idle timeout: {}", self.0.error(e));
|
||||||
}
|
}
|
||||||
|
|
@ -384,6 +388,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
render_device,
|
render_device,
|
||||||
inputs,
|
inputs,
|
||||||
idle,
|
idle,
|
||||||
|
grace_period,
|
||||||
focus_follows_mouse: focus_follows_mouse.despan().unwrap_or(true),
|
focus_follows_mouse: focus_follows_mouse.despan().unwrap_or(true),
|
||||||
window_management_key,
|
window_management_key,
|
||||||
vrr,
|
vrr,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
config::{
|
config::{
|
||||||
context::Context,
|
context::Context,
|
||||||
extractor::{n64, opt, Extractor, ExtractorError},
|
extractor::{n64, opt, val, Extractor, ExtractorError},
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
},
|
},
|
||||||
toml::{
|
toml::{
|
||||||
|
|
@ -25,7 +25,45 @@ pub enum IdleParserError {
|
||||||
|
|
||||||
pub struct IdleParser<'a>(pub &'a Context<'a>);
|
pub struct IdleParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
||||||
|
pub struct Idle {
|
||||||
|
pub timeout: Option<Duration>,
|
||||||
|
pub grace_period: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Parser for IdleParser<'_> {
|
impl Parser for IdleParser<'_> {
|
||||||
|
type Value = Idle;
|
||||||
|
type Error = IdleParserError;
|
||||||
|
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 (minutes, seconds, grace_period_val) = ext.extract((
|
||||||
|
opt(n64("minutes")),
|
||||||
|
opt(n64("seconds")),
|
||||||
|
opt(val("grace-period")),
|
||||||
|
))?;
|
||||||
|
let mut timeout = None;
|
||||||
|
if minutes.is_some() || seconds.is_some() {
|
||||||
|
timeout = Some(parse_duration(&minutes, &seconds));
|
||||||
|
}
|
||||||
|
let mut grace_period = None;
|
||||||
|
if let Some(gp) = grace_period_val {
|
||||||
|
grace_period = Some(gp.parse(&mut GracePeriodParser(self.0))?);
|
||||||
|
}
|
||||||
|
Ok(Idle {
|
||||||
|
timeout,
|
||||||
|
grace_period,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GracePeriodParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
||||||
|
impl Parser for GracePeriodParser<'_> {
|
||||||
type Value = Duration;
|
type Value = Duration;
|
||||||
type Error = IdleParserError;
|
type Error = IdleParserError;
|
||||||
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||||
|
|
@ -37,9 +75,13 @@ impl Parser for IdleParser<'_> {
|
||||||
) -> ParseResult<Self> {
|
) -> ParseResult<Self> {
|
||||||
let mut ext = Extractor::new(self.0, span, table);
|
let mut ext = Extractor::new(self.0, span, table);
|
||||||
let (minutes, seconds) = ext.extract((opt(n64("minutes")), opt(n64("seconds"))))?;
|
let (minutes, seconds) = ext.extract((opt(n64("minutes")), opt(n64("seconds"))))?;
|
||||||
let idle = Duration::from_secs(
|
let grace_period = parse_duration(&minutes, &seconds);
|
||||||
minutes.despan().unwrap_or_default() * 60 + seconds.despan().unwrap_or_default(),
|
Ok(grace_period)
|
||||||
);
|
|
||||||
Ok(idle)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_duration(minutes: &Option<Spanned<u64>>, seconds: &Option<Spanned<u64>>) -> Duration {
|
||||||
|
Duration::from_secs(
|
||||||
|
minutes.despan().unwrap_or_default() * 60 + seconds.despan().unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ use {
|
||||||
keyboard::{Keymap, ModifiedKeySym},
|
keyboard::{Keymap, ModifiedKeySym},
|
||||||
logging::set_log_level,
|
logging::set_log_level,
|
||||||
on_devices_enumerated, on_idle, quit, reload, set_default_workspace_capture,
|
on_devices_enumerated, on_idle, quit, reload, set_default_workspace_capture,
|
||||||
set_explicit_sync_enabled, set_idle, set_ui_drag_enabled, set_ui_drag_threshold,
|
set_explicit_sync_enabled, set_idle, set_idle_grace_period, set_ui_drag_enabled,
|
||||||
|
set_ui_drag_threshold,
|
||||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||||
switch_to_vt,
|
switch_to_vt,
|
||||||
theme::{reset_colors, reset_font, reset_sizes, set_font},
|
theme::{reset_colors, reset_font, reset_sizes, set_font},
|
||||||
|
|
@ -188,7 +189,14 @@ impl Action {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Action::ConfigureIdle { idle } => B::new(move || set_idle(Some(idle))),
|
Action::ConfigureIdle { idle, grace_period } => B::new(move || {
|
||||||
|
if let Some(idle) = idle {
|
||||||
|
set_idle(Some(idle))
|
||||||
|
}
|
||||||
|
if let Some(period) = grace_period {
|
||||||
|
set_idle_grace_period(period)
|
||||||
|
}
|
||||||
|
}),
|
||||||
Action::MoveToOutput { output, workspace } => {
|
Action::MoveToOutput { output, workspace } => {
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
B::new(move || {
|
B::new(move || {
|
||||||
|
|
@ -967,6 +975,9 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
||||||
if let Some(idle) = config.idle {
|
if let Some(idle) = config.idle {
|
||||||
set_idle(Some(idle));
|
set_idle(Some(idle));
|
||||||
}
|
}
|
||||||
|
if let Some(period) = config.grace_period {
|
||||||
|
set_idle_grace_period(period);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
on_devices_enumerated({
|
on_devices_enumerated({
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
|
|
|
||||||
|
|
@ -811,8 +811,25 @@
|
||||||
"Vulkan"
|
"Vulkan"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"GracePeriod": {
|
||||||
|
"description": "The definition of a grace period.\n\nOmitted values are set to 0. If all values are 0, the grace period is disabled.\n\n- Example:\n\n ```toml\n idle.grace-period.seconds = 3\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"minutes": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The number of minutes the grace period lasts.",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"seconds": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The number of seconds the grace period lasts.",
|
||||||
|
"minimum": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
},
|
||||||
"Idle": {
|
"Idle": {
|
||||||
"description": "The definition of an idle timeout.\n\nOmitted values are set to 0. If all values are 0, the idle timeout is disabled.\n\n- Example:\n\n ```toml\n idle.minutes = 10\n ```\n",
|
"description": "The definition of an idle timeout.\n\nOmitted values are set to 0. If any value is explicitly set and all values are 0, the\nidle timeout is disabled.\n\n- Example:\n\n ```toml\n idle.minutes = 10\n ```\n",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"minutes": {
|
"minutes": {
|
||||||
|
|
@ -824,6 +841,10 @@
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "The number of seconds before going idle.",
|
"description": "The number of seconds before going idle.",
|
||||||
"minimum": 0.0
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"grace-period": {
|
||||||
|
"description": "The grace period after the timeout expires.\n\nDuring the grace period, the screen goes black but the outputs are not yet\ndisabled and the `on-idle` action does not yet run. This is a visual indicator\nthat the system will soon get idle.\n\nThe default is 5 seconds.\n",
|
||||||
|
"$ref": "#/$defs/GracePeriod"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
|
||||||
|
|
@ -1670,12 +1670,51 @@ The string should have one of the following values:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-GracePeriod"></a>
|
||||||
|
### `GracePeriod`
|
||||||
|
|
||||||
|
The definition of a grace period.
|
||||||
|
|
||||||
|
Omitted values are set to 0. If all values are 0, the grace period is disabled.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
idle.grace-period.seconds = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Values of this type should be tables.
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `minutes` (optional):
|
||||||
|
|
||||||
|
The number of minutes the grace period lasts.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be integers.
|
||||||
|
|
||||||
|
The numbers should be greater than or equal to 0.
|
||||||
|
|
||||||
|
- `seconds` (optional):
|
||||||
|
|
||||||
|
The number of seconds the grace period lasts.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be integers.
|
||||||
|
|
||||||
|
The numbers should be greater than or equal to 0.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Idle"></a>
|
<a name="types-Idle"></a>
|
||||||
### `Idle`
|
### `Idle`
|
||||||
|
|
||||||
The definition of an idle timeout.
|
The definition of an idle timeout.
|
||||||
|
|
||||||
Omitted values are set to 0. If all values are 0, the idle timeout is disabled.
|
Omitted values are set to 0. If any value is explicitly set and all values are 0, the
|
||||||
|
idle timeout is disabled.
|
||||||
|
|
||||||
- Example:
|
- Example:
|
||||||
|
|
||||||
|
|
@ -1707,6 +1746,18 @@ The table has the following fields:
|
||||||
|
|
||||||
The numbers should be greater than or equal to 0.
|
The numbers should be greater than or equal to 0.
|
||||||
|
|
||||||
|
- `grace-period` (optional):
|
||||||
|
|
||||||
|
The grace period after the timeout expires.
|
||||||
|
|
||||||
|
During the grace period, the screen goes black but the outputs are not yet
|
||||||
|
disabled and the `on-idle` action does not yet run. This is a visual indicator
|
||||||
|
that the system will soon get idle.
|
||||||
|
|
||||||
|
The default is 5 seconds.
|
||||||
|
|
||||||
|
The value of this field should be a [GracePeriod](#types-GracePeriod).
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Input"></a>
|
<a name="types-Input"></a>
|
||||||
### `Input`
|
### `Input`
|
||||||
|
|
|
||||||
|
|
@ -2293,7 +2293,8 @@ Idle:
|
||||||
description: |
|
description: |
|
||||||
The definition of an idle timeout.
|
The definition of an idle timeout.
|
||||||
|
|
||||||
Omitted values are set to 0. If all values are 0, the idle timeout is disabled.
|
Omitted values are set to 0. If any value is explicitly set and all values are 0, the
|
||||||
|
idle timeout is disabled.
|
||||||
|
|
||||||
- Example:
|
- Example:
|
||||||
|
|
||||||
|
|
@ -2313,6 +2314,44 @@ Idle:
|
||||||
integer_only: true
|
integer_only: true
|
||||||
minimum: 0
|
minimum: 0
|
||||||
required: false
|
required: false
|
||||||
|
grace-period:
|
||||||
|
description: |
|
||||||
|
The grace period after the timeout expires.
|
||||||
|
|
||||||
|
During the grace period, the screen goes black but the outputs are not yet
|
||||||
|
disabled and the `on-idle` action does not yet run. This is a visual indicator
|
||||||
|
that the system will soon get idle.
|
||||||
|
|
||||||
|
The default is 5 seconds.
|
||||||
|
ref: GracePeriod
|
||||||
|
required: false
|
||||||
|
|
||||||
|
|
||||||
|
GracePeriod:
|
||||||
|
kind: table
|
||||||
|
description: |
|
||||||
|
The definition of a grace period.
|
||||||
|
|
||||||
|
Omitted values are set to 0. If all values are 0, the grace period is disabled.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
idle.grace-period.seconds = 3
|
||||||
|
```
|
||||||
|
fields:
|
||||||
|
minutes:
|
||||||
|
description: The number of minutes the grace period lasts.
|
||||||
|
kind: number
|
||||||
|
integer_only: true
|
||||||
|
minimum: 0
|
||||||
|
required: false
|
||||||
|
seconds:
|
||||||
|
description: The number of seconds the grace period lasts.
|
||||||
|
kind: number
|
||||||
|
integer_only: true
|
||||||
|
minimum: 0
|
||||||
|
required: false
|
||||||
|
|
||||||
|
|
||||||
RepeatRate:
|
RepeatRate:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@ request set_interval {
|
||||||
interval: pod(u64),
|
interval: pod(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request set_grace_period (since = 13) {
|
||||||
|
period: pod(u64),
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event interval {
|
event interval {
|
||||||
|
|
@ -19,3 +23,7 @@ event inhibitor {
|
||||||
pid: pod(u64),
|
pid: pod(u64),
|
||||||
comm: str,
|
comm: str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event grace_period (since = 13) {
|
||||||
|
period: pod(u64),
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue