Skip to content

Commit

Permalink
sctk: Implement xdg-activation support
Browse files Browse the repository at this point in the history
  • Loading branch information
Drakulix committed Nov 2, 2023
1 parent 6d8f942 commit 75eee59
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 3 deletions.
67 changes: 67 additions & 0 deletions runtime/src/command/platform_specific/wayland/activation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use iced_core::window::Id;
use iced_futures::MaybeSend;

use std::fmt;

/// xdg-activation Actions
pub enum Action<T> {
/// request an activation token
RequestToken {
/// application id
app_id: Option<String>,
/// window, if provided
window: Option<Id>,
/// message generation
message: Box<dyn FnOnce(Option<String>) -> T + Send + Sync + 'static>,
},
/// request a window to be activated
Activate {
/// window to activate
window: Id,
/// activation token
token: String,
},
}

impl<T> Action<T> {
/// Maps the output of a window [`Action`] using the provided closure.
pub fn map<A>(
self,
mapper: impl Fn(T) -> A + 'static + MaybeSend + Sync,
) -> Action<A>
where
T: 'static,
{
match self {
Action::RequestToken {
app_id,
window,
message,
} => Action::RequestToken {
app_id,
window,
message: Box::new(move |token| mapper(message(token))),
},
Action::Activate { window, token } => {
Action::Activate { window, token }
}
}
}
}

impl<T> fmt::Debug for Action<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Action::RequestToken { app_id, window, .. } => write!(
f,
"Action::ActivationAction::RequestToken {{ app_id: {:?}, window: {:?} }}",
app_id, window,
),
Action::Activate { window, token } => write!(
f,
"Action::ActivationAction::Activate {{ window: {:?}, token: {:?} }}",
window, token,
)
}
}
}
8 changes: 8 additions & 0 deletions runtime/src/command/platform_specific/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::fmt::Debug;

use iced_futures::MaybeSend;

/// activation Actions
pub mod activation;
/// data device Actions
pub mod data_device;
/// layer surface actions
Expand All @@ -21,6 +23,8 @@ pub enum Action<T> {
Popup(popup::Action<T>),
/// data device
DataDevice(data_device::Action<T>),
/// activation
Activation(activation::Action<T>),
}

impl<T> Action<T> {
Expand All @@ -38,6 +42,7 @@ impl<T> Action<T> {
Action::Window(a) => Action::Window(a.map(f)),
Action::Popup(a) => Action::Popup(a.map(f)),
Action::DataDevice(a) => Action::DataDevice(a.map(f)),
Action::Activation(a) => Action::Activation(a.map(f)),
}
}
}
Expand All @@ -53,6 +58,9 @@ impl<T> Debug for Action<T> {
Self::DataDevice(arg0) => {
f.debug_tuple("DataDevice").field(arg0).finish()
}
Self::Activation(arg0) => {
f.debug_tuple("Activation").field(arg0).finish()
}
}
}
}
12 changes: 10 additions & 2 deletions sctk/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ use sctk::{
seat::{keyboard::Modifiers, pointer::PointerEventKind},
};
use std::{
collections::HashMap, ffi::c_void, hash::Hash, marker::PhantomData,
time::Duration,
collections::HashMap, hash::Hash, marker::PhantomData, time::Duration,
};
use wayland_backend::client::ObjectId;
use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport;
Expand Down Expand Up @@ -92,6 +91,8 @@ pub enum Event<Message> {
Popup(platform_specific::wayland::popup::Action<Message>),
/// data device requests from the client
DataDevice(platform_specific::wayland::data_device::Action<Message>),
/// xdg-activation request from the client
Activation(platform_specific::wayland::activation::Action<Message>),
/// request sctk to set the cursor of the active pointer
SetCursor(Interaction),
/// Application Message
Expand Down Expand Up @@ -2063,6 +2064,13 @@ where
command::Action::PlatformSpecific(platform_specific::Action::Wayland(platform_specific::wayland::Action::DataDevice(data_device_action))) => {
proxy.send_event(Event::DataDevice(data_device_action));
}
command::Action::PlatformSpecific(
platform_specific::Action::Wayland(
platform_specific::wayland::Action::Activation(activation_action)
)
) => {
proxy.send_event(Event::Activation(activation_action));
}
_ => {}
};
None
Expand Down
30 changes: 30 additions & 0 deletions sctk/src/commands/activation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use iced_runtime::command::Command;
use iced_runtime::command::{
self,
platform_specific::{self, wayland},
};
use iced_runtime::window::Id as SurfaceId;

pub fn request_token<Message>(
app_id: Option<String>,
window: Option<SurfaceId>,
to_message: impl FnOnce(Option<String>) -> Message + Send + Sync + 'static,
) -> Command<Message> {
Command::single(command::Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::Activation(
wayland::activation::Action::RequestToken {
app_id,
window,
message: Box::new(to_message),
},
)),
))
}

pub fn activate<Message>(window: SurfaceId, token: String) -> Command<Message> {
Command::single(command::Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::Activation(
wayland::activation::Action::Activate { window, token },
)),
))
}
1 change: 1 addition & 0 deletions sctk/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Interact with the wayland objects of your application.

