Add support for outputs with fractional scale factor
Hopefully this will help displaying the wallpapers correctly on outputs with fractional scale factor: https://github.com/gergo-salyi/multibg-sway/issues/5
This commit is contained in:
parent
d55a0ae932
commit
2711872a53
2 changed files with 192 additions and 93 deletions
12
src/main.rs
12
src/main.rs
|
@ -31,6 +31,8 @@ use smithay_client_toolkit::reexports::client::{
|
|||
backend::{ReadEventsGuard, WaylandError},
|
||||
globals::registry_queue_init,
|
||||
};
|
||||
use smithay_client_toolkit::reexports::protocols
|
||||
::wp::viewporter::client::wp_viewporter::WpViewporter;
|
||||
|
||||
use crate::{
|
||||
cli::Cli,
|
||||
|
@ -67,6 +69,11 @@ fn main()
|
|||
let layer_shell = LayerShell::bind(&globals, &qh).unwrap();
|
||||
let shm = Shm::bind(&globals, &qh).unwrap();
|
||||
|
||||
let registry_state = RegistryState::new(&globals);
|
||||
|
||||
let viewporter: WpViewporter = registry_state
|
||||
.bind_one(&qh, 1..=1, ()).expect("wp_viewporter not available");
|
||||
|
||||
// Sync tools for sway ipc tasks
|
||||
let mut poll = Poll::new().unwrap();
|
||||
let waker = Arc::new(Waker::new(poll.registry(), SWAY).unwrap());
|
||||
|
@ -74,10 +81,11 @@ fn main()
|
|||
|
||||
let mut state = State {
|
||||
compositor_state,
|
||||
registry_state: RegistryState::new(&globals),
|
||||
registry_state,
|
||||
output_state: OutputState::new(&globals, &qh),
|
||||
shm,
|
||||
layer_shell,
|
||||
viewporter,
|
||||
wallpaper_dir,
|
||||
pixel_format: None,
|
||||
background_layers: Vec::new(),
|
||||
|
@ -85,7 +93,7 @@ fn main()
|
|||
tx.clone(), Arc::clone(&waker)
|
||||
),
|
||||
brightness: cli.brightness.unwrap_or(0),
|
||||
contrast: cli.contrast.unwrap_or(0.0)
|
||||
contrast: cli.contrast.unwrap_or(0.0),
|
||||
};
|
||||
|
||||
event_queue.roundtrip(&mut state).unwrap();
|
||||
|
|
271
src/wayland.rs
271
src/wayland.rs
|
@ -1,4 +1,7 @@
|
|||
use std::{num::NonZeroU32, path::PathBuf};
|
||||
use std::{
|
||||
num::NonZeroU32,
|
||||
path::PathBuf
|
||||
};
|
||||
|
||||
use log::{debug, error, warn};
|
||||
use smithay_client_toolkit::{
|
||||
|
@ -21,8 +24,16 @@ use smithay_client_toolkit::{
|
|||
},
|
||||
};
|
||||
use smithay_client_toolkit::reexports::client::{
|
||||
Connection, QueueHandle,
|
||||
protocol::{wl_output, wl_shm, wl_surface},
|
||||
Connection, Dispatch, Proxy, QueueHandle,
|
||||
protocol::{
|
||||
wl_output::{self, WlOutput},
|
||||
wl_shm,
|
||||
wl_surface::WlSurface
|
||||
},
|
||||
};
|
||||
use smithay_client_toolkit::reexports::protocols::wp::viewporter::client::{
|
||||
wp_viewport::WpViewport,
|
||||
wp_viewporter::WpViewporter
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -36,6 +47,7 @@ pub struct State {
|
|||
pub output_state: OutputState,
|
||||
pub shm: Shm,
|
||||
pub layer_shell: LayerShell,
|
||||
pub viewporter: WpViewporter,
|
||||
pub wallpaper_dir: PathBuf,
|
||||
pub pixel_format: Option<wl_shm::Format>,
|
||||
pub background_layers: Vec<BackgroundLayer>,
|
||||
|
@ -69,7 +81,7 @@ impl CompositorHandler for State
|
|||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_surface: &WlSurface,
|
||||
_new_factor: i32,
|
||||
) {
|
||||
}
|
||||
|
@ -78,7 +90,7 @@ impl CompositorHandler for State
|
|||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_surface: &WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
}
|
||||
|
@ -87,7 +99,7 @@ impl CompositorHandler for State
|
|||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_surface: &WlSurface,
|
||||
_new_transform: wl_output::Transform,
|
||||
) {
|
||||
}
|
||||
|
@ -108,7 +120,7 @@ impl LayerShellHandler for State
|
|||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
layer: &LayerSurface,
|
||||
_configure: LayerSurfaceConfigure,
|
||||
configure: LayerSurfaceConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
// The new layer is ready: request all the visible workspace from sway,
|
||||
|
@ -122,8 +134,17 @@ impl LayerShellHandler for State
|
|||
.request_visible_workspace(&bg_layer.output_name);
|
||||
|
||||
debug!(
|
||||
"Background layer has been configured for output: {}",
|
||||
bg_layer.output_name
|
||||
"Configured layer on output: {}, new surface size {}x{}",
|
||||
bg_layer.output_name,
|
||||
configure.new_size.0, configure.new_size.1
|
||||
);
|
||||
}
|
||||
else {
|
||||
debug!(
|
||||
"Ignoring configure for already configured layer on output: {}, \
|
||||
new surface size {}x{}",
|
||||
bg_layer.output_name,
|
||||
configure.new_size.0, configure.new_size.1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +159,7 @@ impl OutputHandler for State {
|
|||
&mut self,
|
||||
_conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
output: wl_output::WlOutput,
|
||||
output: WlOutput,
|
||||
) {
|
||||
let Some(info) = self.output_state.info(&output)
|
||||
else {
|
||||
|
@ -163,39 +184,43 @@ impl OutputHandler for State {
|
|||
return;
|
||||
};
|
||||
|
||||
if !width.is_positive() {
|
||||
if !width.is_positive() || !height.is_positive() {
|
||||
error!(
|
||||
"New output '{}' has a non-positive width: {}, skipping",
|
||||
output_name,
|
||||
width
|
||||
"New output '{}' has non-positive resolution: {} x {}, skipping",
|
||||
output_name, width, height
|
||||
);
|
||||
return;
|
||||
}
|
||||
if !height.is_positive() {
|
||||
|
||||
let integer_scale_factor = info.scale_factor;
|
||||
|
||||
let Some((logical_width, logical_height)) = info.logical_size
|
||||
else {
|
||||
error!(
|
||||
"New output '{}' has a non-positive height: {}, skipping",
|
||||
output_name,
|
||||
height
|
||||
"New output '{}' has no logical_size, skipping",
|
||||
output_name
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if !logical_width.is_positive() || !logical_height.is_positive() {
|
||||
error!(
|
||||
"New output '{}' has non-positive logical size: {} x {}, skipping",
|
||||
output_name, logical_width, logical_height
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
debug!(
|
||||
"New output, name: {}, resolution: {}x{}, scale factor: {}",
|
||||
output_name, width, height, info.scale_factor
|
||||
"New output, name: {}, resolution: {}x{}, integer scale factor: {}, \
|
||||
logical size: {}x{}",
|
||||
output_name, width, height, integer_scale_factor,
|
||||
logical_width, logical_height
|
||||
);
|
||||
|
||||
let surface = self.compositor_state.create_surface(qh);
|
||||
|
||||
// We are a wallpaper and we never want to be scaled
|
||||
// by the compositor. So we declare that we are already
|
||||
// correctly scaled no matter the scale factor.
|
||||
// This will handle integer scale factors.
|
||||
surface.set_buffer_scale(info.scale_factor);
|
||||
|
||||
let layer = self.layer_shell.create_layer_surface(
|
||||
qh,
|
||||
surface,
|
||||
self.compositor_state.create_surface(qh),
|
||||
Layer::Background,
|
||||
layer_surface_name(&output_name),
|
||||
Some(&output)
|
||||
|
@ -203,10 +228,26 @@ impl OutputHandler for State {
|
|||
|
||||
layer.set_exclusive_zone(-1); // Don't let the status bar push it around
|
||||
layer.set_keyboard_interactivity(KeyboardInteractivity::None);
|
||||
layer.set_size(
|
||||
(width / info.scale_factor) as u32,
|
||||
(height / info.scale_factor) as u32
|
||||
);
|
||||
|
||||
let surface = layer.wl_surface();
|
||||
|
||||
let mut viewport = None;
|
||||
|
||||
if width == logical_width || height == logical_height {
|
||||
debug!("Output '{}' needs no scaling", output_name);
|
||||
}
|
||||
else if width == logical_width * integer_scale_factor
|
||||
&& height == logical_height * integer_scale_factor
|
||||
{
|
||||
debug!("Output '{}' needs integer scaling", output_name);
|
||||
surface.set_buffer_scale(integer_scale_factor);
|
||||
}
|
||||
else {
|
||||
debug!("Output '{}' needs fractional scaling", output_name);
|
||||
let new_viewport = self.viewporter.get_viewport(surface, qh, ());
|
||||
new_viewport.set_destination(logical_width, logical_height);
|
||||
viewport = Some(new_viewport);
|
||||
}
|
||||
|
||||
layer.commit();
|
||||
|
||||
|
@ -260,6 +301,7 @@ impl OutputHandler for State {
|
|||
configured: false,
|
||||
workspace_backgrounds,
|
||||
shm_slot_pool,
|
||||
viewport,
|
||||
});
|
||||
|
||||
debug!(
|
||||
|
@ -273,19 +315,16 @@ impl OutputHandler for State {
|
|||
fn update_output(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
output: wl_output::WlOutput,
|
||||
qh: &QueueHandle<Self>,
|
||||
output: WlOutput,
|
||||
) {
|
||||
// This will only be fully needed if we implement scaling the wallpapers
|
||||
// to the output resolution
|
||||
|
||||
let Some(info) = self.output_state.info(&output)
|
||||
else {
|
||||
error!("Updated output has no output info, skipping");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(name) = info.name
|
||||
let Some(output_name) = info.name
|
||||
else {
|
||||
error!("Updated output has no name, skipping");
|
||||
return;
|
||||
|
@ -296,66 +335,91 @@ impl OutputHandler for State {
|
|||
.map(|mode| mode.dimensions)
|
||||
else {
|
||||
error!(
|
||||
"New output '{}' has no current mode set, skipping",
|
||||
name
|
||||
"Updated output '{}' has no current mode set, skipping",
|
||||
output_name
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
debug!(
|
||||
"Update output, name: {}, resolution: {}x{}, scale factor: {}",
|
||||
name, width, height, info.scale_factor
|
||||
);
|
||||
let integer_scale_factor = info.scale_factor;
|
||||
|
||||
if let Some(bg_layer) = self.background_layers.iter()
|
||||
.find(|bg_layers| bg_layers.output_name == name)
|
||||
{
|
||||
let surface = bg_layer.layer.wl_surface();
|
||||
// We are a wallpaper and we never want to be scaled
|
||||
// by the compositor. So we declare that we are already
|
||||
// correctly scaled no matter the scale factor.
|
||||
// This will handle integer scale factors.
|
||||
surface.set_buffer_scale(info.scale_factor);
|
||||
bg_layer.layer.set_size(
|
||||
(width / info.scale_factor) as u32,
|
||||
(height / info.scale_factor) as u32
|
||||
let Some((logical_width, logical_height)) = info.logical_size
|
||||
else {
|
||||
error!(
|
||||
"Updated output '{}' has no logical_size, skipping",
|
||||
output_name
|
||||
);
|
||||
surface.commit();
|
||||
return;
|
||||
};
|
||||
|
||||
if !logical_width.is_positive() || !logical_height.is_positive() {
|
||||
error!(
|
||||
"Updated output '{}' has non-positive logical size: {} x {}, skipping",
|
||||
output_name, logical_width, logical_height
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
warn!("Handling of output updates are not yet fully implemented");
|
||||
debug!(
|
||||
"Updated output, name: {}, resolution: {}x{}, integer scale factor: {}, \
|
||||
logical size: {}x{}",
|
||||
output_name, width, height, integer_scale_factor,
|
||||
logical_width, logical_height
|
||||
);
|
||||
|
||||
// let Some((width, height)) = info.modes.iter()
|
||||
// .find(|mode| mode.current)
|
||||
// .map(|mode| mode.dimensions)
|
||||
// else {
|
||||
// error!(
|
||||
// "Updated output '{}' has no current mode set, skipping",
|
||||
// name
|
||||
// );
|
||||
// return;
|
||||
// };
|
||||
//
|
||||
// if let Some(bg_layer) = self.background_layers.iter()
|
||||
// .find(|bg_layers| bg_layers.output_name == name)
|
||||
// {
|
||||
// if bg_layer.width == width && bg_layer.height == height {
|
||||
// // if a known output has its resolution unchanged
|
||||
// // then ignore this event
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // renew the output otherwise
|
||||
// self.output_destroyed(conn, qh, output.clone());
|
||||
// self.new_output(conn, qh, output)
|
||||
let Some(bg_layer) = self.background_layers.iter_mut()
|
||||
.find(|bg_layers| bg_layers.output_name == output_name)
|
||||
else {
|
||||
error!(
|
||||
"Updated output '{}' has no background layer, skipping",
|
||||
output_name
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if bg_layer.width != width || bg_layer.height != height {
|
||||
warn!(
|
||||
"Handling of output mode changes are not implemented. \
|
||||
Restart multibg-sway or expect low wallpaper quality due to scaling"
|
||||
);
|
||||
}
|
||||
|
||||
let surface = bg_layer.layer.wl_surface();
|
||||
|
||||
if width == logical_width || height == logical_height {
|
||||
debug!("Output '{}' needs no scaling", output_name);
|
||||
surface.set_buffer_scale(1);
|
||||
if let Some(old_viewport) = bg_layer.viewport.take() {
|
||||
old_viewport.destroy();
|
||||
};
|
||||
}
|
||||
else if width == logical_width * integer_scale_factor
|
||||
&& height == logical_height * integer_scale_factor
|
||||
{
|
||||
debug!("Output '{}' needs integer scaling", output_name);
|
||||
surface.set_buffer_scale(integer_scale_factor);
|
||||
if let Some(old_viewport) = bg_layer.viewport.take() {
|
||||
old_viewport.destroy();
|
||||
};
|
||||
}
|
||||
else {
|
||||
debug!("Output '{}' needs fractional scaling", output_name);
|
||||
surface.set_buffer_scale(1);
|
||||
bg_layer.viewport
|
||||
.get_or_insert_with(||
|
||||
self.viewporter.get_viewport(surface, qh, ())
|
||||
)
|
||||
.set_destination(logical_width, logical_height);
|
||||
}
|
||||
|
||||
surface.commit();
|
||||
}
|
||||
|
||||
fn output_destroyed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
output: wl_output::WlOutput,
|
||||
output: WlOutput,
|
||||
) {
|
||||
let Some(info) = self.output_state.info(&output)
|
||||
else {
|
||||
|
@ -363,7 +427,7 @@ impl OutputHandler for State {
|
|||
return;
|
||||
};
|
||||
|
||||
let Some(name) = info.name
|
||||
let Some(output_name) = info.name
|
||||
else {
|
||||
error!("Destroyed output has no name, skipping");
|
||||
return;
|
||||
|
@ -371,11 +435,11 @@ impl OutputHandler for State {
|
|||
|
||||
debug!(
|
||||
"Output destroyed: {}",
|
||||
name,
|
||||
output_name,
|
||||
);
|
||||
|
||||
if let Some(bg_layer_index) = self.background_layers.iter()
|
||||
.position(|bg_layers| bg_layers.output_name == name)
|
||||
.position(|bg_layers| bg_layers.output_name == output_name)
|
||||
{
|
||||
let removed_bg_layer = self.background_layers
|
||||
.swap_remove(bg_layer_index);
|
||||
|
@ -396,7 +460,7 @@ impl OutputHandler for State {
|
|||
if workspace_bg.buffer.slot().has_active_buffers() {
|
||||
warn!(
|
||||
"On destroyed output '{}' workspace background '{}' will be dropped while its shm slot still has active buffers",
|
||||
name,
|
||||
output_name,
|
||||
workspace_bg.workspace_name,
|
||||
);
|
||||
}
|
||||
|
@ -407,7 +471,7 @@ impl OutputHandler for State {
|
|||
else {
|
||||
error!(
|
||||
"Ignoring destroyed output with unknown name '{}', known outputs were: {}",
|
||||
name,
|
||||
output_name,
|
||||
self.background_layers.iter()
|
||||
.map(|bg_layer| bg_layer.output_name.as_str())
|
||||
.collect::<Vec<_>>().join(", ")
|
||||
|
@ -442,6 +506,32 @@ delegate_output!(State);
|
|||
delegate_registry!(State);
|
||||
delegate_shm!(State);
|
||||
|
||||
impl Dispatch<WpViewporter, ()> for State {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &WpViewporter,
|
||||
_event: <WpViewporter as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
unreachable!("wp_viewporter has no events");
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpViewport, ()> for State {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &WpViewport,
|
||||
_event: <WpViewport as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
unreachable!("wp_viewport has no events");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BackgroundLayer {
|
||||
pub output_name: String,
|
||||
pub width: i32,
|
||||
|
@ -449,7 +539,8 @@ pub struct BackgroundLayer {
|
|||
pub layer: LayerSurface,
|
||||
pub configured: bool,
|
||||
pub workspace_backgrounds: Vec<WorkspaceBackground>,
|
||||
pub shm_slot_pool: SlotPool
|
||||
pub shm_slot_pool: SlotPool,
|
||||
pub viewport: Option<WpViewport>,
|
||||
}
|
||||
impl BackgroundLayer
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue