Fix compositor output routing and pointer hints; format imports

This commit is contained in:
Samuel Onoja committed 2026-09-30 06:39:58 +01:00
1 parent 88e4f3c135
commit 46509f880a
108 files changed
+3682 -8371

No files matched your search

+36 -84
View File
@@ -1,28 +1,22 @@
//! One decoded image and one texture per GPU context, not double-buffered
//! full-screen UI surfaces per monitor. Resize changes sampling only.
use crate::presentation::NativeTextureElement;
use std::cell::Cell;
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::mpsc;
use std::time::{Duration, Instant};
use serde::Deserialize;
use smithay::{
backend::{
allocator::Fourcc,
renderer::{
ErasedContextId, ImportMem, Renderer,
element::Id,
gles::{GlesRenderer, GlesTexture},
utils::CommitCounter,
},
},
output::Output,
utils::{Buffer, Physical, Rectangle, Size},
};
use std::{
cell::Cell,
collections::HashMap,
path::PathBuf,
rc::Rc,
sync::mpsc,
time::{Duration, Instant},
};
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::Id;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::backend::renderer::utils::CommitCounter;
use smithay::backend::renderer::{ErasedContextId, ImportMem, Renderer};
use smithay::output::Output;
use smithay::utils::{Buffer, Physical, Rectangle, Size};
use crate::presentation::NativeTextureElement;
const UPLOAD_RETRY_DELAY: Duration = Duration::from_secs(5);
@@ -80,10 +74,7 @@ impl WallpaperState {
Self::with_wakeup(config, None)
}
pub fn with_wakeup(
config: WallpaperConfig,
wakeup: Option<smithay::reexports::calloop::LoopSignal>,
) -> Self {
pub fn with_wakeup(config: WallpaperConfig, wakeup: Option<smithay::reexports::calloop::LoopSignal>) -> Self {
let retained = config.clone();
let (sender, receiver) = mpsc::channel();
let owned = config.path.as_ref().is_some_and(|path| path.is_file());
@@ -92,8 +83,7 @@ impl WallpaperState {
let wakeup = wakeup.clone();
std::thread::spawn(move || {
let load = || -> Result<image::RgbaImage, String> {
let (width, height) =
image::image_dimensions(&path).map_err(|e| e.to_string())?;
let (width, height) = image::image_dimensions(&path).map_err(|e| e.to_string())?;
if u64::from(width) * u64::from(height) * 4 > 256 * 1024 * 1024 {
return Err("wallpaper exceeds the 256 MiB decode limit".into());
}
@@ -164,9 +154,7 @@ impl WallpaperState {
}
fn upload_ready(&self, context: &ErasedContextId, now: Instant) -> bool {
self.upload_retries
.get(context)
.is_none_or(|retry| now >= *retry)
self.upload_retries.get(context).is_none_or(|retry| now >= *retry)
}
pub(crate) fn take_retry_wakeup(&self) -> bool {
@@ -250,11 +238,7 @@ impl WallpaperState {
}
}
pub fn element(
&mut self,
renderer: &mut GlesRenderer,
output: &Output,
) -> Option<NativeTextureElement> {
pub fn element(&mut self, renderer: &mut GlesRenderer, output: &Output) -> Option<NativeTextureElement> {
let pixels = self.pixels.as_ref()?;
let context = renderer.context_id().erased();
if !self.textures.contains_key(&context) {
@@ -265,32 +249,21 @@ impl WallpaperState {
match renderer.import_memory(pixels.as_raw(), Fourcc::Abgr8888, size, false) {
Ok(texture) => {
self.upload_retries.remove(&context);
tracing::debug!(
bytes = pixels.as_raw().len(),
"uploaded shared wallpaper texture"
);
self.textures.insert(
context.clone(),
WallpaperTexture {
texture,
id: Id::new(),
},
);
tracing::debug!(bytes = pixels.as_raw().len(), "uploaded shared wallpaper texture");
self.textures
.insert(context.clone(), WallpaperTexture { texture, id: Id::new() });
}
Err(error) => {
// Avoid retrying a large failed allocation every frame, but
// allow recovery from temporary GPU memory pressure.
self.upload_retries
.insert(context, Instant::now() + UPLOAD_RETRY_DELAY);
self.upload_retries.insert(context, Instant::now() + UPLOAD_RETRY_DELAY);
tracing::debug!(%error, "wallpaper texture import failed");
return None;
}
}
}
let cached = self.textures.get(&context)?;
let output_size = output
.current_transform()
.transform_size(output.current_mode()?.size);
let output_size = output.current_transform().transform_size(output.current_mode()?.size);
let (geometry, source) = image_geometry(
(pixels.width() as i32, pixels.height() as i32).into(),
output_size,
@@ -324,11 +297,7 @@ fn image_geometry(
(
Rectangle::from_size(output),
Rectangle::new(
(
(f64::from(image.w) - width) / 2.0,
(f64::from(image.h) - height) / 2.0,
)
.into(),
((f64::from(image.w) - width) / 2.0, (f64::from(image.h) - height) / 2.0).into(),
(width, height).into(),
),
)
@@ -341,10 +310,7 @@ fn image_geometry(
)
.into();
(
Rectangle::new(
((output.w - size.w) / 2, (output.h - size.h) / 2).into(),
size,
),
Rectangle::new(((output.w - size.w) / 2, (output.h - size.h) / 2).into(), size),
Rectangle::from_size(image.to_f64()),
)
}
@@ -373,9 +339,7 @@ mod tests {
state.arm_retry_timer(&event_loop.handle(), now).unwrap();
assert!(state.retry_timer_pending.get());
assert!(!state.take_retry_wakeup());
event_loop
.dispatch(Some(Duration::from_secs(1)), &mut ())
.unwrap();
event_loop.dispatch(Some(Duration::from_secs(1)), &mut ()).unwrap();
assert!(!state.retry_timer_pending.get());
assert!(
state.take_retry_wakeup(),
@@ -383,10 +347,7 @@ mod tests {
);
assert!(!state.take_retry_wakeup());
let after = Instant::now();
assert_eq!(
state.next_retry_deadline(after),
Some(after + UPLOAD_RETRY_DELAY)
);
assert_eq!(state.next_retry_deadline(after), Some(after + UPLOAD_RETRY_DELAY));
state.forget_context(&context);
assert_eq!(state.next_retry_deadline(after), None);
state.retry_wakeup.set(true);
@@ -398,7 +359,8 @@ mod tests {
#[test]
fn decoder_wakes_an_idle_event_loop() {
use smithay::reexports::calloop::{EventLoop, timer::Timer};
use smithay::reexports::calloop::EventLoop;
use smithay::reexports::calloop::timer::Timer;
let mut event_loop = EventLoop::<WallpaperState>::try_new().unwrap();
event_loop
.handle()
@@ -407,8 +369,7 @@ mod tests {
})
.unwrap();
let signal = event_loop.get_signal();
let mut state =
WallpaperState::with_wakeup(WallpaperConfig::default(), Some(signal.clone()));
let mut state = WallpaperState::with_wakeup(WallpaperConfig::default(), Some(signal.clone()));
event_loop
.run(None, &mut state, |state| {
if state.poll() {
@@ -429,18 +390,14 @@ mod tests {
mode: WallpaperMode::Fill,
});
let now = Instant::now();
state
.upload_retries
.insert(context.clone(), now + UPLOAD_RETRY_DELAY);
state.upload_retries.insert(context.clone(), now + UPLOAD_RETRY_DELAY);
assert!(!state.upload_ready(&context, now));
assert!(state.upload_ready(&other, now));
assert!(state.upload_ready(&context, now + UPLOAD_RETRY_DELAY));
state.forget_context(&context);
assert!(state.upload_ready(&context, now));
state
.upload_retries
.insert(context.clone(), now + UPLOAD_RETRY_DELAY);
state.upload_retries.insert(context.clone(), now + UPLOAD_RETRY_DELAY);
let (sender, receiver) = mpsc::channel();
state.receiver = Some(receiver);
sender.send(Ok(image::RgbaImage::new(2, 2))).unwrap();
@@ -489,16 +446,11 @@ mod tests {
#[test]
fn fill_crops_and_fit_letterboxes_without_reallocating_image() {
let (geometry, source) = image_geometry(
(3840, 2160).into(),
(1600, 1000).into(),
WallpaperMode::Fill,
);
let (geometry, source) = image_geometry((3840, 2160).into(), (1600, 1000).into(), WallpaperMode::Fill);
assert_eq!(geometry.size, (1600, 1000).into());
assert_eq!(source.size.h, 2160.0);
assert!(source.loc.x > 0.0);
let (geometry, source) =
image_geometry((3840, 2160).into(), (1600, 1000).into(), WallpaperMode::Fit);
let (geometry, source) = image_geometry((3840, 2160).into(), (1600, 1000).into(), WallpaperMode::Fit);
assert_eq!(geometry.size, (1600, 900).into());
assert_eq!(geometry.loc, (0, 50).into());
assert_eq!(source.size, (3840.0, 2160.0).into());