Added logging Improved control flow Stopped blocking the goddamn runtime with my background code Got rid of a potential double free
677 lines
32 KiB
Rust
677 lines
32 KiB
Rust
use crate::config::Config;
|
|
use crate::input::KeyboardLockStates;
|
|
use crate::{GreeterData, GreeterDisplay, LoginOptionTileData};
|
|
use anyhow::{Context, anyhow};
|
|
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::model::{Model, VecModel};
|
|
use log::{debug, error, info, trace};
|
|
use magick_rust::MagickWand;
|
|
use pexels_api::{PexelsClient, SearchParams};
|
|
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::io::{AsyncBufReadExt, BufReader, Lines};
|
|
use tokio::net::UnixStream;
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
use slint::SharedString;
|
|
use tokio_stream::StreamExt;
|
|
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>>,
|
|
) {
|
|
data.on_login({
|
|
let tiles = tiles.clone();
|
|
let display_weak = display_weak.clone();
|
|
|
|
move |username, password| {
|
|
debug!(target: "ckgreeter::callbacks::login", "----- Login callback called");
|
|
let tiles = tiles.clone();
|
|
let display_weak = display_weak.clone();
|
|
debug!(target: "ckgreeter::callbacks::login", "spawning slint thread");
|
|
slint::spawn_local(async move {
|
|
debug!(target: "ckgreeter::callbacks::login", "spawned slint thread");
|
|
let display = display_weak.unwrap();
|
|
|
|
let username = username.to_string();
|
|
let password = password.to_string();
|
|
|
|
debug!(target: "ckgreeter::callbacks::login", "username: {username:?}");
|
|
|
|
let index = display.get_selected_index();
|
|
let tile = &tiles.row_data(index as usize);
|
|
let tile = match tile {
|
|
Some(tile) => tile,
|
|
None => {
|
|
display.invoke_set_error("Failed to login: Invalid index for tile".into());
|
|
error!("Failed to login: Invalid index for tile: {index}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
match login(username.as_str(), password.as_str(), tile.command.as_str()).await {
|
|
Ok(successful) => {
|
|
if !successful {
|
|
display.invoke_set_error("Failed to login: Bad Login".into());
|
|
error!("Failed to login: Bad Login");
|
|
return;
|
|
}
|
|
|
|
if let Err(err) = slint::quit_event_loop() {
|
|
display
|
|
.invoke_set_error(format!("Failed to exit greeter: {err}").into());
|
|
error!("Failed to exit greeter: {err}");
|
|
}
|
|
}
|
|
Err(err) => {
|
|
display.invoke_set_error(format!("Failed to login: {err}").into());
|
|
error!("Failed to login: {err}");
|
|
return;
|
|
}
|
|
};
|
|
})
|
|
.unwrap();
|
|
}
|
|
});
|
|
|
|
async fn login(username: &str, password: &str, cmd: &str) -> anyhow::Result<bool> {
|
|
if username.trim().is_empty() {
|
|
return Err(anyhow::anyhow!("Username is required"));
|
|
}
|
|
|
|
if cmd.trim().is_empty() {
|
|
return Err(anyhow::anyhow!("Command is required"));
|
|
}
|
|
|
|
debug!(target: "ckgreeter::callbacks::login", "connecting 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 starting = false;
|
|
loop {
|
|
next_request.write_to(&mut stream).await?;
|
|
|
|
match Response::read_from(&mut stream).await? {
|
|
Response::AuthMessage {
|
|
auth_message: _auth_message,
|
|
auth_message_type,
|
|
} => {
|
|
let response = match auth_message_type {
|
|
AuthMessageType::Visible => None,
|
|
AuthMessageType::Secret => {
|
|
if !password.is_empty() {
|
|
Some(password.to_string())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
AuthMessageType::Info => None,
|
|
AuthMessageType::Error => None,
|
|
};
|
|
|
|
trace!(target: "ckgreeter::callbacks::login", "sending request: {next_request:?}");
|
|
next_request = Request::PostAuthMessageResponse { response };
|
|
}
|
|
Response::Success => {
|
|
if starting {
|
|
debug!(target: "ckgreeter::callbacks::login", "starting session");
|
|
return Ok(true);
|
|
} else {
|
|
starting = true;
|
|
trace!(target: "ckgreeter::callbacks::login", "sending request: {next_request:?}");
|
|
next_request = Request::StartSession {
|
|
env: vec![],
|
|
cmd: vec![cmd.to_string()],
|
|
}
|
|
}
|
|
}
|
|
Response::Error {
|
|
error_type,
|
|
description,
|
|
} => {
|
|
debug!(target: "ckgreeter::callbacks::login", "error logging in");
|
|
Request::CancelSession.write_to(&mut stream).await?;
|
|
return match error_type {
|
|
ErrorType::AuthError => Ok(false),
|
|
ErrorType::Error => Err(anyhow::anyhow!("login error: {description:?}")),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pub fn register_power_callback(_data: &GreeterData) {
|
|
#[cfg(not(debug_assertions))]
|
|
_data.on_loginctl(move |command: SharedString| {
|
|
debug!(target: "ckgreeter::callbacks::power", "----- Power callback called");
|
|
slint::spawn_local(async move {
|
|
if let Err(err) = handle_power_command(command.as_str()).await {
|
|
eprintln!("Power command failed: {err}");
|
|
}
|
|
})
|
|
.unwrap();
|
|
});
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
async fn handle_power_command(command: &str) -> anyhow::Result<()> {
|
|
debug!(target: "ckgreeter::callbacks::power", "command: {command:?}");
|
|
|
|
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 {
|
|
"suspend" => proxy.suspend(&false).await?,
|
|
"poweroff" => proxy.power_off(&false).await?,
|
|
"reboot" => proxy.reboot(&false).await?,
|
|
command => return Err(anyhow::anyhow!("Unknown power command: {command}")),
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
#[proxy(
|
|
interface = "org.freedesktop.login1.Manager",
|
|
default_service = "org.freedesktop.login1",
|
|
default_path = "/org/freedesktop/login1"
|
|
)]
|
|
trait PowerManager {
|
|
fn suspend(&self, interactive: &bool) -> zbus::Result<()>;
|
|
fn reboot(&self, interactive: &bool) -> zbus::Result<()>;
|
|
fn power_off(&self, interactive: &bool) -> zbus::Result<()>;
|
|
}
|
|
}
|
|
pub fn register_lock_state_callback(data: &GreeterData, data_weak: Weak<GreeterData<'static>>) {
|
|
data.on_check_lock_states({
|
|
let data_weak = data_weak.clone();
|
|
move || {
|
|
let data_weak = data_weak.clone();
|
|
slint::spawn_local(async move {
|
|
debug!(target: "ckgreeter::callbacks::lock_state", "----- lock state callback called");
|
|
let data = data_weak.unwrap();
|
|
if let Some(states) = KeyboardLockStates::get_locks_state() {
|
|
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);
|
|
trace!(target: "ckgreeter::callbacks::lock_state", "num lock state: {}", states.num_lock);
|
|
data.set_scroll_lock_state(states.scroll_lock);
|
|
trace!(target: "ckgreeter::callbacks::lock_state", "scroll lock state: {}", states.scroll_lock)
|
|
}
|
|
})
|
|
.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!!!
|
|
const MAX_RECENT_WALLPAPERS: usize = 25;
|
|
|
|
async fn read_lines<P>(filename: P) -> std::io::Result<Lines<BufReader<File>>>
|
|
where
|
|
P: AsRef<Path> + std::fmt::Debug,
|
|
{
|
|
debug!(target: "ckgreeter::callbacks::wallpaper::read_lines", "reading lines from file: {:?}", filename);
|
|
let file = File::open(filename).await?;
|
|
Ok(BufReader::new(file).lines())
|
|
}
|
|
|
|
async fn write_lines<P>(filename: &P, lines: VecDeque<String>) -> std::io::Result<()>
|
|
where
|
|
P: AsRef<Path> + std::fmt::Debug,
|
|
{
|
|
let mut contents = String::new();
|
|
|
|
for line in lines {
|
|
contents.push_str(&line);
|
|
contents.push('\n');
|
|
}
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper::write_lines", "writing lines to file: {:?}", filename);
|
|
std::fs::write(filename, contents)
|
|
}
|
|
|
|
fn set_background_image(
|
|
display: &GreeterDisplay,
|
|
data: &GreeterData,
|
|
pixels: &[u8],
|
|
w: u32,
|
|
h: u32,
|
|
) {
|
|
debug!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "setting background image");
|
|
if !display.get_background_toggle() {
|
|
trace!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "setting background image 1");
|
|
let old = data.get_wallpaper_image();
|
|
data.set_wallpaper_image(Image::from_rgba8(
|
|
SharedPixelBuffer::<Rgba8Pixel>::clone_from_slice(pixels, w, h),
|
|
));
|
|
trace!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "set background image 1");
|
|
drop(old);
|
|
} else {
|
|
trace!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "setting background image 2");
|
|
let old = data.get_wallpaper_image_1();
|
|
data.set_wallpaper_image_1(Image::from_rgba8(
|
|
SharedPixelBuffer::<Rgba8Pixel>::clone_from_slice(pixels, w, h),
|
|
));
|
|
trace!(target: "ckgreeter::callbacks::wallpaper::set_background_image", "set background image 2");
|
|
drop(old);
|
|
}
|
|
}
|
|
|
|
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
|
|
.export_image_pixels(
|
|
0,
|
|
0,
|
|
wand.get_image_width(),
|
|
wand.get_image_height(),
|
|
"RGBA",
|
|
)
|
|
.ok_or_else(|| anyhow!("Failed to export image pixels"))?;
|
|
|
|
trace!(target: "ckgreeter::callbacks::wallpaper::export_wand_rgba", "exported wand");
|
|
Ok((
|
|
wand.get_image_width() as u32,
|
|
wand.get_image_height() as u32,
|
|
pixels,
|
|
))
|
|
}
|
|
|
|
let reqwest_client = match reqwest::ClientBuilder::new()
|
|
.tls_backend_rustls()
|
|
.http2_prior_knowledge()
|
|
.http2_keep_alive_timeout(std::time::Duration::from_secs(10))
|
|
.build()
|
|
{
|
|
Ok(client) => client,
|
|
Err(err) => {
|
|
eprintln!("Failed to create reqwest client: {err}");
|
|
return;
|
|
}
|
|
};
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "reqwest client created");
|
|
|
|
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_query = config
|
|
.pexels_query
|
|
.clone()
|
|
.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({
|
|
let data_weak = data_weak.clone();
|
|
let display_weak = display_weak.clone();
|
|
move || {
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "----- wallpaper callback called");
|
|
let pexels_client = pexels_client.clone();
|
|
let reqwest_client = reqwest_client.clone();
|
|
let cache_dir = cache_dir.clone();
|
|
let pexels_query = pexels_query.clone();
|
|
|
|
let display_weak = display_weak.clone();
|
|
let data_weak = data_weak.clone();
|
|
|
|
slint::spawn_local(async move {
|
|
trait ResultExt<T, E> {
|
|
fn wpaper_err(self, data: &GreeterData) -> Result<T, E>;
|
|
}
|
|
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 display = display_weak.unwrap();
|
|
|
|
let last_used_log = cache_dir.join("last_used_log.txt");
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "last used log: {:?}", last_used_log);
|
|
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.wpaper_err(&data)?.wpaper_err(&data)?;
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "read last used log");
|
|
|
|
let mut idx = 0;
|
|
let mut last_used_lines = VecDeque::with_capacity(MAX_RECENT_WALLPAPERS);
|
|
while let Some(line) = file_lines.next_line().await.wpaper_err(&data)? {
|
|
last_used_lines.push_back(line);
|
|
idx += 1;
|
|
if idx > MAX_RECENT_WALLPAPERS {
|
|
last_used_lines.pop_front();
|
|
}
|
|
}
|
|
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
|
|
.search_photos(
|
|
pexels_query.as_str(),
|
|
&SearchParams::new()
|
|
.page(page)
|
|
.per_page(10)
|
|
.size(pexels_api::Size::Medium)
|
|
.orientation(pexels_api::Orientation::Landscape),
|
|
)
|
|
.await
|
|
{
|
|
Ok(photos) => {
|
|
if photos.total_results > 0 {
|
|
found_photo = true;
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "got photos from pexels api");
|
|
for photo in photos.photos {
|
|
let string_id = photo.id.to_string();
|
|
trace!(target: "ckgreeter::callbacks::wallpaper", "pexels photo id: {}", string_id);
|
|
if last_used_lines.contains(&string_id) {
|
|
trace!(target: "ckgreeter::callbacks::wallpaper", "pexels photo id already used recently");
|
|
continue;
|
|
}
|
|
is_photo_set = true;
|
|
|
|
last_used_lines.push_back(string_id);
|
|
if last_used_lines.len() > MAX_RECENT_WALLPAPERS {
|
|
last_used_lines.pop_front();
|
|
}
|
|
|
|
let image_path = 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.wpaper_err(&data)? {
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "pexels photo image not found, downloading");
|
|
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
|
|
.send()
|
|
.await.wpaper_err(&data)?;
|
|
let image_bytes = response.bytes().await.wpaper_err(&data)?;
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "pexels photo image downloaded");
|
|
|
|
let size = display.window().size();
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "resizing image with ImageMagick");
|
|
let (w, h, pixels) =
|
|
tokio::task::spawn_blocking(move || {
|
|
let wand = MagickWand::new();
|
|
|
|
wand.read_image_blob(&image_bytes)?;
|
|
drop(image_bytes);
|
|
|
|
if size.width >= size.height {
|
|
wand.fit(
|
|
size.width as usize,
|
|
size.width as usize,
|
|
);
|
|
} else {
|
|
wand.fit(
|
|
size.height as usize,
|
|
size.height as usize,
|
|
);
|
|
}
|
|
|
|
trace!(target: "ckgreeter::callbacks::wallpaper", "exporting png data");
|
|
let png_data = wand.write_image_blob("png")?;
|
|
std::fs::write(image_path, png_data)?;
|
|
trace!(target: "ckgreeter::callbacks::wallpaper", "exported png data to disk");
|
|
|
|
export_wand_rgba(&wand)
|
|
})
|
|
.await.wpaper_err(&data)?.wpaper_err(&data)?;
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "image resized with ImageMagick");
|
|
set_background_image(
|
|
&display,
|
|
&data,
|
|
pixels.as_slice(),
|
|
w,
|
|
h,
|
|
);
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "set background image");
|
|
|
|
drop(pixels);
|
|
} else {
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "pexels photo image found, using cached");
|
|
let contents = tokio::fs::read(image_path).await.wpaper_err(&data)?;
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "resizing image with ImageMagick");
|
|
let (w, h, pixels) =
|
|
tokio::task::spawn_blocking(move || {
|
|
let wand = MagickWand::new();
|
|
|
|
wand.read_image_blob(contents.as_slice())?;
|
|
export_wand_rgba(&wand)
|
|
})
|
|
.await.wpaper_err(&data)?.wpaper_err(&data)?;
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "image resized with ImageMagick");
|
|
set_background_image(
|
|
&display,
|
|
&data,
|
|
pixels.as_slice(),
|
|
w,
|
|
h,
|
|
);
|
|
|
|
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());
|
|
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());
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "switching background to foreground");
|
|
display.invoke_switch_background();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Err(err) => {
|
|
error!("Failed to fetch online images, using cached. {err}");
|
|
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) => {
|
|
de.path().is_file()
|
|
&& de.path().extension()
|
|
== Some(std::ffi::OsStr::new("png"))
|
|
}
|
|
Err(_) => false,
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.await;
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "got cached wallpaper images from disk");
|
|
photos.shuffle(&mut rand::rng());
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "shuffled cached wallpaper images");
|
|
|
|
let mut photos = tokio_stream::iter(photos);
|
|
|
|
while let Some(photo) = photos.next().await {
|
|
match photo {
|
|
Ok(photo) => {
|
|
let photo_path = photo.path();
|
|
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(|first| first == &string_id)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
last_used_lines.push_back(string_id);
|
|
if last_used_lines.len() > MAX_RECENT_WALLPAPERS {
|
|
last_used_lines.pop_front();
|
|
}
|
|
|
|
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();
|
|
|
|
wand.read_image(photo_path.to_str().context(
|
|
"failed to convert image path to UTF-8",
|
|
)?)?;
|
|
export_wand_rgba(&wand)
|
|
})
|
|
.await.wpaper_err(&data)?.wpaper_err(&data)?;
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "image resized with ImageMagick");
|
|
|
|
set_background_image(
|
|
&display,
|
|
&data,
|
|
pixels.as_slice(),
|
|
w,
|
|
h,
|
|
);
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "set background image");
|
|
|
|
|
|
drop(pixels);
|
|
|
|
data.set_wallpaper_author("Unknown".into());
|
|
|
|
data.set_wallpaper_alt("".into());
|
|
data.set_wallpaper_url("Cached wallpaper".into());
|
|
|
|
debug!(target: "ckgreeter::callbacks::wallpaper", "switching background to foreground");
|
|
display.invoke_switch_background();
|
|
|
|
break;
|
|
}
|
|
Err(_err) => {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if is_photo_set || !found_photo {
|
|
break;
|
|
}
|
|
|
|
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();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn register_get_default_font_size(data: &GreeterData, display_weak: Weak<GreeterDisplay>) {
|
|
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 size = display.window().size();
|
|
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
|
|
});
|
|
}
|
|
|
|
pub fn register_callbacks(
|
|
display: &GreeterDisplay,
|
|
data: &GreeterData,
|
|
tiles: Rc<VecModel<LoginOptionTileData>>,
|
|
cache_dir: PathBuf,
|
|
config: &Config,
|
|
) {
|
|
let display_weak = display.as_weak();
|
|
let data_weak = data.as_weak();
|
|
|
|
debug!(target: "ckgreeter::callbacks", "Registering callbacks");
|
|
register_login_callback(data, display_weak.clone(), tiles);
|
|
debug!(target: "ckgreeter::callbacks", "Registered login callback");
|
|
register_power_callback(data);
|
|
debug!(target: "ckgreeter::callbacks", "Registered power callback");
|
|
register_lock_state_callback(data, data_weak.clone());
|
|
debug!(target: "ckgreeter::callbacks", "Registered lock state callback");
|
|
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());
|
|
debug!(target: "ckgreeter::callbacks", "Registered get default font size callback");
|
|
}
|