output-configuration: Support release-requests / v3

This commit is contained in:
Victoria Brekenfeld 2024-04-29 13:33:31 +02:00 committed by Victoria Brekenfeld
parent 3eb6c02008
commit 4b71674e10

View file

@ -54,13 +54,15 @@ struct OutputMngrInstance {
obj: ZwlrOutputManagerV1,
active: Arc<AtomicBool>,
heads: Vec<OutputHeadInstance>,
stale_modes: Vec<ZwlrOutputModeV1>,
}
#[derive(Debug)]
struct OutputHeadInstance {
obj: ZwlrOutputHeadV1,
output: Output,
head: ZwlrOutputHeadV1,
modes: Vec<ZwlrOutputModeV1>,
finished: bool,
}
pub struct OutputMngrInstanceData {
@ -163,6 +165,7 @@ where
obj: data_init.init(resource, data),
heads: Vec::new(),
active,
stale_modes: Vec::new(),
};
let mngr_state = state.output_configuration_state();
@ -234,22 +237,27 @@ where
+ 'static,
{
fn request(
_state: &mut D,
state: &mut D,
_client: &Client,
_obj: &ZwlrOutputHeadV1,
obj: &ZwlrOutputHeadV1,
request: zwlr_output_head_v1::Request,
_data: &Output,
_dh: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
match request {
zwlr_output_head_v1::Request::Release => {
for instance in &mut state.output_configuration_state().instances {
instance.heads.retain(|h| &h.obj != obj);
}
}
_ => {}
}
}
fn destroyed(state: &mut D, _client: ClientId, resource: &ZwlrOutputHeadV1, _data: &Output) {
fn destroyed(state: &mut D, _client: ClientId, obj: &ZwlrOutputHeadV1, _data: &Output) {
for instance in &mut state.output_configuration_state().instances {
instance.heads.retain(|h| &h.head != resource);
instance.heads.retain(|h| &h.obj != obj);
}
}
}
@ -266,18 +274,34 @@ where
+ 'static,
{
fn request(
_state: &mut D,
state: &mut D,
_client: &Client,
_obj: &ZwlrOutputModeV1,
obj: &ZwlrOutputModeV1,
request: zwlr_output_mode_v1::Request,
_data: &Mode,
_dh: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
match request {
zwlr_output_mode_v1::Request::Release => {
for instance in &mut state.output_configuration_state().instances {
instance.stale_modes.retain(|mode| mode != obj)
}
}
_ => {}
}
}
fn destroyed(
state: &mut D,
_client: wayland_backend::server::ClientId,
obj: &ZwlrOutputModeV1,
_data: &Mode,
) {
for instance in &mut state.output_configuration_state().instances {
instance.stale_modes.retain(|mode| mode != obj)
}
}
}
impl<D> Dispatch<ZwlrOutputConfigurationV1, PendingConfiguration, D> for OutputConfigurationState<D>
@ -351,9 +375,7 @@ where
inner
.instances
.iter()
.find_map(|instance| {
instance.heads.iter().find(|h| h.head == *head)
})
.find_map(|instance| instance.heads.iter().find(|h| h.obj == *head))
.map(|i| i.output.clone())
} {
Some(o) => o,
@ -383,6 +405,33 @@ where
}
};
let configured_outputs = final_conf
.iter()
.map(|(o, _)| o.clone())
.collect::<Vec<_>>();
// handle potential races of destroyed heads and modes with cancel instead of a protocol error
// 1. If len doesn't match some outputs aren't configured (technically a protocol error)
// 2. If any configured output isn't in our list anymore, but we passed the len()-test,
// we raced a new head and a destroyed head, so again cancel.
// 3. If the selected mode isn't in our list anymore, we probably already send `finished` for
// the mode, but got no release yet. So again, racy -> cancel.
if configured_outputs.len() != inner.outputs.len()
|| configured_outputs
.iter()
.any(|o| !inner.outputs.contains(o))
|| final_conf.iter().any(|(o, c)| match c {
OutputConfiguration::Enabled { mode, .. } => match mode {
Some(ModeConfiguration::Mode(m)) => !o.modes().contains(m),
_ => false,
},
_ => false,
})
{
obj.cancelled();
return;
}
let result = if matches!(x, zwlr_output_configuration_v1::Request::Test) {
state.test_configuration(final_conf)
} else {
@ -516,7 +565,7 @@ where
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
{
let global = dh.create_global::<D, ZwlrOutputManagerV1, _>(
2,
3,
OutputMngrGlobalData {
filter: Box::new(client_filter),
},
@ -590,19 +639,28 @@ where
for output in std::mem::take(&mut self.removed_outputs).into_iter() {
for instance in &mut self.instances {
instance.heads.retain_mut(|head| {
let mut removed_heads = Vec::new();
for head in &mut instance.heads {
if &head.output == &output {
for mode in &head.modes {
mode.finished();
if head.obj.version() < zwlr_output_head_v1::REQ_RELEASE_SINCE {
removed_heads.push(head.obj.clone());
}
head.head.finished();
false
} else {
true
for mode in &mut head.modes {
mode.finished();
if mode.version() < zwlr_output_mode_v1::REQ_RELEASE_SINCE {
// on >=v3 we keep the obj around until we get a release-request
// otherwise we will drop this with the head
instance.stale_modes.push(mode.clone());
}
}
head.obj.finished();
head.finished = true;
}
});
}
instance.heads.retain(|h| !removed_heads.contains(&h.obj))
}
}
for output in &self.outputs {
{
let state = output.user_data().get::<OutputState>().unwrap();
@ -618,6 +676,7 @@ where
send_head_to_client::<D>(&self.dh, manager, output);
}
}
for manager in self.instances.iter() {
manager.obj.done(self.serial_counter);
}
@ -638,7 +697,12 @@ where
+ OutputConfigurationHandler
+ 'static,
{
let instance = match mngr.heads.iter_mut().find(|i| i.output == *output) {
let instance = match mngr
.heads
.iter_mut()
.filter(|i| !i.finished)
.find(|i| i.output == *output)
{
Some(i) => i,
None => {
if let Ok(client) = dh.get_client(mngr.obj.id()) {
@ -649,9 +713,10 @@ where
) {
mngr.obj.head(&head);
let data = OutputHeadInstance {
head,
obj: head,
modes: Vec::new(),
output: output.clone(),
finished: false,
};
mngr.heads.push(data);
mngr.heads.last_mut().unwrap()
@ -664,13 +729,11 @@ where
}
};
instance.head.name(output.name());
instance.head.description(output.description());
instance.obj.name(output.name());
instance.obj.description(output.description());
let physical = output.physical_properties();
if !(physical.size.w == 0 || physical.size.h == 0) {
instance
.head
.physical_size(physical.size.w, physical.size.h);
instance.obj.physical_size(physical.size.w, physical.size.h);
}
let inner = output
@ -685,11 +748,16 @@ where
instance.modes.retain_mut(|m| {
if !output_modes.contains(m.data::<Mode>().unwrap()) {
m.finished();
if m.version() < zwlr_output_mode_v1::REQ_RELEASE_SINCE {
// on >=v3 we keep the obj around until we get a release-request
mngr.stale_modes.push(m.clone());
}
false
} else {
true
}
});
// update other modes
for output_mode in output_modes.into_iter() {
if let Some(mode) = if let Some(wlr_mode) = instance
@ -698,14 +766,14 @@ where
.find(|mode| *mode.data::<Mode>().unwrap() == output_mode)
{
Some(wlr_mode)
} else if let Ok(client) = dh.get_client(instance.head.id()) {
} else if let Ok(client) = dh.get_client(instance.obj.id()) {
// create the mode if it does not exist yet
if let Ok(mode) = client.create_resource::<ZwlrOutputModeV1, _, D>(
dh,
instance.head.version(),
instance.obj.version(),
output_mode,
) {
instance.head.mode(&mode);
instance.obj.mode(&mode);
mode.size(output_mode.size.w, output_mode.size.h);
mode.refresh(output_mode.refresh);
if output
@ -729,27 +797,27 @@ where
.map(|c| c == output_mode)
.unwrap_or(false)
{
instance.head.current_mode(&*mode);
instance.obj.current_mode(&*mode);
}
}
}
instance.head.enabled(if inner.enabled { 1 } else { 0 });
instance.obj.enabled(if inner.enabled { 1 } else { 0 });
if inner.enabled {
let point = output.current_location();
instance.head.position(point.x, point.y);
instance.head.transform(output.current_transform().into());
instance.obj.position(point.x, point.y);
instance.obj.transform(output.current_transform().into());
instance
.head
.obj
.scale(output.current_scale().fractional_scale());
}
if mngr.obj.version() >= zwlr_output_head_v1::EVT_MAKE_SINCE {
if physical.make != "Unknown" {
instance.head.make(physical.make.clone());
instance.obj.make(physical.make.clone());
}
if physical.model != "Unknown" {
instance.head.model(physical.model);
instance.obj.model(physical.model);
}
}
}