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, obj: ZwlrOutputManagerV1,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
heads: Vec<OutputHeadInstance>, heads: Vec<OutputHeadInstance>,
stale_modes: Vec<ZwlrOutputModeV1>,
} }
#[derive(Debug)] #[derive(Debug)]
struct OutputHeadInstance { struct OutputHeadInstance {
obj: ZwlrOutputHeadV1,
output: Output, output: Output,
head: ZwlrOutputHeadV1,
modes: Vec<ZwlrOutputModeV1>, modes: Vec<ZwlrOutputModeV1>,
finished: bool,
} }
pub struct OutputMngrInstanceData { pub struct OutputMngrInstanceData {
@ -163,6 +165,7 @@ where
obj: data_init.init(resource, data), obj: data_init.init(resource, data),
heads: Vec::new(), heads: Vec::new(),
active, active,
stale_modes: Vec::new(),
}; };
let mngr_state = state.output_configuration_state(); let mngr_state = state.output_configuration_state();
@ -234,22 +237,27 @@ where
+ 'static, + 'static,
{ {
fn request( fn request(
_state: &mut D, state: &mut D,
_client: &Client, _client: &Client,
_obj: &ZwlrOutputHeadV1, obj: &ZwlrOutputHeadV1,
request: zwlr_output_head_v1::Request, request: zwlr_output_head_v1::Request,
_data: &Output, _data: &Output,
_dh: &DisplayHandle, _dh: &DisplayHandle,
_data_init: &mut DataInit<'_, D>, _data_init: &mut DataInit<'_, D>,
) { ) {
match request { 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 { 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, + 'static,
{ {
fn request( fn request(
_state: &mut D, state: &mut D,
_client: &Client, _client: &Client,
_obj: &ZwlrOutputModeV1, obj: &ZwlrOutputModeV1,
request: zwlr_output_mode_v1::Request, request: zwlr_output_mode_v1::Request,
_data: &Mode, _data: &Mode,
_dh: &DisplayHandle, _dh: &DisplayHandle,
_data_init: &mut DataInit<'_, D>, _data_init: &mut DataInit<'_, D>,
) { ) {
match request { 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> impl<D> Dispatch<ZwlrOutputConfigurationV1, PendingConfiguration, D> for OutputConfigurationState<D>
@ -351,9 +375,7 @@ where
inner inner
.instances .instances
.iter() .iter()
.find_map(|instance| { .find_map(|instance| instance.heads.iter().find(|h| h.obj == *head))
instance.heads.iter().find(|h| h.head == *head)
})
.map(|i| i.output.clone()) .map(|i| i.output.clone())
} { } {
Some(o) => o, 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) { let result = if matches!(x, zwlr_output_configuration_v1::Request::Test) {
state.test_configuration(final_conf) state.test_configuration(final_conf)
} else { } else {
@ -516,7 +565,7 @@ where
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
{ {
let global = dh.create_global::<D, ZwlrOutputManagerV1, _>( let global = dh.create_global::<D, ZwlrOutputManagerV1, _>(
2, 3,
OutputMngrGlobalData { OutputMngrGlobalData {
filter: Box::new(client_filter), filter: Box::new(client_filter),
}, },
@ -590,19 +639,28 @@ where
for output in std::mem::take(&mut self.removed_outputs).into_iter() { for output in std::mem::take(&mut self.removed_outputs).into_iter() {
for instance in &mut self.instances { 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 { if &head.output == &output {
for mode in &head.modes { if head.obj.version() < zwlr_output_head_v1::REQ_RELEASE_SINCE {
mode.finished(); removed_heads.push(head.obj.clone());
} }
head.head.finished(); for mode in &mut head.modes {
false mode.finished();
} else { if mode.version() < zwlr_output_mode_v1::REQ_RELEASE_SINCE {
true // 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 { for output in &self.outputs {
{ {
let state = output.user_data().get::<OutputState>().unwrap(); let state = output.user_data().get::<OutputState>().unwrap();
@ -618,6 +676,7 @@ where
send_head_to_client::<D>(&self.dh, manager, output); send_head_to_client::<D>(&self.dh, manager, output);
} }
} }
for manager in self.instances.iter() { for manager in self.instances.iter() {
manager.obj.done(self.serial_counter); manager.obj.done(self.serial_counter);
} }
@ -638,7 +697,12 @@ where
+ OutputConfigurationHandler + OutputConfigurationHandler
+ 'static, + '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, Some(i) => i,
None => { None => {
if let Ok(client) = dh.get_client(mngr.obj.id()) { if let Ok(client) = dh.get_client(mngr.obj.id()) {
@ -649,9 +713,10 @@ where
) { ) {
mngr.obj.head(&head); mngr.obj.head(&head);
let data = OutputHeadInstance { let data = OutputHeadInstance {
head, obj: head,
modes: Vec::new(), modes: Vec::new(),
output: output.clone(), output: output.clone(),
finished: false,
}; };
mngr.heads.push(data); mngr.heads.push(data);
mngr.heads.last_mut().unwrap() mngr.heads.last_mut().unwrap()
@ -664,13 +729,11 @@ where
} }
}; };
instance.head.name(output.name()); instance.obj.name(output.name());
instance.head.description(output.description()); instance.obj.description(output.description());
let physical = output.physical_properties(); let physical = output.physical_properties();
if !(physical.size.w == 0 || physical.size.h == 0) { if !(physical.size.w == 0 || physical.size.h == 0) {
instance instance.obj.physical_size(physical.size.w, physical.size.h);
.head
.physical_size(physical.size.w, physical.size.h);
} }
let inner = output let inner = output
@ -685,11 +748,16 @@ where
instance.modes.retain_mut(|m| { instance.modes.retain_mut(|m| {
if !output_modes.contains(m.data::<Mode>().unwrap()) { if !output_modes.contains(m.data::<Mode>().unwrap()) {
m.finished(); 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 false
} else { } else {
true true
} }
}); });
// update other modes // update other modes
for output_mode in output_modes.into_iter() { for output_mode in output_modes.into_iter() {
if let Some(mode) = if let Some(wlr_mode) = instance if let Some(mode) = if let Some(wlr_mode) = instance
@ -698,14 +766,14 @@ where
.find(|mode| *mode.data::<Mode>().unwrap() == output_mode) .find(|mode| *mode.data::<Mode>().unwrap() == output_mode)
{ {
Some(wlr_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 // create the mode if it does not exist yet
if let Ok(mode) = client.create_resource::<ZwlrOutputModeV1, _, D>( if let Ok(mode) = client.create_resource::<ZwlrOutputModeV1, _, D>(
dh, dh,
instance.head.version(), instance.obj.version(),
output_mode, output_mode,
) { ) {
instance.head.mode(&mode); instance.obj.mode(&mode);
mode.size(output_mode.size.w, output_mode.size.h); mode.size(output_mode.size.w, output_mode.size.h);
mode.refresh(output_mode.refresh); mode.refresh(output_mode.refresh);
if output if output
@ -729,27 +797,27 @@ where
.map(|c| c == output_mode) .map(|c| c == output_mode)
.unwrap_or(false) .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 { if inner.enabled {
let point = output.current_location(); let point = output.current_location();
instance.head.position(point.x, point.y); instance.obj.position(point.x, point.y);
instance.head.transform(output.current_transform().into()); instance.obj.transform(output.current_transform().into());
instance instance
.head .obj
.scale(output.current_scale().fractional_scale()); .scale(output.current_scale().fractional_scale());
} }
if mngr.obj.version() >= zwlr_output_head_v1::EVT_MAKE_SINCE { if mngr.obj.version() >= zwlr_output_head_v1::EVT_MAKE_SINCE {
if physical.make != "Unknown" { if physical.make != "Unknown" {
instance.head.make(physical.make.clone()); instance.obj.make(physical.make.clone());
} }
if physical.model != "Unknown" { if physical.model != "Unknown" {
instance.head.model(physical.model); instance.obj.model(physical.model);
} }
} }
} }