Implement XdgOutput protocol

This commit is contained in:
Poly 2021-07-20 04:01:47 +02:00 committed by Victor Berger
parent 2536a5a9cb
commit 7e4e78151a
4 changed files with 224 additions and 7 deletions

View File

@ -51,7 +51,7 @@ impl Output {
let output_scale = scale.round() as i32; let output_scale = scale.round() as i32;
output.change_current_state(Some(mode), None, Some(output_scale)); output.change_current_state(Some(mode), None, Some(output_scale), Some(location));
output.set_preferred(mode); output.set_preferred(mode);
Self { Self {
@ -159,6 +159,11 @@ impl OutputMap {
output.location.x = output_x; output.location.x = output_x;
output.location.y = 0; output.location.y = 0;
output
.output
.change_current_state(None, None, None, Some(output.location));
output_x += output.size().w; output_x += output.size().w;
} }
@ -314,7 +319,7 @@ impl OutputMap {
output.output.delete_mode(output.current_mode); output.output.delete_mode(output.current_mode);
output output
.output .output
.change_current_state(Some(mode), None, Some(output.output_scale)); .change_current_state(Some(mode), None, Some(output.output_scale), None);
output.output.set_preferred(mode); output.output.set_preferred(mode);
output.current_mode = mode; output.current_mode = mode;
} }
@ -356,9 +361,12 @@ impl OutputMap {
if output.output_scale != output_scale { if output.output_scale != output_scale {
output.output_scale = output_scale; output.output_scale = output_scale;
output output.output.change_current_state(
.output Some(output.current_mode),
.change_current_state(Some(output.current_mode), None, Some(output_scale)); None,
Some(output_scale),
None,
);
} }
} }
} }

View File

@ -15,6 +15,7 @@ use smithay::{
utils::{Logical, Point}, utils::{Logical, Point},
wayland::{ wayland::{
data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent}, data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
output::xdg::init_xdg_output_manager,
seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig}, seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig},
shm::init_shm_global, shm::init_shm_global,
tablet_manager::{init_tablet_manager_global, TabletSeatTrait}, tablet_manager::{init_tablet_manager_global, TabletSeatTrait},
@ -82,6 +83,8 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
let shell_handles = init_shell::<BackendData>(display.clone(), log.clone()); let shell_handles = init_shell::<BackendData>(display.clone(), log.clone());
init_xdg_output_manager(&mut display.borrow_mut(), log.clone());
let socket_name = display let socket_name = display
.borrow_mut() .borrow_mut()
.add_socket_auto() .add_socket_auto()

View File