pub mod activation;
pub mod data_device;
pub mod layer_surface;
pub mod popup;
Expand Down
55 changes: 54 additions & 1 deletion sctk/src/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
conversion,
dpi::LogicalSize,
handlers::{
activation::IcedRequestData,
wp_fractional_scaling::FractionalScalingManager,
wp_viewporter::ViewporterState,
},
Expand All @@ -30,6 +31,7 @@ use iced_runtime::command::platform_specific::{
},
};
use sctk::{
activation::{ActivationState, RequestData},
compositor::CompositorState,
data_device_manager::DataDeviceManagerState,
output::OutputState,
Expand Down Expand Up @@ -179,6 +181,7 @@ where
&globals, &qh,
)
.expect("data device manager is not available"),
activation_state: ActivationState::bind(&globals, &qh).ok(),

queue_handle: qh,
loop_handle,
Expand Down Expand Up @@ -1297,7 +1300,57 @@ where
}
}
}
}
},
Event::Activation(activation_event) => match activation_event {
platform_specific::wayland::activation::Action::RequestToken { app_id, window, message } => {
if let Some(activation_state) = self.state.activation_state.as_ref() {
let (seat_and_serial, surface) = if let Some(id) = window {
let surface = self.state.windows.iter().find(|w| w.id == id)
.map(|w| w.window.wl_surface().clone())
.or_else(|| self.state.layer_surfaces.iter().find(|l| l.id == id)
.map(|l| l.surface.wl_surface().clone())
);
let seat_and_serial = surface.as_ref().and_then(|surface| {
self.state.seats.first().and_then(|seat| if seat.kbd_focus.as_ref().map(|focus| focus == surface).unwrap_or(false) {
seat.last_kbd_press.as_ref().map(|(_, serial)| (seat.seat.clone(), *serial))
} else if seat.ptr_focus.as_ref().map(|focus| focus == surface).unwrap_or(false) {
seat.last_ptr_press.as_ref().map(|(_, _, serial)| (seat.seat.clone(), *serial))
} else {
None
})
});

(seat_and_serial, surface)
} else {
(None, None)
};

activation_state.request_token_with_data(&self.state.queue_handle, IcedRequestData::new(
RequestData {
app_id,
seat_and_serial,
surface,
},
message,
));
} else {
// if we don't have the global, we don't want to stall the app
sticky_exit_callback(
IcedSctkEvent::UserEvent(message(None)),
&self.state,
&mut control_flow,
&mut callback,
)
}
},
platform_specific::wayland::activation::Action::Activate { window, token } => {
if let Some(activation_state) = self.state.activation_state.as_ref() {
if let Some(surface) = self.state.windows.iter().find(|w| w.id == window).map(|w| w.window.wl_surface()) {
activation_state.activate::<SctkState<T>>(surface, token)
}
}
},
},
}
}

Expand Down
2 changes: 2 additions & 0 deletions sctk/src/event_loop/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use iced_runtime::{
window,
};
use sctk::{
activation::ActivationState,
compositor::CompositorState,
data_device_manager::{
data_device::DataDevice,
Expand Down Expand Up @@ -329,6 +330,7 @@ pub struct SctkState<T> {
pub(crate) xdg_shell_state: XdgShell,
pub(crate) layer_shell: Option<LayerShell>,
pub(crate) data_device_manager_state: DataDeviceManagerState,
pub(crate) activation_state: Option<ActivationState>,
pub(crate) token_ctr: u32,
}

Expand Down
60 changes: 60 additions & 0 deletions sctk/src/handlers/activation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use std::sync::Mutex;

use sctk::{
activation::{ActivationHandler, RequestData, RequestDataExt},
delegate_activation,
reexports::client::protocol::{wl_seat::WlSeat, wl_surface::WlSurface},
};

use crate::event_loop::state::SctkState;

pub struct IcedRequestData<T> {
data: RequestData,
message: Mutex<
Option<Box<dyn FnOnce(Option<String>) -> T + Send + Sync + 'static>>,
>,
}

impl<T> IcedRequestData<T> {
pub fn new(
data: RequestData,
message: Box<dyn FnOnce(Option<String>) -> T + Send + Sync + 'static>,
) -> IcedRequestData<T> {
IcedRequestData {
data,
message: Mutex::new(Some(message)),
}
}
}

impl<T> RequestDataExt for IcedRequestData<T> {
fn app_id(&self) -> Option<&str> {
self.data.app_id()
}

fn seat_and_serial(&self) -> Option<(&WlSeat, u32)> {
self.data.seat_and_serial()
}

fn surface(&self) -> Option<&WlSurface> {
self.data.surface()
}
}

impl<T> ActivationHandler for SctkState<T> {
type RequestData = IcedRequestData<T>;

fn new_token(&mut self, token: String, data: &Self::RequestData) {
if let Some(message) = data.message.lock().unwrap().take() {
self.pending_user_events.push(
crate::application::Event::SctkEvent(
crate::sctk_event::IcedSctkEvent::UserEvent(message(Some(
token,
))),
),
);
} // else the compositor send two tokens???
}
}

delegate_activation!(@<T> SctkState<T>, IcedRequestData<T>);
1 change: 1 addition & 0 deletions sctk/src/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// handlers
pub mod activation;
pub mod compositor;
pub mod data_device;
pub mod output;
Expand Down

0 comments on commit 75eee59

Please sign in to comment.