REFACTOR REFACTOR REFACTOR
Added logging Improved control flow Stopped blocking the goddamn runtime with my background code Got rid of a potential double free
This commit is contained in:
parent
48c493c4f5
commit
4f2902a847
@ -28,6 +28,9 @@ tokio-stream = {version = "0.1.18", features = ["fs"]}
|
|||||||
greetd-stub = "0.3.0"
|
greetd-stub = "0.3.0"
|
||||||
chrono = "0.4.44"
|
chrono = "0.4.44"
|
||||||
|
|
||||||
|
pretty_env_logger = "0.5.0"
|
||||||
|
log = {version = "0.4.32", features = ["kv"]}
|
||||||
|
|
||||||
pexels-api = { git = "https://github.com/houseme/pexels", rev = "b0b692a" }
|
pexels-api = { git = "https://github.com/houseme/pexels", rev = "b0b692a" }
|
||||||
reqwest = { version = "0.13.3", features = ["json", "default-tls"] }
|
reqwest = { version = "0.13.3", features = ["json", "default-tls"] }
|
||||||
|
|
||||||
|
|||||||
400
src/callbacks.rs
400
src/callbacks.rs
@ -1,72 +1,82 @@
|
|||||||
use std::collections::VecDeque;
|
use crate::config::Config;
|
||||||
use std::path::{Path, PathBuf};
|
use crate::input::KeyboardLockStates;
|
||||||
use std::rc::Rc;
|
use crate::{GreeterData, GreeterDisplay, LoginOptionTileData};
|
||||||
use std::sync::Arc;
|
use anyhow::{Context, anyhow};
|
||||||
use anyhow::{anyhow, Context};
|
|
||||||
use greetd_ipc::{AuthMessageType, ErrorType, Request, Response};
|
|
||||||
use greetd_ipc::codec::TokioCodec;
|
use greetd_ipc::codec::TokioCodec;
|
||||||
|
use greetd_ipc::{AuthMessageType, ErrorType, Request, Response};
|
||||||
use i_slint_core::api::{ComponentHandle, Global, Image, Rgba8Pixel, SharedPixelBuffer, Weak};
|
use i_slint_core::api::{ComponentHandle, Global, Image, Rgba8Pixel, SharedPixelBuffer, Weak};
|
||||||
use i_slint_core::model::{Model, VecModel};
|
use i_slint_core::model::{Model, VecModel};
|
||||||
|
use log::{debug, error, info, trace};
|
||||||
use magick_rust::MagickWand;
|
use magick_rust::MagickWand;
|
||||||
use pexels_api::{PexelsClient, SearchParams};
|
use pexels_api::{PexelsClient, SearchParams};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::{AsyncBufReadExt, BufReader, Lines};
|
use tokio::io::{AsyncBufReadExt, BufReader, Lines};
|
||||||
use tokio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
use crate::{GreeterData, GreeterDisplay, LoginOptionTileData};
|
|
||||||
use crate::config::Config;
|
|
||||||
use crate::input::KeyboardLockStates;
|
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
use zbus::{Connection, proxy};
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
use slint::SharedString;
|
use slint::SharedString;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
use tokio_stream::wrappers::ReadDirStream;
|
use tokio_stream::wrappers::ReadDirStream;
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
use zbus::{Connection, proxy};
|
||||||
|
|
||||||
pub fn register_login_callback(data: &GreeterData, display_weak: Weak<GreeterDisplay>, tiles: Rc<VecModel<LoginOptionTileData>>) {
|
pub fn register_login_callback(
|
||||||
|
data: &GreeterData,
|
||||||
|
display_weak: Weak<GreeterDisplay>,
|
||||||
|
tiles: Rc<VecModel<LoginOptionTileData>>,
|
||||||
|
) {
|
||||||
data.on_login({
|
data.on_login({
|
||||||
let tiles = tiles.clone();
|
let tiles = tiles.clone();
|
||||||
let display_weak = display_weak.clone();
|
let display_weak = display_weak.clone();
|
||||||
|
|
||||||
move |username, password| {
|
move |username, password| {
|
||||||
|
debug!(target: "ckgreeter::callbacks::login", "----- Login callback called");
|
||||||
let tiles = tiles.clone();
|
let tiles = tiles.clone();
|
||||||
let display_weak = display_weak.clone();
|
let display_weak = display_weak.clone();
|
||||||
|
debug!(target: "ckgreeter::callbacks::login", "spawning slint thread");
|
||||||
slint::spawn_local(async move {
|
slint::spawn_local(async move {
|
||||||
|
debug!(target: "ckgreeter::callbacks::login", "spawned slint thread");
|
||||||
let display = display_weak.unwrap();
|
let display = display_weak.unwrap();
|
||||||
|
|
||||||
let username = username.to_string();
|
let username = username.to_string();
|
||||||
let password = password.to_string();
|
let password = password.to_string();
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::login", "username: {username:?}");
|
||||||
|
|
||||||
let index = display.get_selected_index();
|
let index = display.get_selected_index();
|
||||||
let tile = &tiles.row_data(index as usize);
|
let tile = &tiles.row_data(index as usize);
|
||||||
let tile = match tile {
|
let tile = match tile {
|
||||||
Some(tile) => {tile}
|
Some(tile) => tile,
|
||||||
None => {
|
None => {
|
||||||
display.invoke_set_error("Failed to login: Invalid index for tile".into());
|
display.invoke_set_error("Failed to login: Invalid index for tile".into());
|
||||||
|
error!("Failed to login: Invalid index for tile: {index}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match login(
|
match login(username.as_str(), password.as_str(), tile.command.as_str()).await {
|
||||||
username.as_str(),
|
|
||||||
password.as_str(),
|
|
||||||
tile.command.as_str(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(successful) => {
|
Ok(successful) => {
|
||||||
if !successful {
|
if !successful {
|
||||||
display.invoke_set_error("Failed to login: Bad Login".into());
|
display.invoke_set_error("Failed to login: Bad Login".into());
|
||||||
|
error!("Failed to login: Bad Login");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = slint::quit_event_loop() {
|
if let Err(err) = slint::quit_event_loop() {
|
||||||
display.invoke_set_error(format!("Failed to exit greeter: {err}").into());
|
display
|
||||||
|
.invoke_set_error(format!("Failed to exit greeter: {err}").into());
|
||||||
|
error!("Failed to exit greeter: {err}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
display.invoke_set_error(format!("Failed to login: {}", err).into());
|
display.invoke_set_error(format!("Failed to login: {err}").into());
|
||||||
|
error!("Failed to login: {err}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -75,11 +85,7 @@ pub fn register_login_callback(data: &GreeterData, display_weak: Weak<GreeterDis
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async fn login(
|
async fn login(username: &str, password: &str, cmd: &str) -> anyhow::Result<bool> {
|
||||||
username: &str,
|
|
||||||
password: &str,
|
|
||||||
cmd: &str,
|
|
||||||
) -> anyhow::Result<bool> {
|
|
||||||
if username.trim().is_empty() {
|
if username.trim().is_empty() {
|
||||||
return Err(anyhow::anyhow!("Username is required"));
|
return Err(anyhow::anyhow!("Username is required"));
|
||||||
}
|
}
|
||||||
@ -88,12 +94,21 @@ pub fn register_login_callback(data: &GreeterData, display_weak: Weak<GreeterDis
|
|||||||
return Err(anyhow::anyhow!("Command is required"));
|
return Err(anyhow::anyhow!("Command is required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let socket = std::env::var("GREETD_SOCK")
|
debug!(target: "ckgreeter::callbacks::login", "connecting to greetd socket");
|
||||||
.context("GREETD_SOCK is not set")?;
|
|
||||||
|
|
||||||
let mut stream = UnixStream::connect(socket).await.context("failed to connect to greetd socket")?;
|
let socket = std::env::var("GREETD_SOCK").context("GREETD_SOCK is not set")?;
|
||||||
|
|
||||||
|
let mut stream = UnixStream::connect(socket)
|
||||||
|
.await
|
||||||
|
.context("failed to connect to greetd socket")?;
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::login", "connected to greetd socket");
|
||||||
|
|
||||||
|
let mut next_request = Request::CreateSession {
|
||||||
|
username: username.to_string(),
|
||||||
|
};
|
||||||
|
trace!(target: "ckgreeter::callbacks::login", "sending request to create session: {next_request:?}");
|
||||||
|
|
||||||
let mut next_request = Request::CreateSession { username: username.to_string() };
|
|
||||||
let mut starting = false;
|
let mut starting = false;
|
||||||
loop {
|
loop {
|
||||||
next_request.write_to(&mut stream).await?;
|
next_request.write_to(&mut stream).await?;
|
||||||
@ -116,13 +131,16 @@ pub fn register_login_callback(data: &GreeterData, display_weak: Weak<GreeterDis
|
|||||||
AuthMessageType::Error => None,
|
AuthMessageType::Error => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trace!(target: "ckgreeter::callbacks::login", "sending request: {next_request:?}");
|
||||||
next_request = Request::PostAuthMessageResponse { response };
|
next_request = Request::PostAuthMessageResponse { response };
|
||||||
}
|
}
|
||||||
Response::Success => {
|
Response::Success => {
|
||||||
if starting {
|
if starting {
|
||||||
|
debug!(target: "ckgreeter::callbacks::login", "starting session");
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
} else {
|
} else {
|
||||||
starting = true;
|
starting = true;
|
||||||
|
trace!(target: "ckgreeter::callbacks::login", "sending request: {next_request:?}");
|
||||||
next_request = Request::StartSession {
|
next_request = Request::StartSession {
|
||||||
env: vec![],
|
env: vec![],
|
||||||
cmd: vec![cmd.to_string()],
|
cmd: vec![cmd.to_string()],
|
||||||
@ -133,6 +151,7 @@ pub fn register_login_callback(data: &GreeterData, display_weak: Weak<GreeterDis
|
|||||||
error_type,
|
error_type,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
debug!(target: "ckgreeter::callbacks::login", "error logging in");
|
||||||
Request::CancelSession.write_to(&mut stream).await?;
|
Request::CancelSession.write_to(&mut stream).await?;
|
||||||
return match error_type {
|
return match error_type {
|
||||||
ErrorType::AuthError => Ok(false),
|
ErrorType::AuthError => Ok(false),
|
||||||
@ -145,9 +164,8 @@ pub fn register_login_callback(data: &GreeterData, display_weak: Weak<GreeterDis
|
|||||||
}
|
}
|
||||||
pub fn register_power_callback(_data: &GreeterData) {
|
pub fn register_power_callback(_data: &GreeterData) {
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
_data.on_loginctl(move |
|
_data.on_loginctl(move |command: SharedString| {
|
||||||
command: SharedString
|
debug!(target: "ckgreeter::callbacks::power", "----- Power callback called");
|
||||||
| {
|
|
||||||
slint::spawn_local(async move {
|
slint::spawn_local(async move {
|
||||||
if let Err(err) = handle_power_command(command.as_str()).await {
|
if let Err(err) = handle_power_command(command.as_str()).await {
|
||||||
eprintln!("Power command failed: {err}");
|
eprintln!("Power command failed: {err}");
|
||||||
@ -158,9 +176,16 @@ pub fn register_power_callback(_data: &GreeterData) {
|
|||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
async fn handle_power_command(command: &str) -> anyhow::Result<()> {
|
async fn handle_power_command(command: &str) -> anyhow::Result<()> {
|
||||||
let connection = Connection::system().await?;
|
debug!(target: "ckgreeter::callbacks::power", "command: {command:?}");
|
||||||
let proxy = PowerManagerProxy::new(&connection).await?;
|
|
||||||
|
|
||||||
|
trace!(target: "ckgreeter::callbacks::power", "connecting to dbus");
|
||||||
|
let connection = Connection::system().await?;
|
||||||
|
trace!(target: "ckgreeter::callbacks::power", "connected to dbus");
|
||||||
|
trace!(target: "ckgreeter::callbacks::power", "getting power manager proxy");
|
||||||
|
let proxy = PowerManagerProxy::new(&connection).await?;
|
||||||
|
trace!(target: "ckgreeter::callbacks::power", "got power manager proxy");
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::power", "running power command: {command}");
|
||||||
match command {
|
match command {
|
||||||
"suspend" => proxy.suspend(&false).await?,
|
"suspend" => proxy.suspend(&false).await?,
|
||||||
"poweroff" => proxy.power_off(&false).await?,
|
"poweroff" => proxy.power_off(&false).await?,
|
||||||
@ -189,34 +214,43 @@ pub fn register_lock_state_callback(data: &GreeterData, data_weak: Weak<GreeterD
|
|||||||
move || {
|
move || {
|
||||||
let data_weak = data_weak.clone();
|
let data_weak = data_weak.clone();
|
||||||
slint::spawn_local(async move {
|
slint::spawn_local(async move {
|
||||||
|
debug!(target: "ckgreeter::callbacks::lock_state", "----- lock state callback called");
|
||||||
let data = data_weak.unwrap();
|
let data = data_weak.unwrap();
|
||||||
if let Some(states) = KeyboardLockStates::get_locks_state() {
|
if let Some(states) = KeyboardLockStates::get_locks_state() {
|
||||||
data.set_caps_lock_state(states.caps_lock);
|
data.set_caps_lock_state(states.caps_lock);
|
||||||
|
trace!(target: "ckgreeter::callbacks::lock_state", "caps lock state: {}", states.caps_lock);
|
||||||
data.set_num_lock_state(states.num_lock);
|
data.set_num_lock_state(states.num_lock);
|
||||||
|
trace!(target: "ckgreeter::callbacks::lock_state", "num lock state: {}", states.num_lock);
|
||||||
data.set_scroll_lock_state(states.scroll_lock);
|
data.set_scroll_lock_state(states.scroll_lock);
|
||||||
|
trace!(target: "ckgreeter::callbacks::lock_state", "scroll lock state: {}", states.scroll_lock)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<GreeterDisplay>, data_weak: Weak<GreeterData<'static>>, cache_dir: PathBuf, config: &Config) { //TODO: Seperate this out into seperate methods, cleanup!!!
|
pub fn register_wallpaper_callback(
|
||||||
const MAX_RECENT_WALLPAPERS: usize = 10;
|
data: &GreeterData,
|
||||||
|
display_weak: Weak<GreeterDisplay>,
|
||||||
|
data_weak: Weak<GreeterData<'static>>,
|
||||||
|
cache_dir: PathBuf,
|
||||||
|
config: &Config,
|
||||||
|
) {
|
||||||
|
//TODO: Seperate this out into seperate methods, cleanup!!!
|
||||||
|
const MAX_RECENT_WALLPAPERS: usize = 25;
|
||||||
|
|
||||||
async fn read_lines<P>(filename: P) -> std::io::Result<Lines<BufReader<File>>>
|
async fn read_lines<P>(filename: P) -> std::io::Result<Lines<BufReader<File>>>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path> + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper::read_lines", "reading lines from file: {:?}", filename);
|
||||||
let file = File::open(filename).await?;
|
let file = File::open(filename).await?;
|
||||||
Ok(BufReader::new(file).lines())
|
Ok(BufReader::new(file).lines())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_lines<P>(
|
async fn write_lines<P>(filename: &P, lines: VecDeque<String>) -> std::io::Result<()>
|
||||||
filename: P,
|
|
||||||
lines: VecDeque<String>,
|
|
||||||
) -> std::io::Result<()>
|
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path> + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
|
|
||||||
@ -225,6 +259,7 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
contents.push('\n');
|
contents.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper::write_lines", "writing lines to file: {:?}", filename);
|
||||||
std::fs::write(filename, contents)
|
std::fs::write(filename, contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,26 +270,28 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
w: u32,
|
w: u32,
|
||||||
h: u32,
|
h: u32,
|
||||||
) {
|
) {
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "setting background image");
|
||||||
if !display.get_background_toggle() {
|
if !display.get_background_toggle() {
|
||||||
|
trace!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "setting background image 1");
|
||||||
let old = data.get_wallpaper_image();
|
let old = data.get_wallpaper_image();
|
||||||
data.set_wallpaper_image(Image::from_rgba8(SharedPixelBuffer::<
|
data.set_wallpaper_image(Image::from_rgba8(
|
||||||
Rgba8Pixel,
|
SharedPixelBuffer::<Rgba8Pixel>::clone_from_slice(pixels, w, h),
|
||||||
>::clone_from_slice(
|
));
|
||||||
pixels, w, h
|
trace!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "set background image 1");
|
||||||
)));
|
|
||||||
drop(old);
|
drop(old);
|
||||||
} else {
|
} else {
|
||||||
|
trace!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "setting background image 2");
|
||||||
let old = data.get_wallpaper_image_1();
|
let old = data.get_wallpaper_image_1();
|
||||||
data.set_wallpaper_image_1(Image::from_rgba8(SharedPixelBuffer::<
|
data.set_wallpaper_image_1(Image::from_rgba8(
|
||||||
Rgba8Pixel,
|
SharedPixelBuffer::<Rgba8Pixel>::clone_from_slice(pixels, w, h),
|
||||||
>::clone_from_slice(
|
));
|
||||||
pixels, w, h
|
trace!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "set background image 2");
|
||||||
)));
|
|
||||||
drop(old);
|
drop(old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn export_wand_rgba(wand: &MagickWand) -> anyhow::Result<(u32, u32, Vec<u8>)> {
|
fn export_wand_rgba(wand: &MagickWand) -> anyhow::Result<(u32, u32, Vec<u8>)> {
|
||||||
|
trace!(target: "ckgreeter::callbacks::wallpaper::export_wand_rgba", "exporting wand");
|
||||||
let pixels = wand
|
let pixels = wand
|
||||||
.export_image_pixels(
|
.export_image_pixels(
|
||||||
0,
|
0,
|
||||||
@ -265,6 +302,7 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
)
|
)
|
||||||
.ok_or_else(|| anyhow!("Failed to export image pixels"))?;
|
.ok_or_else(|| anyhow!("Failed to export image pixels"))?;
|
||||||
|
|
||||||
|
trace!(target: "ckgreeter::callbacks::wallpaper::export_wand_rgba", "exported wand");
|
||||||
Ok((
|
Ok((
|
||||||
wand.get_image_width() as u32,
|
wand.get_image_width() as u32,
|
||||||
wand.get_image_height() as u32,
|
wand.get_image_height() as u32,
|
||||||
@ -276,24 +314,31 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
.tls_backend_rustls()
|
.tls_backend_rustls()
|
||||||
.http2_prior_knowledge()
|
.http2_prior_knowledge()
|
||||||
.http2_keep_alive_timeout(std::time::Duration::from_secs(10))
|
.http2_keep_alive_timeout(std::time::Duration::from_secs(10))
|
||||||
.build() {
|
.build()
|
||||||
|
{
|
||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Failed to create reqwest client: {err}");
|
eprintln!("Failed to create reqwest client: {err}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "reqwest client created");
|
||||||
|
|
||||||
if let Some(pexels_api_key) = config.pexels_api_key.clone() {
|
if let Some(pexels_api_key) = config.pexels_api_key.clone() {
|
||||||
|
info!(target: "ckgreeter::callbacks::wallpaper", "Using pexels api");
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "pexels api key found");
|
||||||
let pexels_client = Arc::new(PexelsClient::new(pexels_api_key));
|
let pexels_client = Arc::new(PexelsClient::new(pexels_api_key));
|
||||||
let pexels_query = config
|
let pexels_query = config
|
||||||
.pexels_query
|
.pexels_query
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| String::from("nature"));
|
.unwrap_or_else(|| String::from("nature"));
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "pexels query: {pexels_query}");
|
||||||
|
info!(target: "ckgreeter::callbacks::wallpaper", "Registering wallpaper callback");
|
||||||
data.on_get_new_wallpaper({
|
data.on_get_new_wallpaper({
|
||||||
let data_weak = data_weak.clone();
|
let data_weak = data_weak.clone();
|
||||||
let display_weak = display_weak.clone();
|
let display_weak = display_weak.clone();
|
||||||
move || {
|
move || {
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "----- wallpaper callback called");
|
||||||
let pexels_client = pexels_client.clone();
|
let pexels_client = pexels_client.clone();
|
||||||
let reqwest_client = reqwest_client.clone();
|
let reqwest_client = reqwest_client.clone();
|
||||||
let cache_dir = cache_dir.clone();
|
let cache_dir = cache_dir.clone();
|
||||||
@ -302,66 +347,101 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
let display_weak = display_weak.clone();
|
let display_weak = display_weak.clone();
|
||||||
let data_weak = data_weak.clone();
|
let data_weak = data_weak.clone();
|
||||||
|
|
||||||
let get_wallpaper = {
|
slint::spawn_local(async move {
|
||||||
let data_weak = data_weak.clone();
|
trait ResultExt<T, E> {
|
||||||
let display_weak = display_weak.clone();
|
fn wpaper_err(self, data: &GreeterData) -> Result<T, E>;
|
||||||
async move || -> anyhow::Result<()> {
|
}
|
||||||
|
impl<T, E: Display> ResultExt<T, E> for Result<T, E> {
|
||||||
|
fn wpaper_err(self, data: &GreeterData) -> Result<T, E> {
|
||||||
|
self.inspect_err(|e| {
|
||||||
|
error!(target: "ckgreeter::callbacks::wallpaper", "Failed to get wallpaper: {e}");
|
||||||
|
data.set_wallpaper_author("Error".into());
|
||||||
|
data.set_wallpaper_alt(e.to_string().into());
|
||||||
|
data.set_wallpaper_url("Failed to get wallpaper".into());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let data = data_weak.unwrap();
|
let data = data_weak.unwrap();
|
||||||
let display = display_weak.unwrap();
|
let display = display_weak.unwrap();
|
||||||
|
|
||||||
let last_used_log = cache_dir.join("last_used_log.txt");
|
let last_used_log = cache_dir.join("last_used_log.txt");
|
||||||
if !tokio::fs::try_exists(&last_used_log).await? {
|
debug!(target: "ckgreeter::callbacks::wallpaper", "last used log: {:?}", last_used_log);
|
||||||
File::create(&last_used_log).await?;
|
if !tokio::fs::try_exists(&last_used_log).await.wpaper_err(&data)? {
|
||||||
|
File::create(&last_used_log).await.wpaper_err(&data)?;
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "created last used log");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut file_lines = tokio::time::timeout(std::time::Duration::from_secs(5), read_lines(&last_used_log)).await??;
|
let mut file_lines = tokio::time::timeout(
|
||||||
|
std::time::Duration::from_secs(5),
|
||||||
|
read_lines(&last_used_log),
|
||||||
|
)
|
||||||
|
.await.wpaper_err(&data)?.wpaper_err(&data)?;
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "read last used log");
|
||||||
|
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
let mut last_used_lines = VecDeque::with_capacity(MAX_RECENT_WALLPAPERS);
|
let mut last_used_lines = VecDeque::with_capacity(MAX_RECENT_WALLPAPERS);
|
||||||
while let Some(line) = file_lines.next_line().await? {
|
while let Some(line) = file_lines.next_line().await.wpaper_err(&data)? {
|
||||||
last_used_lines.push_back(line);
|
last_used_lines.push_back(line);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
if idx >= MAX_RECENT_WALLPAPERS {
|
if idx > MAX_RECENT_WALLPAPERS {
|
||||||
break;
|
last_used_lines.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drop(file_lines);
|
drop(file_lines);
|
||||||
|
|
||||||
|
let mut page = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "fetched photos list from pexels api");
|
||||||
|
let mut is_photo_set: bool = false;
|
||||||
|
let mut found_photo: bool = false;
|
||||||
match pexels_client
|
match pexels_client
|
||||||
.search_photos(
|
.search_photos(
|
||||||
pexels_query.as_str(),
|
pexels_query.as_str(),
|
||||||
&SearchParams::new()
|
&SearchParams::new()
|
||||||
.page(1)
|
.page(page)
|
||||||
.per_page(11)
|
.per_page(10)
|
||||||
.size(pexels_api::Size::Medium)
|
.size(pexels_api::Size::Medium)
|
||||||
.orientation(pexels_api::Orientation::Landscape)
|
.orientation(pexels_api::Orientation::Landscape),
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
Ok(photos) => {
|
Ok(photos) => {
|
||||||
if photos.total_results > 0 {
|
if photos.total_results > 0 {
|
||||||
|
found_photo = true;
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "got photos from pexels api");
|
||||||
for photo in photos.photos {
|
for photo in photos.photos {
|
||||||
let string_id = photo.id.to_string();
|
let string_id = photo.id.to_string();
|
||||||
|
trace!(target: "ckgreeter::callbacks::wallpaper", "pexels photo id: {}", string_id);
|
||||||
if last_used_lines.contains(&string_id) {
|
if last_used_lines.contains(&string_id) {
|
||||||
|
trace!(target: "ckgreeter::callbacks::wallpaper", "pexels photo id already used recently");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
is_photo_set = true;
|
||||||
|
|
||||||
last_used_lines.push_back(string_id);
|
last_used_lines.push_back(string_id);
|
||||||
if last_used_lines.len() > MAX_RECENT_WALLPAPERS {
|
if last_used_lines.len() > MAX_RECENT_WALLPAPERS {
|
||||||
last_used_lines.pop_front();
|
last_used_lines.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
let image_path =
|
let image_path = cache_dir.join(format!("{}.png", photo.id));
|
||||||
cache_dir.join(format!("{}.png", photo.id));
|
trace!(target: "ckgreeter::callbacks::wallpaper", "pexels photo image path: {:?}", image_path);
|
||||||
|
|
||||||
let _ = if !tokio::fs::try_exists(&image_path).await? {
|
let _ = if !tokio::fs::try_exists(&image_path).await.wpaper_err(&data)? {
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "pexels photo image not found, downloading");
|
||||||
let response = reqwest_client
|
let response = reqwest_client
|
||||||
.get(photo.src.large2x.clone()) // TODO: determine which photo src size is best, maybe determine based on the monitor size to reduce memory pressure
|
.get(photo.src.large2x.clone()) // TODO: determine which photo src size is best, maybe determine based on the monitor size to reduce memory pressure
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await.wpaper_err(&data)?;
|
||||||
let image_bytes = response.bytes().await?;
|
let image_bytes = response.bytes().await.wpaper_err(&data)?;
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "pexels photo image downloaded");
|
||||||
|
|
||||||
let size = display.window().size();
|
let size = display.window().size();
|
||||||
|
|
||||||
let (w, h, pixels) = tokio::task::spawn_blocking(move || {
|
debug!(target: "ckgreeter::callbacks::wallpaper", "resizing image with ImageMagick");
|
||||||
|
let (w, h, pixels) =
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
let wand = MagickWand::new();
|
let wand = MagickWand::new();
|
||||||
|
|
||||||
wand.read_image_blob(&image_bytes)?;
|
wand.read_image_blob(&image_bytes)?;
|
||||||
@ -379,12 +459,16 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace!(target: "ckgreeter::callbacks::wallpaper", "exporting png data");
|
||||||
let png_data = wand.write_image_blob("png")?;
|
let png_data = wand.write_image_blob("png")?;
|
||||||
std::fs::write(image_path, png_data)?;
|
std::fs::write(image_path, png_data)?;
|
||||||
|
trace!(target: "ckgreeter::callbacks::wallpaper", "exported png data to disk");
|
||||||
|
|
||||||
export_wand_rgba(&wand)
|
export_wand_rgba(&wand)
|
||||||
}).await??;
|
})
|
||||||
|
.await.wpaper_err(&data)?.wpaper_err(&data)?;
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "image resized with ImageMagick");
|
||||||
set_background_image(
|
set_background_image(
|
||||||
&display,
|
&display,
|
||||||
&data,
|
&data,
|
||||||
@ -392,24 +476,24 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
);
|
);
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "set background image");
|
||||||
|
|
||||||
drop(pixels);
|
drop(pixels);
|
||||||
|
|
||||||
data.set_wallpaper_author(photo.photographer.into());
|
|
||||||
data.set_wallpaper_alt(photo.alt.unwrap_or("".into()).into());
|
|
||||||
data.set_wallpaper_url(photo.url.into());
|
|
||||||
|
|
||||||
display.invoke_switch_background();
|
|
||||||
} else {
|
} else {
|
||||||
let contents = tokio::fs::read(image_path).await?;
|
debug!(target: "ckgreeter::callbacks::wallpaper", "pexels photo image found, using cached");
|
||||||
|
let contents = tokio::fs::read(image_path).await.wpaper_err(&data)?;
|
||||||
|
|
||||||
let (w, h, pixels) = tokio::task::spawn_blocking(move || {
|
debug!(target: "ckgreeter::callbacks::wallpaper", "resizing image with ImageMagick");
|
||||||
|
let (w, h, pixels) =
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
let wand = MagickWand::new();
|
let wand = MagickWand::new();
|
||||||
|
|
||||||
wand.read_image_blob(contents.as_slice())?;
|
wand.read_image_blob(contents.as_slice())?;
|
||||||
export_wand_rgba(&wand)
|
export_wand_rgba(&wand)
|
||||||
}).await??;
|
})
|
||||||
|
.await.wpaper_err(&data)?.wpaper_err(&data)?;
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "image resized with ImageMagick");
|
||||||
set_background_image(
|
set_background_image(
|
||||||
&display,
|
&display,
|
||||||
&data,
|
&data,
|
||||||
@ -418,37 +502,47 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
h,
|
h,
|
||||||
);
|
);
|
||||||
|
|
||||||
drop(pixels);
|
debug!(target: "ckgreeter::callbacks::wallpaper", "set background image");
|
||||||
|
|
||||||
|
drop(pixels);
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "set pexels photo author: {}", photo.photographer);
|
||||||
data.set_wallpaper_author(photo.photographer.into());
|
data.set_wallpaper_author(photo.photographer.into());
|
||||||
data.set_wallpaper_alt(photo.alt.unwrap_or("".into()).into());
|
let photo_alt = photo.alt.unwrap_or("".into());
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "set pexels photo alt: {}", photo_alt);
|
||||||
|
data.set_wallpaper_alt(
|
||||||
|
photo_alt.into(),
|
||||||
|
);
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "set pexels photo url: {}", photo.url);
|
||||||
data.set_wallpaper_url(photo.url.into());
|
data.set_wallpaper_url(photo.url.into());
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "switching background to foreground");
|
||||||
display.invoke_switch_background();
|
display.invoke_switch_background();
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::time::timeout(
|
|
||||||
std::time::Duration::from_secs(5),
|
|
||||||
write_lines(&last_used_log, last_used_lines),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.context("timed out while writing wallpaper log")??;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Failed to fetch online images, using cached. {err}");
|
error!("Failed to fetch online images, using cached. {err}");
|
||||||
let mut photos = ReadDirStream::new(tokio::fs::read_dir(cache_dir).await?).filter(|de| match de {
|
debug!(target: "ckgreeter::callbacks::wallpaper", "getting cached wallpaper images from disk");
|
||||||
|
let mut photos =
|
||||||
|
ReadDirStream::new(tokio::fs::read_dir(&cache_dir).await.wpaper_err(&data)?)
|
||||||
|
.filter(|de| match de {
|
||||||
Ok(de) => {
|
Ok(de) => {
|
||||||
de.path().is_file()
|
de.path().is_file()
|
||||||
&& de.path().extension()
|
&& de.path().extension()
|
||||||
== Some(std::ffi::OsStr::new("png"))
|
== Some(std::ffi::OsStr::new("png"))
|
||||||
}
|
}
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
}).collect::<Vec<_>>().await;
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "got cached wallpaper images from disk");
|
||||||
photos.shuffle(&mut rand::rng());
|
photos.shuffle(&mut rand::rng());
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "shuffled cached wallpaper images");
|
||||||
|
|
||||||
let mut photos = tokio_stream::iter(photos);
|
let mut photos = tokio_stream::iter(photos);
|
||||||
|
|
||||||
@ -456,9 +550,16 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
match photo {
|
match photo {
|
||||||
Ok(photo) => {
|
Ok(photo) => {
|
||||||
let photo_path = photo.path();
|
let photo_path = photo.path();
|
||||||
let string_id = photo_path.file_prefix().context("failed to get filename")?.to_string_lossy().to_string();
|
let string_id = photo_path
|
||||||
|
.file_prefix()
|
||||||
|
.context("failed to get filename").wpaper_err(&data)?
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
if last_used_lines.front().is_some_and(|last| last == &string_id) {
|
if last_used_lines
|
||||||
|
.front()
|
||||||
|
.is_some_and(|first| first == &string_id)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,12 +568,21 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
last_used_lines.pop_front();
|
last_used_lines.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (w, h, pixels) = tokio::task::spawn_blocking(move || {
|
debug!(target: "ckgreeter::callbacks::wallpaper", "using cached wallpaper image: {:?}", photo_path);
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "resizing image with ImageMagick");
|
||||||
|
let (w, h, pixels) =
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
let wand = MagickWand::new();
|
let wand = MagickWand::new();
|
||||||
|
|
||||||
wand.read_image(photo_path.to_str().context("failed to convert image path to UTF-8")?)?;
|
wand.read_image(photo_path.to_str().context(
|
||||||
|
"failed to convert image path to UTF-8",
|
||||||
|
)?)?;
|
||||||
export_wand_rgba(&wand)
|
export_wand_rgba(&wand)
|
||||||
}).await??;
|
})
|
||||||
|
.await.wpaper_err(&data)?.wpaper_err(&data)?;
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "image resized with ImageMagick");
|
||||||
|
|
||||||
set_background_image(
|
set_background_image(
|
||||||
&display,
|
&display,
|
||||||
@ -481,16 +591,19 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
);
|
);
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "set background image");
|
||||||
|
|
||||||
|
|
||||||
drop(pixels);
|
drop(pixels);
|
||||||
|
|
||||||
data.set_wallpaper_author("Unknown".into());
|
data.set_wallpaper_author("Unknown".into());
|
||||||
|
|
||||||
data.set_wallpaper_alt("".into());
|
data.set_wallpaper_alt("".into());
|
||||||
data.set_wallpaper_url("Cached wallpaper".into());
|
data.set_wallpaper_url("Cached wallpaper".into());
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks::wallpaper", "switching background to foreground");
|
||||||
display.invoke_switch_background();
|
display.invoke_switch_background();
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
@ -500,64 +613,36 @@ pub fn register_wallpaper_callback(data: &GreeterData, display_weak: Weak<Greete
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match slint::spawn_local({
|
if is_photo_set || !found_photo {
|
||||||
let data_weak = data_weak.clone();
|
break;
|
||||||
async move {
|
|
||||||
match get_wallpaper().await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
let data = data_weak.unwrap();
|
|
||||||
report_wallpaper_error(&data, e.to_string());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
let data = data_weak.unwrap();
|
|
||||||
report_wallpaper_error(&data, e.to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
page += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokio::time::timeout(
|
||||||
|
std::time::Duration::from_secs(5),
|
||||||
|
write_lines(&last_used_log, last_used_lines),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("timed out while writing wallpaper log").wpaper_err(&data)?.wpaper_err(&data)?;
|
||||||
|
return anyhow::Ok(());
|
||||||
|
}).unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fn report_wallpaper_error(data: &GreeterData, error: String) {
|
|
||||||
data.set_wallpaper_author("Error".into());
|
|
||||||
data.set_wallpaper_alt(error.into());
|
|
||||||
data.set_wallpaper_url("Failed to get wallpaper".into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn register_clean_inactive_wallpaper_callback(data: &GreeterData, display_weak: Weak<GreeterDisplay>, data_weak: Weak<GreeterData<'static>>) {
|
|
||||||
data.on_clear_inactive_wallpaper({
|
|
||||||
let display_weak = display_weak.clone();
|
|
||||||
let data_weak = data_weak.clone();
|
|
||||||
|
|
||||||
move || {
|
|
||||||
let display = display_weak.unwrap();
|
|
||||||
let data = data_weak.unwrap();
|
|
||||||
|
|
||||||
if !display.get_background_toggle() {
|
|
||||||
let old = data.get_wallpaper_image();
|
|
||||||
data.set_wallpaper_image(Image::default());
|
|
||||||
drop(old);
|
|
||||||
} else {
|
|
||||||
let old = data.get_wallpaper_image_1();
|
|
||||||
data.set_wallpaper_image_1(Image::default());
|
|
||||||
drop(old);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn register_get_default_font_size(data: &GreeterData, display_weak: Weak<GreeterDisplay>) {
|
pub fn register_get_default_font_size(data: &GreeterData, display_weak: Weak<GreeterDisplay>) {
|
||||||
data.on_get_default_font_size(move || {
|
data.on_get_default_font_size(move || {
|
||||||
|
debug!(target: "ckgreeter::callbacks::font_size", "----- get default font size callback called");
|
||||||
let display = display_weak.unwrap();
|
let display = display_weak.unwrap();
|
||||||
let size = display.window().size();
|
let size = display.window().size();
|
||||||
((size.width as f32).min(size.height as f32)) * 0.01
|
trace!(target: "ckgreeter::callbacks::font_size", "window size: {:?}", size);
|
||||||
|
let font_size = ((size.width as f32).min(size.height as f32)) * 0.01;
|
||||||
|
trace!(target: "ckgreeter::callbacks::font_size", "font size: {}", font_size);
|
||||||
|
font_size
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,10 +656,21 @@ pub fn register_callbacks(
|
|||||||
let display_weak = display.as_weak();
|
let display_weak = display.as_weak();
|
||||||
let data_weak = data.as_weak();
|
let data_weak = data.as_weak();
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::callbacks", "Registering callbacks");
|
||||||
register_login_callback(data, display_weak.clone(), tiles);
|
register_login_callback(data, display_weak.clone(), tiles);
|
||||||
|
debug!(target: "ckgreeter::callbacks", "Registered login callback");
|
||||||
register_power_callback(data);
|
register_power_callback(data);
|
||||||
|
debug!(target: "ckgreeter::callbacks", "Registered power callback");
|
||||||
register_lock_state_callback(data, data_weak.clone());
|
register_lock_state_callback(data, data_weak.clone());
|
||||||
register_wallpaper_callback(data, display_weak.clone(), data_weak.clone(), cache_dir, config);
|
debug!(target: "ckgreeter::callbacks", "Registered lock state callback");
|
||||||
register_clean_inactive_wallpaper_callback(data, display_weak.clone(), data_weak.clone());
|
register_wallpaper_callback(
|
||||||
|
data,
|
||||||
|
display_weak.clone(),
|
||||||
|
data_weak.clone(),
|
||||||
|
cache_dir,
|
||||||
|
config,
|
||||||
|
);
|
||||||
|
debug!(target: "ckgreeter::callbacks", "Registered clean inactive wallpaper callback");
|
||||||
register_get_default_font_size(data, display_weak.clone());
|
register_get_default_font_size(data, display_weak.clone());
|
||||||
|
debug!(target: "ckgreeter::callbacks", "Registered get default font size callback");
|
||||||
}
|
}
|
||||||
@ -2,11 +2,12 @@ use std::collections::HashMap;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use i_slint_core::api::{Image, ToSharedString};
|
use i_slint_core::api::{Image, ToSharedString};
|
||||||
|
use log::{debug, trace};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_aux::prelude::*;
|
use serde_aux::prelude::*;
|
||||||
use crate::LoginOptionTileData;
|
use crate::LoginOptionTileData;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct LoginOptionTileConfig {
|
pub struct LoginOptionTileConfig {
|
||||||
pub(crate) image_path: PathBuf,
|
pub(crate) image_path: PathBuf,
|
||||||
pub(crate) command: String,
|
pub(crate) command: String,
|
||||||
@ -26,6 +27,7 @@ pub fn default_cache_dir() -> PathBuf {
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub(crate) default_username: Option<String>,
|
pub(crate) default_username: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
pub(crate) environments: HashMap<String, LoginOptionTileConfig>,
|
pub(crate) environments: HashMap<String, LoginOptionTileConfig>,
|
||||||
pub(crate) pexels_api_key: Option<String>,
|
pub(crate) pexels_api_key: Option<String>,
|
||||||
pub(crate) pexels_query: Option<String>,
|
pub(crate) pexels_query: Option<String>,
|
||||||
@ -37,11 +39,13 @@ pub struct Config {
|
|||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn get_tiles(&self) -> anyhow::Result<(Vec<LoginOptionTileData>, Option<usize>)> {
|
pub fn get_tiles(&self) -> anyhow::Result<(Vec<LoginOptionTileData>, Option<usize>)> {
|
||||||
|
debug!(target: "ckgreeter::config_get_tiles", "Getting tiles from config");
|
||||||
let mut ordered = self
|
let mut ordered = self
|
||||||
.environments
|
.environments
|
||||||
.iter()
|
.iter()
|
||||||
.collect::<Vec<(&String, &LoginOptionTileConfig)>>();
|
.collect::<Vec<(&String, &LoginOptionTileConfig)>>();
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::config_get_tiles", "Sorting tiles");
|
||||||
ordered.sort_by_key(|(name, env)| {
|
ordered.sort_by_key(|(name, env)| {
|
||||||
let index = if env.index >= 0 { env.index } else { i32::MAX };
|
let index = if env.index >= 0 { env.index } else { i32::MAX };
|
||||||
(index, name.as_str())
|
(index, name.as_str())
|
||||||
@ -53,6 +57,8 @@ impl Config {
|
|||||||
let mut seen_indexes = std::collections::HashSet::new();
|
let mut seen_indexes = std::collections::HashSet::new();
|
||||||
|
|
||||||
for (name, tile_config) in ordered {
|
for (name, tile_config) in ordered {
|
||||||
|
debug!(target: "ckgreeter::config_get_tiles", "Adding tile `{}`", name);
|
||||||
|
trace!(target: "ckgreeter::config_get_tiles", "Tile config: {:#?}", tile_config);
|
||||||
if tile_config.index >= 0 && !seen_indexes.insert(tile_config.index) {
|
if tile_config.index >= 0 && !seen_indexes.insert(tile_config.index) {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"Duplicate login option index `{}` found near `{}`",
|
"Duplicate login option index `{}` found near `{}`",
|
||||||
@ -64,6 +70,7 @@ impl Config {
|
|||||||
if default.is_some() {
|
if default.is_some() {
|
||||||
return Err(anyhow!("There can only be one default login option..."));
|
return Err(anyhow!("There can only be one default login option..."));
|
||||||
}
|
}
|
||||||
|
debug!(target: "ckgreeter::config_get_tiles", "Setting default tile to `{}`", name);
|
||||||
default = Some(tiles.len());
|
default = Some(tiles.len());
|
||||||
}
|
}
|
||||||
tiles.push(LoginOptionTileData {
|
tiles.push(LoginOptionTileData {
|
||||||
|
|||||||
65
src/main.rs
65
src/main.rs
@ -18,46 +18,82 @@ slint::include_modules!();
|
|||||||
|
|
||||||
static START: Once = Once::new();
|
static START: Once = Once::new();
|
||||||
|
|
||||||
|
use log::{debug, error, info};
|
||||||
|
use pretty_env_logger::env_logger::Target;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn start_debug_greetd_stub() {
|
fn start_debug_greetd_stub() {
|
||||||
use libgreetd_stub::SessionOptions;
|
use libgreetd_stub::SessionOptions;
|
||||||
|
|
||||||
let opts = SessionOptions {
|
let opts = SessionOptions {
|
||||||
username: std::env::var("CKGREETER_STUB_USER")
|
username: {
|
||||||
.unwrap_or_else(|_| "debug-user".to_string()),
|
std::env::var("CKGREETER_STUB_USER")
|
||||||
|
.unwrap_or_else(|_| "debug-user".to_string())
|
||||||
|
},
|
||||||
password: std::env::var("CKGREETER_STUB_PASSWORD")
|
password: std::env::var("CKGREETER_STUB_PASSWORD")
|
||||||
.unwrap_or_else(|_| "debug-password".to_string()),
|
.unwrap_or_else(|_| "debug-password".to_string()),
|
||||||
mfa: false,
|
mfa: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::init_greetd_stub", "Using username: {}", opts.username);
|
||||||
|
debug!(target: "ckgreeter::init_greetd_stub", "Using password: {}", opts.password);
|
||||||
|
debug!(target: "ckgreeter::init_greetd_stub", "Using MFA: {}", opts.mfa);
|
||||||
|
|
||||||
let socket_path = std::env::var("CKGREETER_STUB_SOCKET")
|
let socket_path = std::env::var("CKGREETER_STUB_SOCKET")
|
||||||
.unwrap_or_else(|_| "/tmp/greetd-stub.sock".to_string());
|
.unwrap_or_else(|_| "/tmp/greetd-stub.sock".to_string());
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::init_greetd_stub", "Using socket path: {}", socket_path);
|
||||||
|
|
||||||
let stub_socket_path = socket_path.clone();
|
let stub_socket_path = socket_path.clone();
|
||||||
let _stub_task = tokio::task::spawn(async move {
|
let _stub_task = tokio::task::spawn(async move {
|
||||||
libgreetd_stub::start(&stub_socket_path, &opts).await
|
libgreetd_stub::start(&stub_socket_path, &opts).await
|
||||||
});
|
});
|
||||||
|
debug!(target: "ckgreeter::init_greetd_stub", "Stub started");
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::init_greetd_stub", "GREETD_SOCK set to {}", socket_path);
|
||||||
unsafe {std::env::set_var("GREETD_SOCK", socket_path)};
|
unsafe {std::env::set_var("GREETD_SOCK", socket_path)};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pretty_env_logger::formatted_timed_builder()
|
||||||
|
.target(Target::Stdout)
|
||||||
|
.parse_filters("ckgreeter=debug")
|
||||||
|
.init();
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
pretty_env_logger::formatted_timed_builder()
|
||||||
|
.target(Target::Stdout)
|
||||||
|
.parse_filters("ckgreeter=info")
|
||||||
|
.init();
|
||||||
|
|
||||||
|
info!(target: "ckgreeter::init", "Initializing...");
|
||||||
START.call_once(|| {
|
START.call_once(|| {
|
||||||
magick_wand_genesis();
|
magick_wand_genesis();
|
||||||
});
|
});
|
||||||
|
info!(target: "ckgreeter::init", "ImageMagick initialized");
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
start_debug_greetd_stub();
|
start_debug_greetd_stub();
|
||||||
|
info!(target: "ckgreeter::init", "Debug greetd stub started");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const CONFIG_PATH: &str = "/etc/ckgreeter/config.toml";
|
const CONFIG_PATH: &str = "/etc/ckgreeter/config.toml";
|
||||||
|
|
||||||
let config_data = std::fs::read(CONFIG_PATH)
|
let config_path = std::env::var("CKGREETER_CONFIG").unwrap_or_else(|_| CONFIG_PATH.to_string());
|
||||||
.with_context(|| format!("Failed to read config file at {}", CONFIG_PATH))?;
|
|
||||||
|
info!(target: "ckgreeter::init", "Loading config from {}", config_path);
|
||||||
|
let config_data = std::fs::read(&config_path)
|
||||||
|
.with_context(|| format!("Failed to read config file at {}", config_path))?;
|
||||||
|
info!(target: "ckgreeter::init", "Config loaded");
|
||||||
let config: Config = toml::from_slice(&config_data)
|
let config: Config = toml::from_slice(&config_data)
|
||||||
.with_context(|| format!("Failed to parse config file at {}", CONFIG_PATH))?;
|
.with_context(|| format!("Failed to parse config file at {}", config_path))?;
|
||||||
|
debug!(target: "ckgreeter::init", "Config parsed");
|
||||||
|
|
||||||
let cache_dir = &config.background_cache_directory;
|
let cache_dir = &config.background_cache_directory;
|
||||||
|
info!(target: "ckgreeter::init", "Using cache directory: {}", cache_dir.display());
|
||||||
std::fs::create_dir_all(cache_dir)
|
std::fs::create_dir_all(cache_dir)
|
||||||
.with_context(|| format!("Failed to create cache directory: {}", cache_dir.display()))?;
|
.with_context(|| format!("Failed to create cache directory: {}", cache_dir.display()))?;
|
||||||
|
|
||||||
@ -66,14 +102,25 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let (tiles, default_tile) = config.get_tiles()?;
|
let (tiles, default_tile) = config.get_tiles()?;
|
||||||
|
|
||||||
|
if tiles.len() == 0 {
|
||||||
|
error!(target: "ckgreeter::init", "No tiles found in config");
|
||||||
|
return Err(anyhow::anyhow!("No tiles found in config"));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::init", "Got tiles from config");
|
||||||
|
|
||||||
if let Some(default_tile) = default_tile {
|
if let Some(default_tile) = default_tile {
|
||||||
|
debug!(target: "ckgreeter::init", "Default tile: '{}', index: {}", tiles[default_tile].name, default_tile);
|
||||||
data.set_selected_index(default_tile as i32);
|
data.set_selected_index(default_tile as i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tiles = Rc::new(VecModel::from(tiles));
|
let tiles = Rc::new(VecModel::from(tiles));
|
||||||
|
|
||||||
|
info!(target: "ckgreeter::init", "Registering callbacks and timers...");
|
||||||
register_callbacks(&display, &data, tiles.clone(), cache_dir.into(), &config);
|
register_callbacks(&display, &data, tiles.clone(), cache_dir.into(), &config);
|
||||||
|
info!(target: "ckgreeter::init", "Callbacks registered");
|
||||||
let _timers = register_timers(&display, &data, &config);
|
let _timers = register_timers(&display, &data, &config);
|
||||||
|
info!(target: "ckgreeter::init", "Timers registered");
|
||||||
|
|
||||||
match config.default_username {
|
match config.default_username {
|
||||||
Some(username) => {
|
Some(username) => {
|
||||||
@ -85,12 +132,20 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!(target: "ckgreeter::init", "Setting up display...");
|
||||||
|
|
||||||
display.set_tiles(ModelRc::from(tiles));
|
display.set_tiles(ModelRc::from(tiles));
|
||||||
|
debug!(target: "ckgreeter::init", "Tiles set");
|
||||||
|
|
||||||
|
|
||||||
data.set_wallpaper_cooldown(config.background_delay.max(5));
|
data.set_wallpaper_cooldown(config.background_delay.max(5));
|
||||||
|
debug!(target: "ckgreeter::init", "Wallpaper cooldown set");
|
||||||
data.invoke_get_new_wallpaper();
|
data.invoke_get_new_wallpaper();
|
||||||
|
debug!(target: "ckgreeter::init", "Wallpaper fetch invoked");
|
||||||
data.invoke_check_lock_states();
|
data.invoke_check_lock_states();
|
||||||
|
debug!(target: "ckgreeter::init", "Lock state check invoked");
|
||||||
|
|
||||||
|
info!(target: "ckgreeter::init", "Display setup complete");
|
||||||
display.show()?;
|
display.show()?;
|
||||||
tokio::task::block_in_place(slint::run_event_loop)?;
|
tokio::task::block_in_place(slint::run_event_loop)?;
|
||||||
display.hide()?;
|
display.hide()?;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
use i_slint_core::api::Global;
|
use i_slint_core::api::Global;
|
||||||
|
use log::debug;
|
||||||
use crate::{GreeterData, GreeterDisplay};
|
use crate::{GreeterData, GreeterDisplay};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ pub fn register_timers(
|
|||||||
data: &GreeterData,
|
data: &GreeterData,
|
||||||
_config: &Config,
|
_config: &Config,
|
||||||
) -> slint::Timer {
|
) -> slint::Timer {
|
||||||
|
debug!(target: "ckgreeter::timers", "Registering timers...");
|
||||||
let data_weak = data.as_weak();
|
let data_weak = data.as_weak();
|
||||||
|
|
||||||
let timer = slint::Timer::default();
|
let timer = slint::Timer::default();
|
||||||
@ -28,5 +30,7 @@ pub fn register_timers(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
debug!(target: "ckgreeter::timers", "Clock timer started");
|
||||||
|
|
||||||
timer
|
timer
|
||||||
}
|
}
|
||||||
@ -12,7 +12,6 @@ export global Data {
|
|||||||
callback check-lock-states();
|
callback check-lock-states();
|
||||||
|
|
||||||
callback get-new-wallpaper();
|
callback get-new-wallpaper();
|
||||||
callback clear-inactive-wallpaper();
|
|
||||||
|
|
||||||
in property <string> wallpaper-url;
|
in property <string> wallpaper-url;
|
||||||
|
|
||||||
|
|||||||
@ -45,9 +45,7 @@ export component GreeterDisplay inherits Window {
|
|||||||
public function switch_background() {
|
public function switch_background() {
|
||||||
background-toggle = !background-toggle;
|
background-toggle = !background-toggle;
|
||||||
background_timer.start();
|
background_timer.start();
|
||||||
clear_inactive_wallpaper_timer.start();
|
|
||||||
background_timer.restart();
|
background_timer.restart();
|
||||||
clear_inactive_wallpaper_timer.restart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
full-screen: true;
|
full-screen: true;
|
||||||
@ -85,15 +83,6 @@ export component GreeterDisplay inherits Window {
|
|||||||
self.running = false;
|
self.running = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_inactive_wallpaper_timer := Timer {
|
|
||||||
interval: 1200ms;
|
|
||||||
running: false;
|
|
||||||
triggered() => {
|
|
||||||
Data.clear-inactive-wallpaper();
|
|
||||||
self.running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background_1 := Image { // Background (secondary), needed for smooth opacity transitions
|
background_1 := Image { // Background (secondary), needed for smooth opacity transitions
|
||||||
@ -307,6 +296,7 @@ export component GreeterDisplay inherits Window {
|
|||||||
has_focus: environment_selector.has-focus;
|
has_focus: environment_selector.has-focus;
|
||||||
on_click => {
|
on_click => {
|
||||||
Data.selected_index = self.index;
|
Data.selected_index = self.index;
|
||||||
|
environment_selector.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user