Implement XdgOutput protocol
This commit is contained in:
parent
2536a5a9cb
commit
7e4e78151a
|
@ -51,7 +51,7 @@ impl Output {
|
|||
|
||||
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);
|
||||
|
||||
Self {
|
||||
|
@ -159,6 +159,11 @@ impl OutputMap {
|
|||
|
||||
output.location.x = output_x;
|
||||
output.location.y = 0;
|
||||
|
||||
output
|
||||
.output
|
||||
.change_current_state(None, None, None, Some(output.location));
|
||||
|
||||
output_x += output.size().w;
|
||||
}
|
||||
|
||||
|
@ -314,7 +319,7 @@ impl OutputMap {
|
|||
output.output.delete_mode(output.current_mode);
|
||||
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.current_mode = mode;
|
||||
}
|
||||
|
@ -356,9 +361,12 @@ impl OutputMap {
|
|||
|
||||
if output.output_scale != output_scale {
|
||||
output.output_scale = output_scale;
|
||||
output
|
||||
.output
|
||||
.change_current_state(Some(output.current_mode), None, Some(output_scale));
|
||||
output.output.change_current_state(
|
||||
Some(output.current_mode),
|
||||
None,
|
||||
Some(output_scale),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ use smithay::{
|
|||
utils::{Logical, Point},
|
||||
wayland::{
|
||||
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},
|
||||
shm::init_shm_global,
|
||||
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());
|
||||
|
||||
init_xdg_output_manager(&mut display.borrow_mut(), log.clone());
|
||||
|
||||
let socket_name = display
|
||||
.borrow_mut()
|
||||
.add_socket_auto()
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
//! Some(Mode { size: (1920, 1080).into(), refresh: 60000 }), // the resolution mode,
|
||||
//! Some(wl_output::Transform::Normal), // global screen transformation
|
||||
//! Some(1), // global screen scaling factor
|
||||
//! Some((0,0).into()) // output position
|
||||
//! );
|
||||
//! // set the preferred mode
|
||||
//! output.set_preferred(Mode { size: (1920, 1080).into(), refresh: 60000 });
|
||||
|
@ -47,6 +48,8 @@
|
|||
//! output.add_mode(Mode { size: (1024, 768).into(), refresh: 60000 });
|
||||
//! ```
|
||||
|
||||
pub mod xdg;
|
||||
|
||||
use std::{
|
||||
ops::Deref as _,
|
||||
sync::{Arc, Mutex},
|
||||
|
@ -65,6 +68,8 @@ use slog::{info, o, trace, warn};
|
|||
|
||||
use crate::utils::{Logical, Physical, Point, Raw, Size};
|
||||
|
||||
use self::xdg::XdgOutput;
|
||||
|
||||
/// An output mode
|
||||
///
|
||||
/// A possible combination of dimensions and refresh rate for an output.
|
||||
|
@ -106,6 +111,8 @@ struct Inner {
|
|||
modes: Vec<Mode>,
|
||||
current_mode: Option<Mode>,
|
||||
preferred_mode: Option<Mode>,
|
||||
|
||||
xdg_output: Option<XdgOutput>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
|
@ -194,6 +201,7 @@ impl Output {
|
|||
modes: Vec::new(),
|
||||
current_mode: None,
|
||||
preferred_mode: None,
|
||||
xdg_output: None,
|
||||
}));
|
||||
|
||||
let output = Output { inner: inner.clone() };
|
||||
|
@ -220,6 +228,16 @@ impl Output {
|
|||
(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
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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_transform: Option<Transform>,
|
||||
new_scale: Option<i32>,
|
||||
new_location: Option<Point<i32, Logical>>,
|
||||
) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
if let Some(mode) = new_mode {
|
||||
|
@ -287,11 +306,21 @@ impl Output {
|
|||
if inner.preferred_mode == new_mode {
|
||||
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 {
|
||||
if let Some(mode) = new_mode {
|
||||
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);
|
||||
}
|
||||
if let Some(scale) = new_scale {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}),
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue