use crate::{ShellError, WindowId};
use super::protocol::xdg_activation::{xdg_activation_token_v1, xdg_activation_v1};
use super::{ObjectKind, RawEvent, WaylandShell};
pub(super) const ACTIVATION_ROUNDTRIPS: usize = 4;
impl WaylandShell {
pub(super) fn activation_token(
&mut self,
window: WindowId,
) -> Result<Option<String>, ShellError> {
let surface = self.window(window)?.surface;
let manager = self.conn.activation;
if manager.is_null() {
return Ok(None);
}
let token = unsafe { xdg_activation_v1::get_activation_token(manager) };
if token.is_null() {
return Err(ShellError::Backend(
"xdg_activation_v1.get_activation_token failed".to_string(),
));
}
self.conn.watch(token, ObjectKind::ActivationToken);
let seat = self
.seats
.iter()
.filter(|seat| seat.last_serial != 0)
.max_by_key(|seat| seat.last_serial)
.map(|seat| (seat.proxy, seat.last_serial));
unsafe {
if let Some((seat, serial)) = seat {
xdg_activation_token_v1::set_serial(token, serial, seat);
}
xdg_activation_token_v1::set_surface(token, surface);
xdg_activation_token_v1::commit(token);
}
let mut answer = None;
for _ in 0..ACTIVATION_ROUNDTRIPS {
if let Err(error) = self.conn.roundtrip() {
unsafe { self.conn.release(token, xdg_activation_token_v1::destroy) };
return Err(error);
}
answer = take_done(&mut self.conn.sink().events, token as usize);
if answer.is_some() {
break;
}
}
unsafe { self.conn.release(token, xdg_activation_token_v1::destroy) };
answer.map(Some).ok_or_else(|| {
ShellError::Backend("the compositor sent no xdg-activation token".to_string())
})
}
}
fn take_done(events: &mut Vec<RawEvent>, token: usize) -> Option<String> {
let index = events.iter().position(
|event| matches!(event, RawEvent::ActivationDone { token: from, .. } if *from == token),
)?;
match events.remove(index) {
RawEvent::ActivationDone { value, .. } => Some(value),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn done(token: usize, value: &str) -> RawEvent {
RawEvent::ActivationDone {
token,
value: value.to_string(),
}
}
#[test]
fn take_done_takes_only_its_own_answer() {
let mut events = vec![
RawEvent::GlobalRemove { name: 7 },
done(1, "other"),
done(2, "mine"),
RawEvent::GlobalRemove { name: 8 },
];
assert_eq!(take_done(&mut events, 2).as_deref(), Some("mine"));
assert_eq!(take_done(&mut events, 2), None);
assert_eq!(events.len(), 3);
assert!(matches!(events[0], RawEvent::GlobalRemove { name: 7 }));
assert!(matches!(
events[1],
RawEvent::ActivationDone { token: 1, .. }
));
assert!(matches!(events[2], RawEvent::GlobalRemove { name: 8 }));
}
}