Support XDG Activation
Test XDG Activation
This commit is contained in:
parent
b34b08f004
commit
180efb0ba9
10 changed files with 413 additions and 27 deletions
|
|
@ -23,6 +23,10 @@ use wayland_protocols::{
|
|||
viewporter::server::wp_viewporter::WpViewporter,
|
||||
},
|
||||
xdg::{
|
||||
activation::v1::server::{
|
||||
xdg_activation_token_v1::{self, XdgActivationTokenV1},
|
||||
xdg_activation_v1::{self, XdgActivationV1},
|
||||
},
|
||||
shell::server::{
|
||||
xdg_popup::{self, XdgPopup},
|
||||
xdg_positioner::{self, XdgPositioner},
|
||||
|
|
@ -176,6 +180,14 @@ struct KeyboardState {
|
|||
current_focus: Option<SurfaceId>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ActivationTokenData {
|
||||
serial: Option<(u32, WlSeat)>,
|
||||
app_id: Option<String>,
|
||||
surface: Option<WlSurface>,
|
||||
constructed: bool,
|
||||
}
|
||||
|
||||
struct State {
|
||||
surfaces: HashMap<SurfaceId, SurfaceData>,
|
||||
outputs: HashMap<WlOutput, Output>,
|
||||
|
|
@ -185,12 +197,16 @@ struct State {
|
|||
last_surface_id: Option<SurfaceId>,
|
||||
last_output: Option<WlOutput>,
|
||||
callbacks: Vec<WlCallback>,
|
||||
seat: Option<WlSeat>,
|
||||
pointer: Option<WlPointer>,
|
||||
keyboard: Option<KeyboardState>,
|
||||
configure_serial: u32,
|
||||
selection: Option<WlDataSource>,
|
||||
data_device_man: Option<WlDataDeviceManager>,
|
||||
data_device: Option<WlDataDevice>,
|
||||
xdg_activation: Option<XdgActivationV1>,
|
||||
valid_tokens: HashSet<String>,
|
||||
token_counter: u32,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
|
|
@ -204,12 +220,16 @@ impl Default for State {
|
|||
last_surface_id: None,
|
||||
last_output: None,
|
||||
callbacks: Vec::new(),
|
||||
seat: None,
|
||||
pointer: None,
|
||||
keyboard: None,
|
||||
configure_serial: 0,
|
||||
selection: None,
|
||||
data_device_man: None,
|
||||
data_device: None,
|
||||
xdg_activation: None,
|
||||
valid_tokens: HashSet::new(),
|
||||
token_counter: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -246,11 +266,9 @@ impl State {
|
|||
keyboard.leave(self.configure_serial, &self.surfaces[id].surface);
|
||||
}
|
||||
|
||||
keyboard.enter(
|
||||
self.configure_serial,
|
||||
&self.surfaces[&surface_id].surface,
|
||||
Vec::default(),
|
||||
);
|
||||
let surface = self.surfaces.get_mut(&surface_id).unwrap();
|
||||
keyboard.enter(self.configure_serial, &surface.surface, Vec::default());
|
||||
surface.last_enter_serial = Some(self.configure_serial);
|
||||
|
||||
*current_focus = Some(surface_id);
|
||||
}
|
||||
|
|
@ -267,6 +285,10 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_focused(&self) -> Option<SurfaceId> {
|
||||
self.keyboard.as_ref()?.current_focus
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn configure_popup(&mut self, surface_id: SurfaceId) {
|
||||
let surface = self.surfaces.get_mut(&surface_id).unwrap();
|
||||
|
|
@ -360,6 +382,7 @@ impl Server {
|
|||
dh.create_global::<State, WlSeat, _>(5, ());
|
||||
dh.create_global::<State, WlDataDeviceManager, _>(3, ());
|
||||
dh.create_global::<State, ZwpTabletManagerV2, _>(1, ());
|
||||
dh.create_global::<State, XdgActivationV1, _>(1, ());
|
||||
global_noop!(ZwpLinuxDmabufV1);
|
||||
global_noop!(ZwpRelativePointerManagerV1);
|
||||
global_noop!(WpViewporter);
|
||||
|
|
@ -495,6 +518,11 @@ impl Server {
|
|||
self.display.flush_clients().unwrap();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn get_focused(&self) -> Option<SurfaceId> {
|
||||
self.state.get_focused()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn configure_popup(&mut self, surface_id: SurfaceId) {
|
||||
self.state.configure_popup(surface_id);
|
||||
|
|
@ -971,7 +999,7 @@ impl Dispatch<WlDataDeviceManager, ()> for State {
|
|||
|
||||
impl GlobalDispatch<WlSeat, ()> for State {
|
||||
fn bind(
|
||||
_: &mut Self,
|
||||
state: &mut Self,
|
||||
_: &DisplayHandle,
|
||||
_: &Client,
|
||||
resource: wayland_server::New<WlSeat>,
|
||||
|
|
@ -980,6 +1008,7 @@ impl GlobalDispatch<WlSeat, ()> for State {
|
|||
) {
|
||||
let seat = data_init.init(resource, ());
|
||||
seat.capabilities(wl_seat::Capability::Pointer | wl_seat::Capability::Keyboard);
|
||||
state.seat = Some(seat);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1524,3 +1553,126 @@ impl Dispatch<WlCallback, ()> for State {
|
|||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalDispatch<XdgActivationV1, ()> for State {
|
||||
fn bind(
|
||||
state: &mut Self,
|
||||
_: &DisplayHandle,
|
||||
_: &Client,
|
||||
resource: wayland_server::New<XdgActivationV1>,
|
||||
_: &(),
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
state.xdg_activation = Some(data_init.init(resource, ()));
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgActivationV1, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &Client,
|
||||
_: &XdgActivationV1,
|
||||
request: <XdgActivationV1 as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
xdg_activation_v1::Request::Destroy => {}
|
||||
xdg_activation_v1::Request::GetActivationToken { id } => {
|
||||
data_init.init(id, Mutex::new(ActivationTokenData::default()));
|
||||
}
|
||||
xdg_activation_v1::Request::Activate { token, surface } => {
|
||||
if state.valid_tokens.remove(&token) {
|
||||
let surface_id = SurfaceId(surface.id().protocol_id());
|
||||
state.focus_toplevel(surface_id);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgActivationTokenV1, Mutex<ActivationTokenData>> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &Client,
|
||||
token: &XdgActivationTokenV1,
|
||||
request: <XdgActivationTokenV1 as Resource>::Request,
|
||||
data: &Mutex<ActivationTokenData>,
|
||||
_: &DisplayHandle,
|
||||
_: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
let mut data = data.lock().unwrap();
|
||||
match request {
|
||||
xdg_activation_token_v1::Request::SetSerial { serial, seat } => {
|
||||
if data.constructed {
|
||||
token.post_error(
|
||||
xdg_activation_token_v1::Error::AlreadyUsed,
|
||||
"The activation token has already been constructed",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
data.serial = Some((serial, seat));
|
||||
}
|
||||
xdg_activation_token_v1::Request::SetAppId { app_id } => {
|
||||
if data.constructed {
|
||||
token.post_error(
|
||||
xdg_activation_token_v1::Error::AlreadyUsed,
|
||||
"The activation token has already been constructed",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
data.app_id = Some(app_id);
|
||||
}
|
||||
xdg_activation_token_v1::Request::SetSurface { surface } => {
|
||||
if data.constructed {
|
||||
token.post_error(
|
||||
xdg_activation_token_v1::Error::AlreadyUsed,
|
||||
"The activation token has already been constructed",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
data.surface = Some(surface);
|
||||
}
|
||||
xdg_activation_token_v1::Request::Commit => {
|
||||
if data.constructed {
|
||||
token.post_error(
|
||||
xdg_activation_token_v1::Error::AlreadyUsed,
|
||||
"The activation token has already been constructed",
|
||||
);
|
||||
return;
|
||||
}
|
||||
data.constructed = true;
|
||||
|
||||
// Require a valid serial, otherwise ignore the activation.
|
||||
// This matches niri's behavior: https://github.com/YaLTeR/niri/blob/5e549e13238a853f8860e29621ab6b31ee1b9ee4/src/handlers/mod.rs#L712-L723
|
||||
let valid = if let (Some((serial, seat)), Some(surface_data)) = (
|
||||
data.serial.take(),
|
||||
data.surface.take().and_then(|surface| {
|
||||
state.surfaces.get(&SurfaceId(surface.id().protocol_id()))
|
||||
}),
|
||||
) {
|
||||
state.seat == Some(seat)
|
||||
&& surface_data
|
||||
.last_enter_serial
|
||||
.is_some_and(|last_enter| serial >= last_enter)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let activation_token = state.token_counter.to_string();
|
||||
state.token_counter += 1;
|
||||
if valid {
|
||||
state.valid_tokens.insert(activation_token.clone());
|
||||
}
|
||||
token.done(activation_token);
|
||||
}
|
||||
xdg_activation_token_v1::Request::Destroy => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue