Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xdg activation #76

Merged
merged 2 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ bitflags = "1.2"
thiserror = "1"
log = "0.4.17"
twox-hash = { version = "1.5", default-features = false }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "dc8c4a0", optional = true }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "2e9bf9f", optional = true }

[dependencies.palette]
version = "0.7"
Expand Down
2 changes: 1 addition & 1 deletion examples/sctk_drag/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "dc8c4a0" }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "2e9bf9f" }
iced = { path = "../..", default-features = false, features = ["wayland", "debug", "a11y"] }
iced_style = { path = "../../style" }
env_logger = "0.10"
Expand Down
2 changes: 1 addition & 1 deletion examples/sctk_todos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ iced = { path = "../..", default-features=false, features = ["async-std", "wayla
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
once_cell = "1.15"
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "dc8c4a0" }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "2e9bf9f" }
iced_style = { path = "../../style" }

log = "0.4.17"
Expand Down
2 changes: 1 addition & 1 deletion runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ wayland = ["iced_accessibility?/accesskit_unix", "iced_core/wayland", "sctk"]

[dependencies]
thiserror = "1"
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "dc8c4a0", optional = true }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "2e9bf9f", optional = true }

[dependencies.iced_core]
version = "0.10"
Expand Down
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()
}
}
}
}
2 changes: 1 addition & 1 deletion sctk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ a11y = ["iced_accessibility", "iced_runtime/a11y"]
[dependencies]
tracing = "0.1"
thiserror = "1.0"
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "dc8c4a0" }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "2e9bf9f" }
wayland-protocols = { version = "0.31.0", features = [ "staging"]}
# sctk = { package = "smithay-client-toolkit", path = "../../fork/client-toolkit/" }
raw-window-handle = "0.5"
Expand Down
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 @@
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 @@
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 @@ -1611,7 +1612,7 @@
self.frame = frame;
}

pub(crate) fn frame(&self) -> Option<&WlSurface> {

Check warning on line 1615 in sctk/src/application.rs

View workflow job for this annotation

GitHub Actions / native (ubuntu-latest, stable)

method `frame` is never used
self.frame.as_ref()
}

Expand Down Expand Up @@ -2063,6 +2064,13 @@
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
77 changes: 65 additions & 12 deletions 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,11 +31,12 @@ use iced_runtime::command::platform_specific::{
},
};
use sctk::{
activation::{ActivationState, RequestData},
compositor::CompositorState,
data_device_manager::DataDeviceManagerState,
output::OutputState,
reexports::{
calloop::{self, EventLoop},
calloop::{self, EventLoop, PostAction},
client::{
globals::registry_queue_init, protocol::wl_surface::WlSurface,
ConnectError, Connection, DispatchError, Proxy,
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 @@ -1157,11 +1160,11 @@ where
match self.event_loop.handle().insert_source(read_pipe, move |_, f, state| {
let mut dnd_offer = match state.dnd_offer.take() {
Some(s) => s,
None => return,
None => return PostAction::Continue,
};
let (mime_type, data, token) = match dnd_offer.cur_read.take() {
Some(s) => s,
None => return,
None => return PostAction::Continue,
};
let mut reader = BufReader::new(f.as_ref());
let consumed = match reader.fill_buf() {
Expand All @@ -1185,18 +1188,18 @@ where
Err(e) if matches!(e.kind(), std::io::ErrorKind::Interrupted) => {
dnd_offer.cur_read = Some((mime_type, data, token));
state.dnd_offer = Some(dnd_offer);
return;
return PostAction::Continue;
},
Err(e) => {
error!("Error reading selection data: {}", e);
loop_handle.remove(token);
if !dnd_offer.dropped {
state.dnd_offer = Some(dnd_offer);
}
return;
return PostAction::Remove;
},
};
reader.consume(consumed);
PostAction::Continue
}) {
Ok(token) => {
dnd_offer.cur_read = Some((mime_type.clone(), Vec::new(), token));
Expand All @@ -1215,11 +1218,11 @@ where
match self.event_loop.handle().insert_source(read_pipe, move |_, f, state| {
let selection_offer = match state.selection_offer.as_mut() {
Some(s) => s,
None => return,
None => return PostAction::Continue,
};
let (mime_type, data, token) = match selection_offer.cur_read.take() {
Some(s) => s,
None => return,
None => return PostAction::Continue,
};
let mut reader = BufReader::new(f.as_ref());
let consumed = match reader.fill_buf() {
Expand All @@ -1236,15 +1239,15 @@ where
},
Err(e) if matches!(e.kind(), std::io::ErrorKind::Interrupted) => {
selection_offer.cur_read = Some((mime_type, data, token));
return;
return PostAction::Continue;
},
Err(e) => {
error!("Error reading selection data: {}", e);
loop_handle.remove(token);
return;
return PostAction::Continue;
},
};
reader.consume(consumed);
PostAction::Continue
}) {
Ok(token) => {
selection_offer.cur_read = Some((mime_type.clone(), Vec::new(), token));
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
Loading
Loading