@ -39,6 +39,7 @@
//! Some(Mode { size: (1920, 1080).into(), refresh: 60000 }), // the resolution mode, //! Some(Mode { size: (1920, 1080).into(), refresh: 60000 }), // the resolution mode,
//! Some(wl_output::Transform::Normal), // global screen transformation //! Some(wl_output::Transform::Normal), // global screen transformation
//! Some(1), // global screen scaling factor //! Some(1), // global screen scaling factor
//! Some((0,0).into()) // output position
//! ); //! );
//! // set the preferred mode //! // set the preferred mode
//! output.set_preferred(Mode { size: (1920, 1080).into(), refresh: 60000 }); //! output.set_preferred(Mode { size: (1920, 1080).into(), refresh: 60000 });
@ -47,6 +48,8 @@
//! output.add_mode(Mode { size: (1024, 768).into(), refresh: 60000 }); //! output.add_mode(Mode { size: (1024, 768).into(), refresh: 60000 });
//! ``` //! ```
pub mod xdg;
use std::{ use std::{
ops::Deref as _, ops::Deref as _,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@ -65,6 +68,8 @@ use slog::{info, o, trace, warn};
use crate::utils::{Logical, Physical, Point, Raw, Size}; use crate::utils::{Logical, Physical, Point, Raw, Size};
use self::xdg::XdgOutput;
/// An output mode /// An output mode
/// ///
/// A possible combination of dimensions and refresh rate for an output. /// A possible combination of dimensions and refresh rate for an output.
@ -106,6 +111,8 @@ struct Inner {
modes: Vec<Mode>, modes: Vec<Mode>,
current_mode: Option<Mode>, current_mode: Option<Mode>,
preferred_mode: Option<Mode>, preferred_mode: Option<Mode>,
xdg_output: Option<XdgOutput>,
} }
impl Inner { impl Inner {
@ -194,6 +201,7 @@ impl Output {
modes: Vec::new(), modes: Vec::new(),
current_mode: None, current_mode: None,
preferred_mode: None, preferred_mode: None,
xdg_output: None,
})); }));
let output = Output { inner: inner.clone() }; let output = Output { inner: inner.clone() };
@ -220,6 +228,16 @@ impl Output {
(output, global) (output, global)
} }
/// Attempt to retrieve a [`Output`] from an existing resource
pub fn from_resource(output: &WlOutput) -> Option<Output> {
output
.as_ref()
.user_data()
.get::<Arc<Mutex<Inner>>>()
.cloned()
.map(|inner| Output { inner })
}
/// Sets the preferred mode of this output /// Sets the preferred mode of this output
/// ///
/// If the provided mode was not previously known to this output, it is added to its /// If the provided mode was not previously known to this output, it is added to its
@ -257,7 +275,7 @@ impl Output {
/// Change the current state of this output /// Change the current state of this output
/// ///
/// You can changed the current mode, transform status or scale of this output. Providing /// You can changed the current mode, transform status, location or scale of this output. Providing
/// `None` to any of these field means that the value does not change. /// `None` to any of these field means that the value does not change.
/// ///
/// If the provided mode was not previously known to this output, it is added to its /// If the provided mode was not previously known to this output, it is added to its
@ -269,6 +287,7 @@ impl Output {
new_mode: Option<Mode>, new_mode: Option<Mode>,
new_transform: Option<Transform>, new_transform: Option<Transform>,
new_scale: Option<i32>, new_scale: Option<i32>,
new_location: Option<Point<i32, Logical>>,
) { ) {
let mut inner = self.inner.lock().unwrap(); let mut inner = self.inner.lock().unwrap();
if let Some(mode) = new_mode { if let Some(mode) = new_mode {
@ -287,11 +306,21 @@ impl Output {
if inner.preferred_mode == new_mode { if inner.preferred_mode == new_mode {
flags |= WMode::Preferred; flags |= WMode::Preferred;
} }
if let Some(new_location) = new_location {
inner.location = new_location;
}
// XdgOutput has to be updated before WlOutput
// Because WlOutput::done() has to allways be called last
if let Some(xdg_output) = inner.xdg_output.as_ref() {
xdg_output.change_current_state(new_mode, new_scale, new_location);
}
for output in &inner.instances { for output in &inner.instances {
if let Some(mode) = new_mode { if let Some(mode) = new_mode {
output.mode(flags, mode.size.w, mode.size.h, mode.refresh); output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
} }
if new_transform.is_some() { if new_transform.is_some() || new_location.is_some() {
inner.send_geometry(output); inner.send_geometry(output);
} }
if let Some(scale) = new_scale { if let Some(scale) = new_scale {

177
src/wayland/output/xdg.rs Normal file
View File

@ -0,0 +1,177 @@
//! XDG Output advertising capabilities
//!
//! This protocol is ment for describing outputs in a way
//! which is more in line with the concept of an output on desktop oriented systems.
use std::{
ops::Deref as _,
sync::{Arc, Mutex},
};
use slog::{o, trace};
use wayland_protocols::unstable::xdg_output::v1::server::{
zxdg_output_manager_v1::{self, ZxdgOutputManagerV1},
zxdg_output_v1::ZxdgOutputV1,
};
use wayland_server::{protocol::wl_output::WlOutput, Display, Filter, Global, Main};
use crate::utils::{Logical, Physical, Point, Size};
use super::{Mode, Output};
#[derive(Debug)]
struct Inner {
name: String,
description: String,
logical_position: Point<i32, Logical>,
physical_size: Option<Size<i32, Physical>>,
scale: i32,
instances: Vec<ZxdgOutputV1>,
log: ::slog::Logger,
}
#[derive(Debug, Clone)]
pub(super) struct XdgOutput {
inner: Arc<Mutex<Inner>>,
}
impl XdgOutput {
fn new(output: &super::Inner, log: ::slog::Logger) -> Self {
trace!(log, "Creating new xdg_output"; "name" => &output.name);
let description = format!(
"{} - {} - {}",
output.physical.make, output.physical.model, output.name
);
let physical_size = output.current_mode.map(|mode| mode.size);
Self {
inner: Arc::new(Mutex::new(Inner {
name: output.name.clone(),
description,
logical_position: output.location,
physical_size,
scale: output.scale,
instances: Vec::new(),
log,
})),
}
}
fn add_instance(&self, xdg_output: Main<ZxdgOutputV1>, wl_output: &WlOutput) {
let mut inner = self.inner.lock().unwrap();
xdg_output.logical_position(inner.logical_position.x, inner.logical_position.y);
if let Some(size) = inner.physical_size {
let logical_size = size.to_logical(inner.scale);
xdg_output.logical_size(logical_size.w, logical_size.h);
}
if xdg_output.as_ref().version() >= 2 {
xdg_output.name(inner.name.clone());
xdg_output.description(inner.description.clone());
}
// xdg_output.done() is deprecated since version 3
if xdg_output.as_ref().version() < 3 {
xdg_output.done();
}
wl_output.done();
xdg_output.quick_assign(|_, _, _| {});
xdg_output.assign_destructor(Filter::new(|xdg_output: ZxdgOutputV1, _, _| {
let inner = &xdg_output.as_ref().user_data().get::<XdgOutput>().unwrap().inner;
inner
.lock()
.unwrap()
.instances
.retain(|o| !o.as_ref().equals(&xdg_output.as_ref()));
}));
xdg_output.as_ref().user_data().set_threadsafe({
let xdg_output = self.clone();
move || xdg_output
});
inner.instances.push(xdg_output.deref().clone());
}
pub(super) fn change_current_state(
&self,
new_mode: Option<Mode>,
new_scale: Option<i32>,
new_location: Option<Point<i32, Logical>>,
) {
let mut output = self.inner.lock().unwrap();
if let Some(new_mode) = new_mode {
output.physical_size = Some(new_mode.size);
}
if let Some(new_scale) = new_scale {
output.scale = new_scale;
}
if let Some(new_location) = new_location {
output.logical_position = new_location;
}
for instance in output.instances.iter() {
if new_mode.is_some() | new_scale.is_some() {
if let Some(size) = output.physical_size {
let logical_size = size.to_logical(output.scale);
instance.logical_size(logical_size.w, logical_size.h);
}
}
if new_location.is_some() {
instance.logical_position(output.logical_position.x, output.logical_position.y);
}
// xdg_output.done() is deprecated since version 3
if instance.as_ref().version() < 3 {
instance.done();
}
// No need for wl_output.done() here, it will be called by caller (super::Output::change_current_state)
}
}
}
/// Initialize a xdg output manager global.
pub fn init_xdg_output_manager<L>(display: &mut Display, logger: L) -> Global<ZxdgOutputManagerV1>
where
L: Into<Option<::slog::Logger>>,
{
let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "xdg_output_handler"));
display.create_global(
3,
Filter::new(move |(manager, _version): (Main<ZxdgOutputManagerV1>, _), _, _| {
let log = log.clone();
manager.quick_assign(move |_, req, _| match req {
zxdg_output_manager_v1::Request::GetXdgOutput {
id,
output: wl_output,
} => {
let output = Output::from_resource(&wl_output).unwrap();
let mut inner = output.inner.lock().unwrap();
if inner.xdg_output.is_none() {
inner.xdg_output = Some(XdgOutput::new(&inner, log.clone()));
}
inner.xdg_output.as_ref().unwrap().add_instance(id, &wl_output);
}
zxdg_output_manager_v1::Request::Destroy => {
// Nothing to do
}
_ => {}
});
}),
)
}