Compare commits
10 commits
eaa09a6fef
...
60bce9eae4
| Author | SHA1 | Date | |
|---|---|---|---|
| 60bce9eae4 | |||
|
|
c95d066b5b | ||
|
|
6d3dbedd50 | ||
|
|
983d34ad96 | ||
|
|
16abcc66e1 | ||
|
|
267bb837f1 | ||
|
|
7899fccbbe | ||
|
|
b0a2103dfc | ||
|
|
8e9706bd21 | ||
|
|
e3f2e505bb |
9 changed files with 1024 additions and 775 deletions
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
- [ ] I have disclosed use of any AI generated code in my commit messages.
|
||||
- If you are using an LLM, and do not fully understand the changes it is making to the code base, do not create a PR.
|
||||
- In our experience, AI generated code often results in overly complex code that lacks enough context for a proper fix or feature inclusion. This results in considerably longer code reviews. Due to this, AI authored or partially authored PRs may be closed without comment.
|
||||
- [ ] I understand these changes in full and will be able to respond to review comments.
|
||||
- [ ] My change is accurately described in the commit message.
|
||||
- [ ] My contribution is tested and working as described.
|
||||
- [ ] I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions.
|
||||
|
||||
1641
Cargo.lock
generated
1641
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
30
Cargo.toml
30
Cargo.toml
|
|
@ -1,25 +1,31 @@
|
|||
[package]
|
||||
name = "cosmic-idle"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
keyframe = "1.1.1"
|
||||
wayland-client = "0.31.5"
|
||||
wayland-protocols = { version = "0.32.3", features = ["client", "staging"] }
|
||||
wayland-protocols-wlr = { version = "0.3.3", features = ["client"] }
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic", features = ["calloop"] }
|
||||
wayland-client = "0.31.11"
|
||||
wayland-protocols = { version = "0.32.9", features = ["client", "staging"] }
|
||||
wayland-protocols-wlr = { version = "0.3.9", features = ["client"] }
|
||||
cosmic-config = { path = "../libcosmic/cosmic-config", features = ["calloop"] }
|
||||
cosmic-idle-config = { path = "./cosmic-idle-config" }
|
||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" }
|
||||
calloop = { version = "0.14.0", features = ["executor"] }
|
||||
calloop-wayland-source = "0.4.0"
|
||||
log = "0.4.22"
|
||||
env_logger = "0.11.5"
|
||||
cosmic-settings-config = { path = "../cosmic-settings-daemon/config" }
|
||||
calloop = { version = "0.14.3", features = ["executor"] }
|
||||
calloop-wayland-source = "0.4.1"
|
||||
log = "0.4.28"
|
||||
env_logger = "0.11.8"
|
||||
upower_dbus = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
||||
zbus = "4.0.0"
|
||||
futures-lite = "2.3.0"
|
||||
zbus = "5.12"
|
||||
futures-lite = "2.6.1"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"cosmic-idle-config"
|
||||
]
|
||||
|
||||
[patch.'https://github.com/pop-os/libcosmic']
|
||||
cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||
|
||||
[patch.'https://github.com/pop-os/cosmic-settings-daemon']
|
||||
cosmic-settings-config = { path = "../cosmic-settings-daemon/config" }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "cosmic-idle-config"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic" }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
cosmic-config = { path = "../../libcosmic/cosmic-config" }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use cosmic_config::{cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry};
|
||||
use cosmic_config::{CosmicConfigEntry, cosmic_config_derive::CosmicConfigEntry};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, CosmicConfigEntry)]
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
reorder_imports = true
|
||||
imports_granularity = "Crate"
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
use keyframe::{ease, functions::EaseInOut};
|
||||
use std::time::{Duration, Instant};
|
||||
use wayland_client::{
|
||||
delegate_noop,
|
||||
Connection, Dispatch, QueueHandle, delegate_noop,
|
||||
protocol::{wl_buffer, wl_callback, wl_output, wl_pointer, wl_surface},
|
||||
Connection, Dispatch, QueueHandle,
|
||||
};
|
||||
use wayland_protocols::wp::{
|
||||
single_pixel_buffer::v1::client::wp_single_pixel_buffer_manager_v1,
|
||||
|
|
@ -15,7 +14,7 @@ use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_l
|
|||
|
||||
use crate::{State, StateInner};
|
||||
|
||||
const FADE_TIME: Duration = Duration::from_millis(2000);
|
||||
const FADE_TIME: Duration = Duration::from_secs(5);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FadeBlackSurface {
|
||||
|
|
@ -101,12 +100,12 @@ impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ()> for State {
|
|||
height,
|
||||
} => {
|
||||
for output in &mut state.outputs {
|
||||
if let Some(fade_surface) = &mut output.fade_surface {
|
||||
if &fade_surface.layer_surface == obj {
|
||||
fade_surface.layer_surface.ack_configure(serial);
|
||||
fade_surface.configure(&state.inner, width, height);
|
||||
break;
|
||||
}
|
||||
if let Some(fade_surface) = &mut output.fade_surface
|
||||
&& &fade_surface.layer_surface == obj
|
||||
{
|
||||
fade_surface.layer_surface.ack_configure(serial);
|
||||
fade_surface.configure(&state.inner, width, height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -127,23 +126,23 @@ impl Dispatch<wl_callback::WlCallback, wl_surface::WlSurface> for State {
|
|||
match event {
|
||||
wl_callback::Event::Done { callback_data: _ } => {
|
||||
for output in &mut state.outputs {
|
||||
if let Some(fade_surface) = &mut output.fade_surface {
|
||||
if &fade_surface.surface == surface {
|
||||
if fade_surface.is_done() {
|
||||
// All outputs are done fading
|
||||
if state
|
||||
.outputs
|
||||
.iter()
|
||||
.flat_map(|o| o.fade_surface.as_ref())
|
||||
.all(|s| s.is_done())
|
||||
{
|
||||
state.fade_done();
|
||||
}
|
||||
} else {
|
||||
fade_surface.update(&state.inner);
|
||||
if let Some(fade_surface) = &mut output.fade_surface
|
||||
&& &fade_surface.surface == surface
|
||||
{
|
||||
if fade_surface.is_done() {
|
||||
// All outputs are done fading
|
||||
if state
|
||||
.outputs
|
||||
.iter()
|
||||
.flat_map(|o| o.fade_surface.as_ref())
|
||||
.all(|s| s.is_done())
|
||||
{
|
||||
state.fade_done();
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
fade_surface.update(&state.inner);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
use futures_lite::StreamExt;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc, Mutex,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use crate::{Event, EventSender};
|
||||
|
|
@ -99,14 +99,14 @@ pub async fn serve(event_sender: EventSender) -> zbus::Result<()> {
|
|||
let mut name_owner_stream = dbus.receive_name_owner_changed().await?;
|
||||
while let Some(event) = name_owner_stream.next().await {
|
||||
let args = event.args()?;
|
||||
if args.new_owner.is_none() {
|
||||
if let zbus::names::BusName::Unique(name) = args.name {
|
||||
let mut inhibitors = inhibitors.lock().unwrap();
|
||||
if !inhibitors.is_empty() {
|
||||
inhibitors.retain(|inhibitor| inhibitor.client != name);
|
||||
if inhibitors.is_empty() {
|
||||
let _ = event_sender.send(Event::ScreensaverInhibit(false));
|
||||
}
|
||||
if args.new_owner.is_none()
|
||||
&& let zbus::names::BusName::Unique(name) = args.name
|
||||
{
|
||||
let mut inhibitors = inhibitors.lock().unwrap();
|
||||
if !inhibitors.is_empty() {
|
||||
inhibitors.retain(|inhibitor| inhibitor.client != name);
|
||||
if inhibitors.is_empty() {
|
||||
let _ = event_sender.send(Event::ScreensaverInhibit(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
46
src/main.rs
46
src/main.rs
|
|
@ -1,18 +1,17 @@
|
|||
#![allow(clippy::single_match)]
|
||||
|
||||
use calloop::{channel, EventLoop};
|
||||
use calloop::{EventLoop, channel, timer};
|
||||
use calloop_wayland_source::WaylandSource;
|
||||
use cosmic_config::{calloop::ConfigWatchSource, CosmicConfigEntry};
|
||||
use cosmic_config::{CosmicConfigEntry, calloop::ConfigWatchSource};
|
||||
use cosmic_idle_config::CosmicIdleConfig;
|
||||
use cosmic_settings_config::shortcuts;
|
||||
use futures_lite::stream::StreamExt;
|
||||
use std::process::Command;
|
||||
use std::{process::Command, time::Duration};
|
||||
use upower_dbus::UPowerProxy;
|
||||
use wayland_client::{
|
||||
delegate_noop,
|
||||
globals::{registry_queue_init, GlobalListContents},
|
||||
Connection, Dispatch, Proxy, QueueHandle, delegate_noop,
|
||||
globals::{GlobalListContents, registry_queue_init},
|
||||
protocol::{wl_compositor, wl_output, wl_registry, wl_seat},
|
||||
Connection, Dispatch, Proxy, QueueHandle,
|
||||
};
|
||||
use wayland_protocols::{
|
||||
ext::idle_notify::v1::client::{ext_idle_notification_v1, ext_idle_notifier_v1},
|
||||
|
|
@ -30,6 +29,9 @@ mod fade_black;
|
|||
use fade_black::FadeBlackSurface;
|
||||
mod freedesktop_screensaver;
|
||||
|
||||
// Delay between screen off and locking
|
||||
const LOCK_SCREEN_DELAY: Duration = Duration::from_millis(500);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Event {
|
||||
OnBattery(bool),
|
||||
|
|
@ -99,6 +101,7 @@ struct State {
|
|||
on_battery: bool,
|
||||
screensaver_inhibit: bool,
|
||||
system_actions: shortcuts::SystemActions,
|
||||
loop_handle: calloop::LoopHandle<'static, Self>,
|
||||
}
|
||||
|
||||
fn run_command(command: String) {
|
||||
|
|
@ -157,18 +160,30 @@ impl State {
|
|||
output.fade_surface = None;
|
||||
}
|
||||
|
||||
if let Some(command) = self
|
||||
let timer = timer::Timer::from_duration(LOCK_SCREEN_DELAY);
|
||||
self.loop_handle
|
||||
.insert_source(timer, |_, _, state| {
|
||||
state.lock_screen();
|
||||
timer::TimeoutAction::Drop
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn lock_screen(&self) {
|
||||
let command = self
|
||||
.system_actions
|
||||
.get(&shortcuts::action::System::LockScreen)
|
||||
{
|
||||
crate::run_command(command.to_string());
|
||||
}
|
||||
.map_or("loginctl lock-session", |s| s.as_str());
|
||||
crate::run_command(command.to_string());
|
||||
}
|
||||
|
||||
fn update_suspend_idle(&mut self, is_idle: bool) {
|
||||
if is_idle {
|
||||
// TODO: Make command configurable
|
||||
run_command("systemctl suspend".to_string());
|
||||
let command = self
|
||||
.system_actions
|
||||
.get(&shortcuts::action::System::Suspend)
|
||||
.map_or("systemctl suspend", |s| s.as_str());
|
||||
crate::run_command(command.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -267,6 +282,8 @@ fn main() {
|
|||
let shortcuts_config = shortcuts::context().unwrap();
|
||||
let system_actions = shortcuts::system_actions(&shortcuts_config);
|
||||
|
||||
let mut event_loop: EventLoop<State> = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut state = State {
|
||||
inner: StateInner {
|
||||
registry: globals.registry().clone(),
|
||||
|
|
@ -286,6 +303,7 @@ fn main() {
|
|||
on_battery: false,
|
||||
screensaver_inhibit: false,
|
||||
system_actions,
|
||||
loop_handle: event_loop.handle(),
|
||||
};
|
||||
globals.contents().with_list(|list| {
|
||||
for global in list {
|
||||
|
|
@ -296,8 +314,6 @@ fn main() {
|
|||
});
|
||||
state.recreate_notifications();
|
||||
|
||||
let mut event_loop: EventLoop<State> = EventLoop::try_new().unwrap();
|
||||
|
||||
WaylandSource::new(connection, event_queue)
|
||||
.insert(event_loop.handle())
|
||||
.unwrap();
|
||||
|
|
@ -342,7 +358,7 @@ fn main() {
|
|||
})
|
||||
.unwrap();
|
||||
|
||||
while let Ok(_) = event_loop.dispatch(None, &mut state) {}
|
||||
while event_loop.dispatch(None, &mut state).is_ok() {}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue