1
0
mirror of https://codeberg.org/hako/Rosenthal.git synced 2025-04-14 10:04:32 +00:00
Rosenthal/rosenthal/packages/patches/rust-smithay.patch
Hilton Chain f8c9cd9dcc
rosenthal: Add niri.
* rosenthal/packages/patches/niri.patch: New file.
* rosenthal/packages/patches/rust-libspa-sys.patch: New file.
* rosenthal/packages/patches/rust-libspa.patch: New file.
* rosenthal/packages/patches/rust-pipewire.patch: New file.
* rosenthal/packages/patches/rust-smithay.patch: New file.
* rosenthal/packages/rust-crates.scm (niri-cargo-inputs): New variable.
* rosenthal/packages/rust-apps.scm (niri): New variable.
* README.org (Packages): Add it.
* etc/manifest: Add it.
2025-02-26 21:33:15 +08:00

1902 lines
76 KiB
Diff

diff --git a/Cargo.toml b/Cargo.toml
index e3c6a34..76847c8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -218,23 +218,23 @@ version = "0.9.0"
optional = true
[dependencies.wayland-backend]
-version = "0.3.5"
+version = "0.3.6"
optional = true
[dependencies.wayland-client]
-version = "0.31.3"
+version = "0.31.8"
optional = true
[dependencies.wayland-cursor]
-version = "0.31.3"
+version = "0.31.8"
optional = true
[dependencies.wayland-egl]
-version = "0.32.0"
+version = "0.32.5"
optional = true
[dependencies.wayland-protocols]
-version = "0.32.5"
+version = "0.32.6"
features = [
"unstable",
"staging",
@@ -243,21 +243,21 @@ features = [
optional = true
[dependencies.wayland-protocols-misc]
-version = "0.3.1"
+version = "0.3.6"
features = ["server"]
optional = true
[dependencies.wayland-protocols-wlr]
-version = "0.3.1"
+version = "0.3.6"
features = ["server"]
optional = true
[dependencies.wayland-server]
-version = "0.31.0"
+version = "0.31.7"
optional = true
[dependencies.wayland-sys]
-version = "0.31"
+version = "0.31.6"
optional = true
[dependencies.winit]
diff --git a/src/utils/clock.rs b/src/utils/clock.rs
index bc44a39..78bf7f9 100644
--- a/src/utils/clock.rs
+++ b/src/utils/clock.rs
@@ -71,10 +71,12 @@ impl Time<Monotonic> {
/// This should match timestamps from libinput:
/// <https://wayland.freedesktop.org/libinput/doc/latest/timestamps.html>
pub fn as_millis(&self) -> u32 {
- // Assume monotonic clock (but not realitime) fits as milliseconds in 32-bit
debug_assert!(self.tp.tv_sec >= 0);
debug_assert!(self.tp.tv_nsec >= 0);
- self.tp.tv_sec as u32 * 1000 + self.tp.tv_nsec as u32 / 1000000
+
+ // The monotonic clock does not fit as milliseconds in 32-bit after ~50 days of uptime. We
+ // do a modulo conversion which should match what happens in libinput.
+ (self.as_micros() / 1000) as u32
}
/// Returns the time in microseconds
diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs
index d899f3b..581eb95 100644
--- a/src/utils/geometry.rs
+++ b/src/utils/geometry.rs
@@ -1115,13 +1115,13 @@ impl<Kind> Rectangle<f64, Kind> {
/// Convert to i32 by returning the largest integer-space rectangle fitting into the float-based rectangle
#[inline]
pub fn to_i32_down<N: Coordinate>(self) -> Rectangle<N, Kind> {
- Rectangle::from_extemities(self.loc.to_i32_ceil(), (self.loc + self.size).to_i32_floor())
+ Rectangle::from_extremities(self.loc.to_i32_ceil(), (self.loc + self.size).to_i32_floor())
}
/// Convert to i32 by returning the smallest integet-space rectangle encapsulating the float-based rectangle
#[inline]
pub fn to_i32_up<N: Coordinate>(self) -> Rectangle<N, Kind> {
- Rectangle::from_extemities(self.loc.to_i32_floor(), (self.loc + self.size).to_i32_ceil())
+ Rectangle::from_extremities(self.loc.to_i32_floor(), (self.loc + self.size).to_i32_ceil())
}
}
@@ -1162,9 +1162,20 @@ impl<N: Coordinate, Kind> Rectangle<N, Kind> {
/// Create a new [`Rectangle`] from the coordinates of its top-left corner and its bottom-right corner
#[inline]
+ #[deprecated = "use Rectangle::from_extremities instead"]
+ #[doc(hidden)]
pub fn from_extemities(
topleft: impl Into<Point<N, Kind>>,
bottomright: impl Into<Point<N, Kind>>,
+ ) -> Self {
+ Rectangle::from_extremities(topleft, bottomright)
+ }
+
+ /// Create a new [`Rectangle`] from the coordinates of its top-left corner and its bottom-right corner
+ #[inline]
+ pub fn from_extremities(
+ topleft: impl Into<Point<N, Kind>>,
+ bottomright: impl Into<Point<N, Kind>>,
) -> Self {
let topleft = topleft.into();
let bottomright = bottomright.into();
@@ -1236,7 +1247,7 @@ impl<N: Coordinate, Kind> Rectangle<N, Kind> {
if !self.overlaps(other) {
return None;
}
- Some(Rectangle::from_extemities(
+ Some(Rectangle::from_extremities(
(self.loc.x.max(other.loc.x), self.loc.y.max(other.loc.y)),
(
(self.loc.x.saturating_add(self.size.w)).min(other.loc.x.saturating_add(other.size.w)),
@@ -1257,7 +1268,7 @@ impl<N: Coordinate, Kind> Rectangle<N, Kind> {
match ret {
None => Rectangle::default(),
- Some((min_point, max_point)) => Rectangle::from_extemities(min_point, max_point),
+ Some((min_point, max_point)) => Rectangle::from_extremities(min_point, max_point),
}
}
diff --git a/src/utils/hook.rs b/src/utils/hook.rs
index b4b6bca..72ac9bc 100644
--- a/src/utils/hook.rs
+++ b/src/utils/hook.rs
@@ -3,8 +3,8 @@ use std::sync::Arc;
crate::utils::ids::id_gen!(hooks_id);
/// Unique hook identifier used to unregister commit/descruction hooks
-#[derive(Debug, Clone, Copy, Eq, PartialEq)]
-pub struct HookId(usize);
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct HookId(Arc<InnerId>);
pub(crate) struct Hook<T: ?Sized> {
pub id: HookId,
@@ -22,7 +22,7 @@ impl<T: ?Sized> std::fmt::Debug for Hook<T> {
impl<T: ?Sized> Clone for Hook<T> {
fn clone(&self) -> Self {
Self {
- id: self.id,
+ id: self.id.clone(),
cb: self.cb.clone(),
}
}
@@ -31,14 +31,23 @@ impl<T: ?Sized> Clone for Hook<T> {
impl<T: ?Sized> Hook<T> {
pub fn new(cb: Arc<T>) -> Self {
Self {
- id: HookId(hooks_id::next()),
+ id: HookId(Arc::new(InnerId::new())),
cb,
}
}
}
-impl<T: ?Sized> Drop for Hook<T> {
+#[derive(Debug, Eq, PartialEq)]
+struct InnerId(usize);
+
+impl InnerId {
+ fn new() -> Self {
+ Self(hooks_id::next())
+ }
+}
+
+impl Drop for InnerId {
fn drop(&mut self) {
- hooks_id::remove(self.id.0);
+ hooks_id::remove(self.0);
}
}
diff --git a/src/wayland/compositor/tree.rs b/src/wayland/compositor/tree.rs
index 4edae9d..2be3dc5 100644
--- a/src/wayland/compositor/tree.rs
+++ b/src/wayland/compositor/tree.rs
@@ -211,7 +211,7 @@ impl PrivateSurfaceData {
hook: impl Fn(&mut dyn Any, &DisplayHandle, &WlSurface) + Send + Sync + 'static,
) -> HookId {
let hook: Hook<CommitHook> = Hook::new(Arc::new(hook));
- let id = hook.id;
+ let id = hook.id.clone();
Self::lock_user_data(surface).pre_commit_hooks.push(hook);
id
}
@@ -221,7 +221,7 @@ impl PrivateSurfaceData {
hook: impl Fn(&mut dyn Any, &DisplayHandle, &WlSurface) + Send + Sync + 'static,
) -> HookId {
let hook: Hook<CommitHook> = Hook::new(Arc::new(hook));
- let id = hook.id;
+ let id = hook.id.clone();
Self::lock_user_data(surface).post_commit_hooks.push(hook);
id
}
@@ -231,7 +231,7 @@ impl PrivateSurfaceData {
hook: impl Fn(&mut dyn Any, &WlSurface) + Send + Sync + 'static,
) -> HookId {
let hook: Hook<DestructionHook> = Hook::new(Arc::new(hook));
- let id = hook.id;
+ let id = hook.id.clone();
Self::lock_user_data(surface).destruction_hooks.push(hook);
id
}
diff --git a/src/wayland/drm_syncobj/mod.rs b/src/wayland/drm_syncobj/mod.rs
index ff5e973..c27cffc 100644
--- a/src/wayland/drm_syncobj/mod.rs
+++ b/src/wayland/drm_syncobj/mod.rs
@@ -333,8 +333,8 @@ where
match request {
wp_linux_drm_syncobj_surface_v1::Request::Destroy => {
if let Ok(surface) = data.surface.upgrade() {
- compositor::remove_pre_commit_hook(&surface, data.commit_hook_id);
- compositor::remove_destruction_hook(&surface, data.destruction_hook_id);
+ compositor::remove_pre_commit_hook(&surface, data.commit_hook_id.clone());
+ compositor::remove_destruction_hook(&surface, data.destruction_hook_id.clone());
with_states(&surface, |states| {
*states
.data_map
diff --git a/src/wayland/idle_notify/mod.rs b/src/wayland/idle_notify/mod.rs
index f64e5fd..8f6febe 100644
--- a/src/wayland/idle_notify/mod.rs
+++ b/src/wayland/idle_notify/mod.rs
@@ -74,6 +74,9 @@ pub struct IdleNotificationUserData {
is_idle: AtomicBool,
timeout: Duration,
timer_token: Mutex<Option<RegistrationToken>>,
+
+ /// If listener was created with `get_input_idle_notification`
+ ignore_inhibitor: bool,
}
impl IdleNotificationUserData {
@@ -113,7 +116,7 @@ impl<D: IdleNotifierHandler> IdleNotifierState<D> {
D: IdleNotifierHandler,
D: 'static,
{
- let global = display.create_global::<D, ExtIdleNotifierV1, _>(1, ());
+ let global = display.create_global::<D, ExtIdleNotifierV1, _>(2, ());
Self {
global,
notifications: HashMap::new(),
@@ -131,9 +134,13 @@ impl<D: IdleNotifierHandler> IdleNotifierState<D> {
self.is_inhibited = is_inhibited;
for notification in self.notifications() {
- if is_inhibited {
- let data = notification.data::<IdleNotificationUserData>().unwrap();
+ let data = notification.data::<IdleNotificationUserData>().unwrap();
+
+ if data.ignore_inhibitor {
+ continue;
+ }
+ if is_inhibited {
if data.is_idle() {
notification.resumed();
data.set_idle(false);
@@ -189,7 +196,7 @@ impl<D: IdleNotifierHandler> IdleNotifierState<D> {
self.loop_handle.remove(token);
}
- if self.is_inhibited {
+ if !data.ignore_inhibitor && self.is_inhibited {
return;
}
@@ -200,7 +207,10 @@ impl<D: IdleNotifierHandler> IdleNotifierState<D> {
move |_, _, state| {
let data = idle_notification.data::<IdleNotificationUserData>().unwrap();
- if !state.idle_notifier_state().is_inhibited && !data.is_idle() {
+ let is_inhibited = !data.ignore_inhibitor && state.idle_notifier_state().is_inhibited;
+ let is_idle_already = data.is_idle();
+
+ if !is_inhibited && !is_idle_already {
idle_notification.idled();
data.set_idle(true);
}
@@ -275,6 +285,32 @@ where
is_idle: AtomicBool::new(false),
timeout,
timer_token: Mutex::new(None),
+ ignore_inhibitor: false,
+ },
+ );
+
+ idle_notifier_state.reinsert_timer(&idle_notification);
+
+ state
+ .idle_notifier_state()
+ .notifications
+ .entry(seat)
+ .or_default()
+ .push(idle_notification);
+ }
+ ext_idle_notifier_v1::Request::GetInputIdleNotification { id, timeout, seat } => {
+ let timeout = Duration::from_millis(timeout as u64);
+
+ let idle_notifier_state = state.idle_notifier_state();
+
+ let idle_notification = data_init.init(
+ id,
+ IdleNotificationUserData {
+ seat: seat.clone(),
+ is_idle: AtomicBool::new(false),
+ timeout,
+ timer_token: Mutex::new(None),
+ ignore_inhibitor: true,
},
);
diff --git a/src/wayland/input_method/input_method_handle.rs b/src/wayland/input_method/input_method_handle.rs
index b33949c..9644e85 100644
--- a/src/wayland/input_method/input_method_handle.rs
+++ b/src/wayland/input_method/input_method_handle.rs
@@ -74,9 +74,9 @@ impl InputMethodHandle {
}
/// Callback function to access the input method object
- pub(crate) fn with_instance<F>(&self, mut f: F)
+ pub(crate) fn with_instance<F>(&self, f: F)
where
- F: FnMut(&mut Instance),
+ F: FnOnce(&mut Instance),
{
let mut inner = self.inner.lock().unwrap();
if let Some(instance) = inner.instance.as_mut() {
@@ -103,20 +103,17 @@ impl InputMethodHandle {
pub(crate) fn set_text_input_rectangle<D: SeatHandler + 'static>(
&self,
state: &mut D,
- x: i32,
- y: i32,
- width: i32,
- height: i32,
+ rect: Rectangle<i32, Logical>,
) {
let mut inner = self.inner.lock().unwrap();
- inner.popup_handle.rectangle = Rectangle::new((x, y).into(), (width, height).into());
+ inner.popup_handle.rectangle = rect;
let mut popup_surface = match inner.popup_handle.surface.clone() {
Some(popup_surface) => popup_surface,
None => return,
};
- popup_surface.set_text_input_rectangle(x, y, width, height);
+ popup_surface.set_text_input_rectangle(rect.loc.x, rect.loc.y, rect.size.w, rect.size.h);
if let Some(instance) = &inner.instance {
let data = instance.object.data::<InputMethodUserData<D>>().unwrap();
@@ -149,14 +146,12 @@ impl InputMethodHandle {
/// Deactivate the active input method.
///
- /// The `done` is required in cases where the change in state is initiated not by text-input.
- pub(crate) fn deactivate_input_method<D: SeatHandler + 'static>(&self, state: &mut D, done: bool) {
+ /// The `done` is always send when deactivating IME.
+ pub(crate) fn deactivate_input_method<D: SeatHandler + 'static>(&self, state: &mut D) {
self.with_input_method(|im| {
if let Some(instance) = im.instance.as_mut() {
instance.object.deactivate();
- if done {
- instance.done();
- }
+ instance.done();
if let Some(popup) = im.popup_handle.surface.as_mut() {
let data = instance.object.data::<InputMethodUserData<D>>().unwrap();
if popup.get_parent().is_some() {
@@ -211,7 +206,7 @@ where
) {
match request {
zwp_input_method_v2::Request::CommitString { text } => {
- data.text_input_handle.with_focused_text_input(|ti, _surface| {
+ data.text_input_handle.with_active_text_input(|ti, _surface| {
ti.commit_string(Some(text.clone()));
});
}
@@ -220,7 +215,7 @@ where
cursor_begin,
cursor_end,
} => {
- data.text_input_handle.with_focused_text_input(|ti, _surface| {
+ data.text_input_handle.with_active_text_input(|ti, _surface| {
ti.preedit_string(Some(text.clone()), cursor_begin, cursor_end);
});
}
@@ -228,7 +223,7 @@ where
before_length,
after_length,
} => {
- data.text_input_handle.with_focused_text_input(|ti, _surface| {
+ data.text_input_handle.with_active_text_input(|ti, _surface| {
ti.delete_surrounding_text(before_length, after_length);
});
}
@@ -332,8 +327,6 @@ where
data: &InputMethodUserData<D>,
) {
data.handle.inner.lock().unwrap().instance = None;
- data.text_input_handle.with_focused_text_input(|ti, surface| {
- ti.leave(surface);
- });
+ data.text_input_handle.leave();
}
}
diff --git a/src/wayland/input_method/input_method_keyboard_grab.rs b/src/wayland/input_method/input_method_keyboard_grab.rs
index 2bdedcf..24ac2a7 100644
--- a/src/wayland/input_method/input_method_keyboard_grab.rs
+++ b/src/wayland/input_method/input_method_keyboard_grab.rs
@@ -54,7 +54,7 @@ where
let keyboard = inner.grab.as_ref().unwrap();
inner
.text_input_handle
- .focused_text_input_serial_or_default(serial.0, |serial| {
+ .active_text_input_serial_or_default(serial.0, |serial| {
keyboard.key(serial, time, keycode.raw() - 8, key_state.into());
if let Some(serialized) = modifiers.map(|m| m.serialized) {
keyboard.modifiers(
diff --git a/src/wayland/seat/keyboard.rs b/src/wayland/seat/keyboard.rs
index fd2d512..951c760 100644
--- a/src/wayland/seat/keyboard.rs
+++ b/src/wayland/seat/keyboard.rs
@@ -171,7 +171,7 @@ impl<D: SeatHandler + 'static> KeyboardTarget<D> for WlSurface {
let input_method = seat.input_method();
if input_method.has_instance() {
- input_method.deactivate_input_method(state, true);
+ input_method.deactivate_input_method(state);
}
// NOTE: Always set focus regardless whether the client actually has the
@@ -191,7 +191,7 @@ impl<D: SeatHandler + 'static> KeyboardTarget<D> for WlSurface {
let input_method = seat.input_method();
if input_method.has_instance() {
- input_method.deactivate_input_method(state, true);
+ input_method.deactivate_input_method(state);
text_input.leave();
}
diff --git a/src/wayland/selection/device.rs b/src/wayland/selection/device.rs
index 4027d9e..27c019d 100644
--- a/src/wayland/selection/device.rs
+++ b/src/wayland/selection/device.rs
@@ -1,6 +1,4 @@
-use std::any::Any;
-use std::any::TypeId;
-
+use wayland_protocols::ext::data_control::v1::server::ext_data_control_device_v1::ExtDataControlDeviceV1;
use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1 as PrimaryDevice;
use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_device_v1::ZwlrDataControlDeviceV1;
use wayland_server::backend::ObjectId;
@@ -9,16 +7,26 @@ use wayland_server::protocol::wl_seat::WlSeat;
use wayland_server::Resource;
use super::data_device::DataDeviceUserData;
+use super::ext_data_control::ExtDataControlDeviceUserData;
use super::offer::SelectionOffer;
use super::primary_selection::PrimaryDeviceUserData;
use super::private::selection_dispatch;
use super::wlr_data_control::DataControlDeviceUserData;
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub(crate) enum DataDeviceKind {
+ Core,
+ Primary,
+ WlrDataControl,
+ ExtDataControl,
+}
+
#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SelectionDevice {
+pub(crate) enum SelectionDevice {
DataDevice(WlDataDevice),
Primary(PrimaryDevice),
- DataControl(ZwlrDataControlDeviceV1),
+ WlrDataControl(ZwlrDataControlDeviceV1),
+ ExtDataControl(ExtDataControlDeviceV1),
}
impl SelectionDevice {
@@ -34,9 +42,13 @@ impl SelectionDevice {
selection_dispatch!(self; Self(device) => device.id())
}
- /// Get the [`TypeId`] of the underlying data device provider.
- pub fn inner_type_id(&self) -> TypeId {
- selection_dispatch!(self; Self(device) => device.type_id())
+ pub fn device_kind(&self) -> DataDeviceKind {
+ match self {
+ Self::DataDevice(_) => DataDeviceKind::Core,
+ Self::Primary(_) => DataDeviceKind::Primary,
+ Self::WlrDataControl(_) => DataDeviceKind::WlrDataControl,
+ Self::ExtDataControl(_) => DataDeviceKind::ExtDataControl,
+ }
}
/// [`WlSeat`] associated with this device.
@@ -50,10 +62,14 @@ impl SelectionDevice {
let data: &PrimaryDeviceUserData = device.data().unwrap();
data.wl_seat.clone()
}
- SelectionDevice::DataControl(device) => {
+ SelectionDevice::WlrDataControl(device) => {
let data: &DataControlDeviceUserData = device.data().unwrap();
data.wl_seat.clone()
}
+ SelectionDevice::ExtDataControl(device) => {
+ let data: &ExtDataControlDeviceUserData = device.data().unwrap();
+ data.wl_seat.clone()
+ }
}
}
@@ -63,9 +79,8 @@ impl SelectionDevice {
(Self::DataDevice(device), SelectionOffer::DataDevice(offer)) => {
device.selection(Some(offer));
}
- (Self::DataControl(device), SelectionOffer::DataControl(offer)) => {
- device.selection(Some(offer));
- }
+ (Self::WlrDataControl(obj), SelectionOffer::WlrDataControl(offer)) => obj.selection(Some(offer)),
+ (Self::ExtDataControl(obj), SelectionOffer::ExtDataControl(offer)) => obj.selection(Some(offer)),
_ => unreachable!("non-supported configuration for setting clipboard selection."),
}
}
@@ -73,7 +88,8 @@ impl SelectionDevice {
pub fn unset_selection(&self) {
match self {
Self::DataDevice(device) => device.selection(None),
- Self::DataControl(device) => device.selection(None),
+ Self::WlrDataControl(device) => device.selection(None),
+ Self::ExtDataControl(device) => device.selection(None),
Self::Primary(_) => unreachable!("primary clipboard has no clipboard selection"),
}
}
@@ -83,8 +99,11 @@ impl SelectionDevice {
(Self::Primary(device), SelectionOffer::Primary(offer)) => {
device.selection(Some(offer));
}
- (Self::DataControl(device), SelectionOffer::DataControl(offer)) => {
- device.primary_selection(Some(offer));
+ (Self::WlrDataControl(obj), SelectionOffer::WlrDataControl(offer)) => {
+ obj.primary_selection(Some(offer))
+ }
+ (Self::ExtDataControl(obj), SelectionOffer::ExtDataControl(offer)) => {
+ obj.primary_selection(Some(offer))
}
_ => unreachable!("non-supported configuration for setting clipboard selection."),
}
@@ -93,7 +112,8 @@ impl SelectionDevice {
pub fn unset_primary_selection(&self) {
match self {
Self::Primary(device) => device.selection(None),
- Self::DataControl(device) => device.primary_selection(None),
+ Self::WlrDataControl(device) => device.primary_selection(None),
+ Self::ExtDataControl(device) => device.primary_selection(None),
Self::DataDevice(_) => unreachable!("data control has primary selection"),
}
}
diff --git a/src/wayland/selection/ext_data_control/device.rs b/src/wayland/selection/ext_data_control/device.rs
new file mode 100644
index 0000000..64f82c6
--- /dev/null
+++ b/src/wayland/selection/ext_data_control/device.rs
@@ -0,0 +1,100 @@
+use std::cell::RefCell;
+
+use wayland_protocols::ext::data_control::v1::server::ext_data_control_device_v1::{
+ self, ExtDataControlDeviceV1,
+};
+use wayland_server::protocol::wl_seat::WlSeat;
+use wayland_server::{Client, Dispatch, DisplayHandle};
+
+use crate::input::Seat;
+use crate::wayland::selection::device::SelectionDevice;
+use crate::wayland::selection::offer::OfferReplySource;
+use crate::wayland::selection::seat_data::SeatData;
+use crate::wayland::selection::source::SelectionSourceProvider;
+use crate::wayland::selection::{SelectionSource, SelectionTarget};
+
+use super::{DataControlHandler, DataControlState};
+
+#[doc(hidden)]
+#[derive(Debug)]
+pub struct ExtDataControlDeviceUserData {
+ pub(crate) primary: bool,
+ pub(crate) wl_seat: WlSeat,
+}
+
+impl<D> Dispatch<ExtDataControlDeviceV1, ExtDataControlDeviceUserData, D> for DataControlState
+where
+ D: Dispatch<ExtDataControlDeviceV1, ExtDataControlDeviceUserData>,
+ D: DataControlHandler,
+ D: 'static,
+{
+ fn request(
+ handler: &mut D,
+ _client: &Client,
+ resource: &ExtDataControlDeviceV1,
+ request: <ExtDataControlDeviceV1 as wayland_server::Resource>::Request,
+ data: &ExtDataControlDeviceUserData,
+ dh: &DisplayHandle,
+ _: &mut wayland_server::DataInit<'_, D>,
+ ) {
+ let seat = match Seat::<D>::from_resource(&data.wl_seat) {
+ Some(seat) => seat,
+ None => return,
+ };
+
+ match request {
+ ext_data_control_device_v1::Request::SetSelection { source, .. } => {
+ seat.user_data()
+ .insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
+
+ let source = source.map(SelectionSourceProvider::ExtDataControl);
+
+ handler.new_selection(
+ SelectionTarget::Clipboard,
+ source.clone().map(|provider| SelectionSource { provider }),
+ seat.clone(),
+ );
+
+ seat.user_data()
+ .get::<RefCell<SeatData<D::SelectionUserData>>>()
+ .unwrap()
+ .borrow_mut()
+ .set_clipboard_selection::<D>(dh, source.map(OfferReplySource::Client));
+ }
+ ext_data_control_device_v1::Request::SetPrimarySelection { source, .. } => {
+ // When the primary selection is disabled, we should simply ignore the requests.
+ if !data.primary {
+ return;
+ }
+
+ seat.user_data()
+ .insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
+
+ let source = source.map(SelectionSourceProvider::ExtDataControl);
+
+ handler.new_selection(
+ SelectionTarget::Primary,
+ source.clone().map(|provider| SelectionSource { provider }),
+ seat.clone(),
+ );
+
+ seat.user_data()
+ .get::<RefCell<SeatData<D::SelectionUserData>>>()
+ .unwrap()
+ .borrow_mut()
+ .set_primary_selection::<D>(dh, source.map(OfferReplySource::Client));
+ }
+ ext_data_control_device_v1::Request::Destroy => seat
+ .user_data()
+ .get::<RefCell<SeatData<D::SelectionUserData>>>()
+ .unwrap()
+ .borrow_mut()
+ .retain_devices(|ndd| match ndd {
+ SelectionDevice::ExtDataControl(ndd) => ndd != resource,
+ _ => true,
+ }),
+
+ _ => unreachable!(),
+ }
+ }
+}
diff --git a/src/wayland/selection/ext_data_control/mod.rs b/src/wayland/selection/ext_data_control/mod.rs
new file mode 100644
index 0000000..f049f5d
--- /dev/null
+++ b/src/wayland/selection/ext_data_control/mod.rs
@@ -0,0 +1,255 @@
+//! Automatic handling of the `ext_data_control` protocol
+//!
+//! ## Initialization
+//!
+//! To initialize this implementation, create [`DataControlState`], store it in your `State`
+//! struct, and implement the required trait, as shown in the example:
+//!
+//! ```
+//! # extern crate wayland_server;
+//! # #[macro_use] extern crate smithay;
+//! use smithay::wayland::selection::SelectionHandler;
+//! use smithay::wayland::selection::ext_data_control::{DataControlState, DataControlHandler};
+//! # use smithay::input::{Seat, SeatHandler, SeatState, pointer::CursorImageStatus};
+//! # use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
+//!
+//! # struct State { data_control_state: DataControlState }
+//! # let mut display = wayland_server::Display::<State>::new().unwrap();
+//! // Create the data_control state
+//! let data_control_state = DataControlState::new::<State, _>(
+//! &display.handle(), None, |_| true
+//! );
+//!
+//! // insert the DataControlState into your state
+//! // ..
+//!
+//! // implement the necessary traits
+//! # impl SeatHandler for State {
+//! # type KeyboardFocus = WlSurface;
+//! # type PointerFocus = WlSurface;
+//! # type TouchFocus = WlSurface;
+//! # fn seat_state(&mut self) -> &mut SeatState<Self> { unimplemented!() }
+//! # fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) { unimplemented!() }
+//! # fn cursor_image(&mut self, seat: &Seat<Self>, image: CursorImageStatus) { unimplemented!() }
+//! # }
+//! impl SelectionHandler for State {
+//! type SelectionUserData = ();
+//! }
+//! impl DataControlHandler for State {
+//! fn data_control_state(&self) -> &DataControlState { &self.data_control_state }
+//! // ... override default implementations here to customize handling ...
+//! }
+//! delegate_ext_data_control!(State);
+//!
+//! // You're now ready to go!
+//! ```
+//!
+//! Be aware that data control clients rely on other selection providers to be implemneted, like
+//! wl_data_device or zwp_primary_selection.
+
+use wayland_protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1;
+use wayland_server::backend::GlobalId;
+use wayland_server::{Client, DisplayHandle, GlobalDispatch};
+
+mod device;
+mod source;
+
+pub use device::ExtDataControlDeviceUserData;
+pub use source::ExtDataControlSourceUserData;
+
+use super::primary_selection::PrimarySelectionState;
+use super::SelectionHandler;
+
+/// Access the data control state.
+pub trait DataControlHandler: Sized + SelectionHandler {
+ /// [`DataControlState`] getter.
+ fn data_control_state(&self) -> &DataControlState;
+}
+
+/// State of the data control.
+#[derive(Debug)]
+pub struct DataControlState {
+ manager_global: GlobalId,
+}
+
+impl DataControlState {
+ /// Register new [ExtDataControlManagerV1] global.
+ ///
+ /// Passing `primary_selection` will enable support for primary selection as well.
+ pub fn new<D, F>(
+ display: &DisplayHandle,
+ primary_selection: Option<&PrimarySelectionState>,
+ filter: F,
+ ) -> Self
+ where
+ D: GlobalDispatch<ExtDataControlManagerV1, ExtDataControlManagerGlobalData> + 'static,
+ F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
+ {
+ let data = ExtDataControlManagerGlobalData {
+ primary: primary_selection.is_some(),
+ filter: Box::new(filter),
+ };
+ let manager_global = display.create_global::<D, ExtDataControlManagerV1, _>(1, data);
+ Self { manager_global }
+ }
+
+ /// [ExtDataControlManagerV1] GlobalId getter.
+ pub fn global(&self) -> GlobalId {
+ self.manager_global.clone()
+ }
+}
+
+#[allow(missing_debug_implementations)]
+#[doc(hidden)]
+pub struct ExtDataControlManagerGlobalData {
+ /// Whether to allow primary selection.
+ primary: bool,
+
+ /// Filter whether the clients can view global.
+ filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
+}
+
+#[doc(hidden)]
+#[derive(Debug, Clone, Copy)]
+pub struct ExtDataControlManagerUserData {
+ /// Whether to allow primary selection.
+ primary: bool,
+}
+
+mod handlers {
+ use std::cell::RefCell;
+
+ use tracing::error;
+ use wayland_protocols::ext::data_control::v1::server::{
+ ext_data_control_device_v1::ExtDataControlDeviceV1,
+ ext_data_control_manager_v1::{self, ExtDataControlManagerV1},
+ ext_data_control_source_v1::ExtDataControlSourceV1,
+ };
+ use wayland_server::{Client, Dispatch, DisplayHandle, GlobalDispatch};
+
+ use crate::input::Seat;
+ use crate::wayland::selection::device::SelectionDevice;
+ use crate::wayland::selection::seat_data::SeatData;
+ use crate::wayland::selection::SelectionTarget;
+
+ use super::DataControlHandler;
+ use super::DataControlState;
+ use super::ExtDataControlDeviceUserData;
+ use super::ExtDataControlManagerGlobalData;
+ use super::ExtDataControlManagerUserData;
+ use super::ExtDataControlSourceUserData;
+
+ impl<D> GlobalDispatch<ExtDataControlManagerV1, ExtDataControlManagerGlobalData, D> for DataControlState
+ where
+ D: GlobalDispatch<ExtDataControlManagerV1, ExtDataControlManagerGlobalData>,
+ D: Dispatch<ExtDataControlManagerV1, ExtDataControlManagerUserData>,
+ D: Dispatch<ExtDataControlDeviceV1, ExtDataControlDeviceUserData>,
+ D: Dispatch<ExtDataControlSourceV1, ExtDataControlSourceUserData>,
+ D: DataControlHandler,
+ D: 'static,
+ {
+ fn bind(
+ _state: &mut D,
+ _handle: &DisplayHandle,
+ _client: &wayland_server::Client,
+ resource: wayland_server::New<ExtDataControlManagerV1>,
+ global_data: &ExtDataControlManagerGlobalData,
+ data_init: &mut wayland_server::DataInit<'_, D>,
+ ) {
+ data_init.init(
+ resource,
+ ExtDataControlManagerUserData {
+ primary: global_data.primary,
+ },
+ );
+ }
+
+ fn can_view(client: Client, global_data: &ExtDataControlManagerGlobalData) -> bool {
+ (global_data.filter)(&client)
+ }
+ }
+
+ impl<D> Dispatch<ExtDataControlManagerV1, ExtDataControlManagerUserData, D> for DataControlState
+ where
+ D: Dispatch<ExtDataControlManagerV1, ExtDataControlManagerUserData>,
+ D: Dispatch<ExtDataControlDeviceV1, ExtDataControlDeviceUserData>,
+ D: Dispatch<ExtDataControlSourceV1, ExtDataControlSourceUserData>,
+ D: DataControlHandler,
+ D: 'static,
+ {
+ fn request(
+ _handler: &mut D,
+ client: &wayland_server::Client,
+ _resource: &ExtDataControlManagerV1,
+ request: <ExtDataControlManagerV1 as wayland_server::Resource>::Request,
+ data: &ExtDataControlManagerUserData,
+ dh: &DisplayHandle,
+ data_init: &mut wayland_server::DataInit<'_, D>,
+ ) {
+ match request {
+ ext_data_control_manager_v1::Request::CreateDataSource { id } => {
+ data_init.init(id, ExtDataControlSourceUserData::new());
+ }
+ ext_data_control_manager_v1::Request::GetDataDevice { id, seat: wl_seat } => {
+ match Seat::<D>::from_resource(&wl_seat) {
+ Some(seat) => {
+ seat.user_data()
+ .insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
+
+ let device = SelectionDevice::ExtDataControl(data_init.init(
+ id,
+ ExtDataControlDeviceUserData {
+ wl_seat,
+ primary: data.primary,
+ },
+ ));
+
+ let mut seat_data = seat
+ .user_data()
+ .get::<RefCell<SeatData<D::SelectionUserData>>>()
+ .unwrap()
+ .borrow_mut();
+
+ seat_data.add_device(device.clone());
+
+ // NOTE: broadcast selection only to the newly created device.
+ let device = Some(&device);
+ seat_data.send_selection::<D>(dh, SelectionTarget::Clipboard, device, true);
+ if data.primary {
+ seat_data.send_selection::<D>(dh, SelectionTarget::Primary, device, true);
+ }
+ }
+ None => {
+ error!(
+ data_control_device = ?id,
+ client = ?client,
+ "Unmanaged seat given to a primary selection device."
+ );
+ }
+ }
+ }
+ ext_data_control_manager_v1::Request::Destroy => (),
+ _ => unreachable!(),
+ }
+ }
+ }
+}
+
+/// Macro to delegate implementation of the ext_data_control protocol
+#[macro_export]
+macro_rules! delegate_ext_data_control {
+ ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
+ $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
+ $crate::reexports::wayland_protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1: $crate::wayland::selection::ext_data_control::ExtDataControlManagerGlobalData
+ ] => $crate::wayland::selection::ext_data_control::DataControlState);
+ $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
+ $crate::reexports::wayland_protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1: $crate::wayland::selection::ext_data_control::ExtDataControlManagerUserData
+ ] => $crate::wayland::selection::ext_data_control::DataControlState);
+ $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
+ $crate::reexports::wayland_protocols::ext::data_control::v1::server::ext_data_control_device_v1::ExtDataControlDeviceV1: $crate::wayland::selection::ext_data_control::ExtDataControlDeviceUserData
+ ] => $crate::wayland::selection::ext_data_control::DataControlState);
+ $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
+ $crate::reexports::wayland_protocols::ext::data_control::v1::server::ext_data_control_source_v1::ExtDataControlSourceV1: $crate::wayland::selection::ext_data_control::ExtDataControlSourceUserData
+ ] => $crate::wayland::selection::ext_data_control::DataControlState);
+ };
+}
diff --git a/src/wayland/selection/ext_data_control/source.rs b/src/wayland/selection/ext_data_control/source.rs
new file mode 100644
index 0000000..e254d99
--- /dev/null
+++ b/src/wayland/selection/ext_data_control/source.rs
@@ -0,0 +1,76 @@
+use std::sync::Mutex;
+
+use wayland_server::backend::ClientId;
+use wayland_server::{Dispatch, DisplayHandle, Resource};
+
+use crate::utils::alive_tracker::AliveTracker;
+use crate::utils::IsAlive;
+
+use wayland_protocols::ext::data_control::v1::server::ext_data_control_source_v1::{
+ self, ExtDataControlSourceV1,
+};
+
+use super::{DataControlHandler, DataControlState};
+
+#[doc(hidden)]
+#[derive(Default, Debug)]
+pub struct ExtDataControlSourceUserData {
+ pub(crate) inner: Mutex<SourceMetadata>,
+ alive_tracker: AliveTracker,
+}
+
+impl ExtDataControlSourceUserData {
+ pub(crate) fn new() -> Self {
+ Self::default()
+ }
+}
+
+/// The metadata describing a data source
+#[derive(Debug, Default, Clone)]
+pub struct SourceMetadata {
+ /// The MIME types supported by this source
+ pub mime_types: Vec<String>,
+}
+
+impl<D> Dispatch<ExtDataControlSourceV1, ExtDataControlSourceUserData, D> for DataControlState
+where
+ D: Dispatch<ExtDataControlSourceV1, ExtDataControlSourceUserData>,
+ D: DataControlHandler,
+ D: 'static,
+{
+ fn request(
+ _state: &mut D,
+ _client: &wayland_server::Client,
+ _resource: &ExtDataControlSourceV1,
+ request: <ExtDataControlSourceV1 as wayland_server::Resource>::Request,
+ data: &ExtDataControlSourceUserData,
+ _dhandle: &DisplayHandle,
+ _data_init: &mut wayland_server::DataInit<'_, D>,
+ ) {
+ match request {
+ ext_data_control_source_v1::Request::Offer { mime_type } => {
+ let mut data = data.inner.lock().unwrap();
+ data.mime_types.push(mime_type);
+ }
+ ext_data_control_source_v1::Request::Destroy => (),
+ _ => unreachable!(),
+ }
+ }
+
+ fn destroyed(
+ _state: &mut D,
+ _client: ClientId,
+ _resource: &ExtDataControlSourceV1,
+ data: &ExtDataControlSourceUserData,
+ ) {
+ data.alive_tracker.destroy_notify();
+ }
+}
+
+impl IsAlive for ExtDataControlSourceV1 {
+ #[inline]
+ fn alive(&self) -> bool {
+ let data: &ExtDataControlSourceUserData = self.data().unwrap();
+ data.alive_tracker.alive()
+ }
+}
diff --git a/src/wayland/selection/mod.rs b/src/wayland/selection/mod.rs
index 75cb897..7391a89 100644
--- a/src/wayland/selection/mod.rs
+++ b/src/wayland/selection/mod.rs
@@ -16,6 +16,7 @@ use std::os::unix::io::OwnedFd;
use crate::input::{Seat, SeatHandler};
pub mod data_device;
+pub mod ext_data_control;
pub mod primary_selection;
pub mod wlr_data_control;
@@ -67,7 +68,8 @@ pub(crate) mod private {
/// match self {
/// Enum::DataDevice(foo) => foo.something(),
/// Enum::Primary(foo) => foo.something(),
- /// Enum::DataControl(foo) => foo.something(),
+ /// Enum::WlrDataControl(foo) => foo.something(),
+ /// Enum::ExtDataControl(foo) => foo.something(),
/// }
/// ```
///
@@ -77,7 +79,8 @@ pub(crate) mod private {
/// match (self, other) {
/// (Enum::DataDevice(foo), EnumNext::DataDevice(zoo)) => foo.something(zoo),
/// (Enum::Primary(foo), EnumNext::Primary(zoo)) => foo.something(zoo),
- /// (Enum::DataControl(foo), EnumNext::DataControl(zoo)) => foo.something(zoo),
+ /// (Enum::WlrDataControl(foo), EnumNext::WlrDataControl(zoo)) => foo.something(zoo),
+ /// (Enum::ExtDataControl(foo), EnumNext::ExtDataControl(zoo)) => foo.something(zoo),
/// _ => unreachable!(),
/// }
/// ```
@@ -88,14 +91,16 @@ pub(crate) mod private {
match $what {
$enum::DataDevice($($c1)*) => $x,
$enum::Primary($($c1)*) => $x,
- $enum::DataControl($($c1)*) => $x,
+ $enum::WlrDataControl($($c1)*) => $x,
+ $enum::ExtDataControl($($c1)*) => $x,
}
};
($what:ident$(, $what_next:ident)+; $enum:ident ( $($c1:tt)*) $(, $enum_next:ident ( $($c2:tt)* ) )+ => $x:expr) => {
match ($what$(, $what_next)*) {
($enum::DataDevice($($c1)*)$(, $enum_next::DataDevice($($c2)*))*) => $x,
($enum::Primary($($c1)*)$(, $enum_next::Primary($($c2)*))*) => $x,
- ($enum::DataControl($($c1)*)$(, $enum_next::DataControl($($c2)*))*) => $x,
+ ($enum::WlrDataControl($($c1)*)$(, $enum_next::WlrDataControl($($c2)*))*) => $x,
+ ($enum::ExtDataControl($($c1)*)$(, $enum_next::ExtDataControl($($c2)*))*) => $x,
_ => unreachable!(),
}
};
diff --git a/src/wayland/selection/offer.rs b/src/wayland/selection/offer.rs
index 16fc187..f072a56 100644
--- a/src/wayland/selection/offer.rs
+++ b/src/wayland/selection/offer.rs
@@ -1,19 +1,19 @@
-use std::any::TypeId;
use std::os::unix::io::OwnedFd;
use std::sync::Arc;
use tracing::debug;
-use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1 as PrimaryDevice;
+use wayland_protocols::ext::data_control::v1::server::ext_data_control_offer_v1::{
+ self, ExtDataControlOfferV1,
+};
use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_offer_v1::{
self, ZwpPrimarySelectionOfferV1 as PrimaryOffer,
};
use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_offer_v1::{
- self, ZwlrDataControlOfferV1 as DataControlOffer,
+ self, ZwlrDataControlOfferV1,
};
use wayland_server::backend::protocol::Message;
use wayland_server::backend::ObjectId;
use wayland_server::backend::{ClientId, Handle, ObjectData};
-use wayland_server::protocol::wl_data_device::WlDataDevice;
use wayland_server::protocol::wl_data_offer;
use wayland_server::protocol::wl_seat::WlSeat;
use wayland_server::DisplayHandle;
@@ -24,7 +24,7 @@ use zwp_primary_selection_offer_v1::Request as PrimaryRequest;
use crate::input::Seat;
-use super::device::SelectionDevice;
+use super::device::{DataDeviceKind, SelectionDevice};
use super::private::selection_dispatch;
use super::source::{CompositorSelectionProvider, SelectionSourceProvider};
use super::SelectionHandler;
@@ -57,10 +57,11 @@ impl<U: Clone + Send + Sync + 'static> OfferReplySource<U> {
/// Offer representing various selection offers.
#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SelectionOffer {
+pub(crate) enum SelectionOffer {
DataDevice(WlDataOffer),
Primary(PrimaryOffer),
- DataControl(DataControlOffer),
+ WlrDataControl(ZwlrDataControlOfferV1),
+ ExtDataControl(ExtDataControlOfferV1),
}
impl SelectionOffer {
@@ -76,32 +77,34 @@ impl SelectionOffer {
// NOTE: the types are tied to the `SelectionDevice`, so every
// RTTI like checking is safe and reliable.
- let type_id = device.inner_type_id();
+ let device_kind = device.device_kind();
let data = Arc::new(OfferReplyData {
- type_id,
+ device_kind,
seat: device.seat(),
source: data,
});
let backend = dh.backend_handle();
- let interface = if type_id == TypeId::of::<WlDataDevice>() {
- WlDataOffer::interface()
- } else if type_id == TypeId::of::<PrimaryDevice>() {
- PrimaryOffer::interface()
- } else {
- DataControlOffer::interface()
+ let interface = match device_kind {
+ DataDeviceKind::Core => WlDataOffer::interface(),
+ DataDeviceKind::Primary => PrimaryOffer::interface(),
+ DataDeviceKind::WlrDataControl => ZwlrDataControlOfferV1::interface(),
+ DataDeviceKind::ExtDataControl => ExtDataControlOfferV1::interface(),
};
let offer = backend
.create_object::<D>(client_id, interface, device.version(), data)
.unwrap();
- if type_id == TypeId::of::<WlDataDevice>() {
- Self::DataDevice(WlDataOffer::from_id(dh, offer).unwrap())
- } else if type_id == TypeId::of::<PrimaryDevice>() {
- Self::Primary(PrimaryOffer::from_id(dh, offer).unwrap())
- } else {
- Self::DataControl(DataControlOffer::from_id(dh, offer).unwrap())
+ match device_kind {
+ DataDeviceKind::Core => Self::DataDevice(WlDataOffer::from_id(dh, offer).unwrap()),
+ DataDeviceKind::Primary => Self::Primary(PrimaryOffer::from_id(dh, offer).unwrap()),
+ DataDeviceKind::WlrDataControl => {
+ Self::WlrDataControl(ZwlrDataControlOfferV1::from_id(dh, offer).unwrap())
+ }
+ DataDeviceKind::ExtDataControl => {
+ Self::ExtDataControl(ExtDataControlOfferV1::from_id(dh, offer).unwrap())
+ }
}
}
@@ -111,7 +114,7 @@ impl SelectionOffer {
}
struct OfferReplyData<U: Clone + Send + Sync + 'static> {
- type_id: TypeId,
+ device_kind: DataDeviceKind,
source: OfferReplySource<U>,
seat: WlSeat,
}
@@ -128,33 +131,47 @@ where
msg: Message<ObjectId, OwnedFd>,
) -> Option<Arc<dyn ObjectData<D>>> {
let dh = DisplayHandle::from(dh.clone());
- let type_id = self.type_id;
// NOTE: we can't parse message more than once, since it expects the `OwnedFd` which
// we can't clone. To achieve that, we use RTTI passed along the selection data, to
// make the parsing work only once.
- let (mime_type, fd, object_name) = if type_id == TypeId::of::<WlDataDevice>() {
- if let Ok((_resource, DataOfferRequest::Receive { mime_type, fd })) =
- WlDataOffer::parse_request(&dh, msg)
- {
- (mime_type, fd, "wl_data_offer")
- } else {
- return None;
+ let (mime_type, fd, object_name) = match self.device_kind {
+ DataDeviceKind::Core => {
+ if let Ok((_resource, DataOfferRequest::Receive { mime_type, fd })) =
+ WlDataOffer::parse_request(&dh, msg)
+ {
+ (mime_type, fd, "wl_data_offer")
+ } else {
+ return None;
+ }
}
- } else if type_id == TypeId::of::<PrimaryDevice>() {
- if let Ok((_resource, PrimaryRequest::Receive { mime_type, fd })) =
- PrimaryOffer::parse_request(&dh, msg)
- {
- (mime_type, fd, "primary_selection_offer")
- } else {
- return None;
+ DataDeviceKind::Primary => {
+ if let Ok((_resource, PrimaryRequest::Receive { mime_type, fd })) =
+ PrimaryOffer::parse_request(&dh, msg)
+ {
+ (mime_type, fd, "primary_selection_offer")
+ } else {
+ return None;
+ }
+ }
+ DataDeviceKind::WlrDataControl => {
+ if let Ok((_resource, DataControlRequest::Receive { mime_type, fd })) =
+ ZwlrDataControlOfferV1::parse_request(&dh, msg)
+ {
+ (mime_type, fd, "wlr_data_control_offer")
+ } else {
+ return None;
+ }
+ }
+ DataDeviceKind::ExtDataControl => {
+ if let Ok((_resource, ext_data_control_offer_v1::Request::Receive { mime_type, fd })) =
+ ExtDataControlOfferV1::parse_request(&dh, msg)
+ {
+ (mime_type, fd, "ext_data_control_offer")
+ } else {
+ return None;
+ }
}
- } else if let Ok((_resource, DataControlRequest::Receive { mime_type, fd })) =
- DataControlOffer::parse_request(&dh, msg)
- {
- (mime_type, fd, "data_control_offer")
- } else {
- return None;
};
if !self.source.contains_mime_type(&mime_type) {
diff --git a/src/wayland/selection/seat_data.rs b/src/wayland/selection/seat_data.rs
index 79205c7..1b13815 100644
--- a/src/wayland/selection/seat_data.rs
+++ b/src/wayland/selection/seat_data.rs
@@ -10,8 +10,8 @@ use super::{SelectionHandler, SelectionTarget};
/// Seat data used to handle regular selection operations.
///
-/// The data is shared accross primary, data device, and data control selections.
-pub struct SeatData<U: Clone + Sync + Send + 'static> {
+/// The data is shared across primary, data device, and data control selections.
+pub(crate) struct SeatData<U: Clone + Sync + Send + 'static> {
known_devices: Vec<SelectionDevice>,
clipboard_selection: Option<OfferReplySource<U>>,
clipboard_selection_focus: Option<Client>,
@@ -20,7 +20,7 @@ pub struct SeatData<U: Clone + Sync + Send + 'static> {
}
impl<U: Clone + Send + Sync + 'static> SeatData<U> {
- /// Create a new [`SeatData`] with emply selections and without focusing any client.
+ /// Create a new [`SeatData`] with empty selections and without focusing any client.
pub fn new() -> Self {
Default::default()
}
@@ -166,21 +166,24 @@ impl<U: Clone + Send + Sync + 'static> SeatData<U> {
// later on.
SelectionDevice::DataDevice(_) => ty == SelectionTarget::Clipboard,
SelectionDevice::Primary(_) => ty == SelectionTarget::Primary,
- SelectionDevice::DataControl(data_control) => {
+ SelectionDevice::WlrDataControl(data_control) => {
// Primary selection is available for data control only since v2.
update_data_control
&& (data_control.version() >= EVT_PRIMARY_SELECTION_SINCE
|| ty != SelectionTarget::Primary)
}
+ SelectionDevice::ExtDataControl(_) => update_data_control,
})
{
// Data control doesn't require focus and should always get selection updates, unless
// it was requested not to update them.
- if !matches!(device, SelectionDevice::DataControl(_))
- && dh
- .get_client(device.id())
- .map(|c| Some(&c) != client)
- .unwrap_or(true)
+ if !matches!(
+ device,
+ SelectionDevice::WlrDataControl(_) | SelectionDevice::ExtDataControl(_)
+ ) && dh
+ .get_client(device.id())
+ .map(|c| Some(&c) != client)
+ .unwrap_or(true)
{
continue;
}
@@ -198,7 +201,10 @@ impl<U: Clone + Send + Sync + 'static> SeatData<U> {
// DataControl devices is the client itself, however other devices use
// the currently focused one as a client.
let client_id = match device {
- SelectionDevice::DataControl(device) => {
+ SelectionDevice::WlrDataControl(device) => {
+ dh.get_client(device.id()).ok().map(|c| c.id())
+ }
+ SelectionDevice::ExtDataControl(device) => {
dh.get_client(device.id()).ok().map(|c| c.id())
}
_ => client.map(|c| c.id()),
diff --git a/src/wayland/selection/source.rs b/src/wayland/selection/source.rs
index e02c427..345e1ff 100644
--- a/src/wayland/selection/source.rs
+++ b/src/wayland/selection/source.rs
@@ -1,13 +1,15 @@
use std::os::unix::io::{AsFd, OwnedFd};
+use wayland_protocols::ext::data_control::v1::server::ext_data_control_source_v1::ExtDataControlSourceV1;
use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1 as PrimarySource;
-use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_source_v1::ZwlrDataControlSourceV1 as DataControlSource;
+use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_source_v1::ZwlrDataControlSourceV1;
use wayland_server::{protocol::wl_data_source::WlDataSource, Resource};
use crate::utils::IsAlive;
use crate::wayland::selection::primary_selection::PrimarySourceUserData;
use super::data_device::DataSourceUserData;
+use super::ext_data_control::ExtDataControlSourceUserData;
use super::private::selection_dispatch;
use super::wlr_data_control::DataControlSourceUserData;
use super::SelectionTarget;
@@ -34,7 +36,9 @@ pub enum SelectionSourceProvider {
/// The primary selection was used as a source.
Primary(PrimarySource),
/// The data control selection was used as source.
- DataControl(DataControlSource),
+ WlrDataControl(ZwlrDataControlSourceV1),
+ /// The data control selection was used as source.
+ ExtDataControl(ExtDataControlSourceV1),
}
impl SelectionSourceProvider {
@@ -59,10 +63,14 @@ impl SelectionSourceProvider {
let data: &PrimarySourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.contains(mime_type)
}
- Self::DataControl(source) => {
+ Self::WlrDataControl(source) => {
let data: &DataControlSourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.contains(mime_type)
}
+ Self::ExtDataControl(source) => {
+ let data: &ExtDataControlSourceUserData = source.data().unwrap();
+ data.inner.lock().unwrap().mime_types.contains(mime_type)
+ }
}
}
@@ -77,10 +85,14 @@ impl SelectionSourceProvider {
let data: &PrimarySourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.clone()
}
- Self::DataControl(source) => {
+ Self::WlrDataControl(source) => {
let data: &DataControlSourceUserData = source.data().unwrap();
data.inner.lock().unwrap().mime_types.clone()
}
+ Self::ExtDataControl(source) => {
+ let data: &ExtDataControlSourceUserData = source.data().unwrap();
+ data.inner.lock().unwrap().mime_types.clone()
+ }
}
}
}
diff --git a/src/wayland/selection/wlr_data_control/device.rs b/src/wayland/selection/wlr_data_control/device.rs
index 0867817..2cb3914 100644
--- a/src/wayland/selection/wlr_data_control/device.rs
+++ b/src/wayland/selection/wlr_data_control/device.rs
@@ -48,7 +48,7 @@ where
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
- let source = source.map(SelectionSourceProvider::DataControl);
+ let source = source.map(SelectionSourceProvider::WlrDataControl);
handler.new_selection(
SelectionTarget::Clipboard,
@@ -71,7 +71,7 @@ where
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
- let source = source.map(SelectionSourceProvider::DataControl);
+ let source = source.map(SelectionSourceProvider::WlrDataControl);
handler.new_selection(
SelectionTarget::Primary,
@@ -91,7 +91,7 @@ where
.unwrap()
.borrow_mut()
.retain_devices(|ndd| match ndd {
- SelectionDevice::DataControl(ndd) => ndd != resource,
+ SelectionDevice::WlrDataControl(ndd) => ndd != resource,
_ => true,
}),
diff --git a/src/wayland/selection/wlr_data_control/mod.rs b/src/wayland/selection/wlr_data_control/mod.rs
index fa9f3d2..0c3b816 100644
--- a/src/wayland/selection/wlr_data_control/mod.rs
+++ b/src/wayland/selection/wlr_data_control/mod.rs
@@ -201,7 +201,7 @@ mod handlers {
seat.user_data()
.insert_if_missing(|| RefCell::new(SeatData::<D::SelectionUserData>::new()));
- let device = SelectionDevice::DataControl(data_init.init(
+ let device = SelectionDevice::WlrDataControl(data_init.init(
id,
DataControlDeviceUserData {
wl_seat,
diff --git a/src/wayland/shm/mod.rs b/src/wayland/shm/mod.rs
index c40de81..184fb1f 100644
--- a/src/wayland/shm/mod.rs
+++ b/src/wayland/shm/mod.rs
@@ -486,7 +486,7 @@ impl ShmBufferUserData {
hook: impl Fn(&mut dyn Any, &wl_buffer::WlBuffer) + Send + Sync + 'static,
) -> HookId {
let hook: Hook<DestructionHook> = Hook::new(Arc::new(hook));
- let id = hook.id;
+ let id = hook.id.clone();
self.destruction_hooks.lock().unwrap().push(hook);
id
}
diff --git a/src/wayland/text_input/text_input_handle.rs b/src/wayland/text_input/text_input_handle.rs
index 9d81ec0..4b4ccd7 100644
--- a/src/wayland/text_input/text_input_handle.rs
+++ b/src/wayland/text_input/text_input_handle.rs
@@ -1,46 +1,64 @@
+use std::mem;
use std::sync::{Arc, Mutex};
use tracing::debug;
-use wayland_protocols::wp::text_input::zv3::server::zwp_text_input_v3::{self, ZwpTextInputV3};
+use wayland_protocols::wp::text_input::zv3::server::zwp_text_input_v3::{
+ self, ChangeCause, ContentHint, ContentPurpose, ZwpTextInputV3,
+};
use wayland_server::backend::{ClientId, ObjectId};
use wayland_server::{protocol::wl_surface::WlSurface, Dispatch, Resource};
use crate::input::SeatHandler;
-use crate::utils::IsAlive;
+use crate::utils::{Logical, Rectangle};
use crate::wayland::input_method::InputMethodHandle;
use super::TextInputManagerState;
-#[derive(Debug)]
-struct Instance {
- instance: ZwpTextInputV3,
- serial: u32,
-}
-
#[derive(Default, Debug)]
pub(crate) struct TextInput {
instances: Vec<Instance>,
focus: Option<WlSurface>,
- enabled_resource_id: Option<ObjectId>,
+ active_text_input_id: Option<ObjectId>,
}
impl TextInput {
- fn with_focused_text_input<F>(&mut self, mut f: F)
+ fn with_focused_client_all_text_inputs<F>(&mut self, mut f: F)
where
F: FnMut(&ZwpTextInputV3, &WlSurface, u32),
{
- if let (Some(surface), Some(enabled_resource_id)) = (&self.focus, &self.enabled_resource_id) {
- if !surface.alive() {
- return;
- }
-
- for ti in self.instances.iter_mut() {
- let instance_id = ti.instance.id();
- if instance_id.same_client_as(&surface.id()) && instance_id.eq(enabled_resource_id) {
- f(&ti.instance, surface, ti.serial);
+ if let Some(surface) = self.focus.as_ref().filter(|surface| surface.is_alive()) {
+ for text_input in self.instances.iter() {
+ let instance_id = text_input.instance.id();
+ if instance_id.same_client_as(&surface.id()) {
+ f(&text_input.instance, surface, text_input.serial);
break;
}
}
+ };
+ }
+
+ fn with_active_text_input<F>(&mut self, mut f: F)
+ where
+ F: FnMut(&ZwpTextInputV3, &WlSurface, u32),
+ {
+ let active_id = match &self.active_text_input_id {
+ Some(active_text_input_id) => active_text_input_id,
+ None => return,
+ };
+
+ let surface = match self.focus.as_ref().filter(|surface| surface.is_alive()) {
+ Some(surface) => surface,
+ None => return,
+ };
+
+ let surface_id = surface.id();
+ if let Some(text_input) = self
+ .instances
+ .iter()
+ .filter(|instance| instance.instance.id().same_client_as(&surface_id))
+ .find(|instance| &instance.instance.id() == active_id)
+ {
+ f(&text_input.instance, surface, text_input.serial);
}
}
}
@@ -57,15 +75,20 @@ impl TextInputHandle {
inner.instances.push(Instance {
instance: instance.clone(),
serial: 0,
+ pending_state: Default::default(),
});
}
fn increment_serial(&self, text_input: &ZwpTextInputV3) {
- let mut inner = self.inner.lock().unwrap();
- for ti in inner.instances.iter_mut() {
- if &ti.instance == text_input {
- ti.serial += 1;
- }
+ if let Some(instance) = self
+ .inner
+ .lock()
+ .unwrap()
+ .instances
+ .iter_mut()
+ .find(|instance| instance.instance == *text_input)
+ {
+ instance.serial += 1
}
}
@@ -81,18 +104,14 @@ impl TextInputHandle {
self.inner.lock().unwrap().focus = surface;
}
- fn set_enabled_resource_id(&self, resource_id: Option<ObjectId>) {
- let mut inner = self.inner.lock().unwrap();
- if inner.enabled_resource_id.is_some() != resource_id.is_some() {
- inner.enabled_resource_id = resource_id;
- }
- }
-
/// Send `leave` on the text-input instance for the currently focused
/// surface.
pub fn leave(&self) {
let mut inner = self.inner.lock().unwrap();
- inner.with_focused_text_input(|text_input, focus, _| {
+ // Leaving clears the active text input.
+ inner.active_text_input_id = None;
+ // NOTE: we implement it in a symmetrical way with `enter`.
+ inner.with_focused_client_all_text_inputs(|text_input, focus, _| {
text_input.leave(focus);
});
}
@@ -101,7 +120,9 @@ impl TextInputHandle {
/// surface.
pub fn enter(&self) {
let mut inner = self.inner.lock().unwrap();
- inner.with_focused_text_input(|text_input, focus, _| {
+ // NOTE: protocol states that if we have multiple text inputs enabled, `enter` must
+ // be send for each of them.
+ inner.with_focused_client_all_text_inputs(|text_input, focus, _| {
text_input.enter(focus);
});
}
@@ -110,7 +131,7 @@ impl TextInputHandle {
/// the state should be discarded and wrong serial sent.
pub fn done(&self, discard_state: bool) {
let mut inner = self.inner.lock().unwrap();
- inner.with_focused_text_input(|text_input, _, serial| {
+ inner.with_active_text_input(|text_input, _, serial| {
if discard_state {
debug!("discarding text-input state due to serial");
// Discarding is done by sending non-matching serial.
@@ -127,20 +148,31 @@ impl TextInputHandle {
F: FnMut(&ZwpTextInputV3, &WlSurface),
{
let mut inner = self.inner.lock().unwrap();
- inner.with_focused_text_input(|ti, surface, _| {
+ inner.with_focused_client_all_text_inputs(|ti, surface, _| {
f(ti, surface);
});
}
- /// Call the callback with the serial of the focused text_input or with the passed
+ /// Access the active text-input instance for the currently focused surface.
+ pub fn with_active_text_input<F>(&self, mut f: F)
+ where
+ F: FnMut(&ZwpTextInputV3, &WlSurface),
+ {
+ let mut inner = self.inner.lock().unwrap();
+ inner.with_active_text_input(|ti, surface, _| {
+ f(ti, surface);
+ });
+ }
+
+ /// Call the callback with the serial of the active text_input or with the passed
/// `default` one when empty.
- pub(crate) fn focused_text_input_serial_or_default<F>(&self, default: u32, mut callback: F)
+ pub(crate) fn active_text_input_serial_or_default<F>(&self, default: u32, mut callback: F)
where
F: FnMut(u32),
{
let mut inner = self.inner.lock().unwrap();
let mut should_default = true;
- inner.with_focused_text_input(|_, _, serial| {
+ inner.with_active_text_input(|_, _, serial| {
should_default = false;
callback(serial);
});
@@ -191,41 +223,99 @@ where
}
};
+ let mut guard = data.handle.inner.lock().unwrap();
+ let pending_state = match guard.instances.iter_mut().find_map(|instance| {
+ if instance.instance == *resource {
+ Some(&mut instance.pending_state)
+ } else {
+ None
+ }
+ }) {
+ Some(pending_state) => pending_state,
+ None => {
+ debug!("got request for untracked text-input");
+ return;
+ }
+ };
+
match request {
zwp_text_input_v3::Request::Enable => {
- data.handle.set_enabled_resource_id(Some(resource.id()));
- data.input_method_handle.activate_input_method(state, &focus);
+ pending_state.enable = Some(true);
}
zwp_text_input_v3::Request::Disable => {
- data.handle.set_enabled_resource_id(None);
- data.input_method_handle.deactivate_input_method(state, false);
+ pending_state.enable = Some(false);
}
zwp_text_input_v3::Request::SetSurroundingText { text, cursor, anchor } => {
- data.input_method_handle.with_instance(|input_method| {
- input_method
- .object
- .surrounding_text(text.clone(), cursor as u32, anchor as u32)
- });
+ pending_state.surrounding_text = Some((text, cursor as u32, anchor as u32));
}
zwp_text_input_v3::Request::SetTextChangeCause { cause } => {
- data.input_method_handle.with_instance(|input_method| {
- input_method
- .object
- .text_change_cause(cause.into_result().unwrap())
- });
+ pending_state.text_change_cause = Some(cause.into_result().unwrap());
}
zwp_text_input_v3::Request::SetContentType { hint, purpose } => {
- data.input_method_handle.with_instance(|input_method| {
- input_method
- .object
- .content_type(hint.into_result().unwrap(), purpose.into_result().unwrap());
- });
+ pending_state.content_type =
+ Some((hint.into_result().unwrap(), purpose.into_result().unwrap()));
}
zwp_text_input_v3::Request::SetCursorRectangle { x, y, width, height } => {
- data.input_method_handle
- .set_text_input_rectangle::<D>(state, x, y, width, height);
+ pending_state.cursor_rectangle = Some(Rectangle::new((x, y).into(), (width, height).into()));
}
zwp_text_input_v3::Request::Commit => {
+ let mut new_state = mem::take(pending_state);
+ let _ = pending_state;
+ let active_text_input_id = &mut guard.active_text_input_id;
+
+ if active_text_input_id.is_some() && *active_text_input_id != Some(resource.id()) {
+ debug!("discarding text_input request since we already have an active one");
+ return;
+ }
+
+ match new_state.enable {
+ Some(true) => {
+ *active_text_input_id = Some(resource.id());
+ // Drop the guard before calling to other subsystem.
+ drop(guard);
+ data.input_method_handle.activate_input_method(state, &focus);
+ }
+ Some(false) => {
+ *active_text_input_id = None;
+ // Drop the guard before calling to other subsystem.
+ drop(guard);
+ data.input_method_handle.deactivate_input_method(state);
+ return;
+ }
+ None => {
+ if *active_text_input_id != Some(resource.id()) {
+ debug!("discarding text_input requests before enabling it");
+ return;
+ }
+
+ // Drop the guard before calling to other subsystems later on.
+ drop(guard);
+ }
+ }
+
+ if let Some((text, cursor, anchor)) = new_state.surrounding_text.take() {
+ data.input_method_handle.with_instance(move |input_method| {
+ input_method.object.surrounding_text(text, cursor, anchor)
+ });
+ }
+
+ if let Some(cause) = new_state.text_change_cause.take() {
+ data.input_method_handle.with_instance(move |input_method| {
+ input_method.object.text_change_cause(cause);
+ });
+ }
+
+ if let Some((hint, purpose)) = new_state.content_type.take() {
+ data.input_method_handle.with_instance(move |input_method| {
+ input_method.object.content_type(hint, purpose);
+ });
+ }
+
+ if let Some(rect) = new_state.cursor_rectangle.take() {
+ data.input_method_handle
+ .set_text_input_rectangle::<D>(state, rect);
+ }
+
data.input_method_handle.with_instance(|input_method| {
input_method.done();
});
@@ -258,7 +348,23 @@ where
};
if deactivate_im {
- data.input_method_handle.deactivate_input_method(state, true);
+ data.input_method_handle.deactivate_input_method(state);
}
}
}
+
+#[derive(Debug)]
+struct Instance {
+ instance: ZwpTextInputV3,
+ serial: u32,
+ pending_state: TextInputState,
+}
+
+#[derive(Debug, Default)]
+struct TextInputState {
+ enable: Option<bool>,
+ surrounding_text: Option<(String, u32, u32)>,
+ content_type: Option<(ContentHint, ContentPurpose)>,
+ cursor_rectangle: Option<Rectangle<i32, Logical>>,
+ text_change_cause: Option<ChangeCause>,
+}
diff --git a/src/wayland/xdg_activation/mod.rs b/src/wayland/xdg_activation/mod.rs
index cac78c7..6b124a3 100644
--- a/src/wayland/xdg_activation/mod.rs
+++ b/src/wayland/xdg_activation/mod.rs
@@ -153,6 +153,19 @@ impl XdgActivationTokenData {
}
}
+impl Default for XdgActivationTokenData {
+ fn default() -> Self {
+ Self {
+ client_id: None,
+ serial: None,
+ app_id: None,
+ surface: None,
+ timestamp: Instant::now(),
+ user_data: Arc::new(UserDataMap::new()),
+ }
+ }
+}
+
/// Tracks the list of pending and current activation requests
#[derive(Debug)]
pub struct XdgActivationState {
@@ -185,9 +198,10 @@ impl XdgActivationState {
/// instead use the return arguments to handle any initialization of the data you might need and to copy the token.
pub fn create_external_token(
&mut self,
- app_id: impl Into<Option<String>>,
+ data: impl Into<Option<XdgActivationTokenData>>,
) -> (&XdgActivationToken, &XdgActivationTokenData) {
- let (token, data) = XdgActivationTokenData::new(None, None, app_id.into(), None);
+ let token = XdgActivationToken::new();
+ let data = data.into().unwrap_or_default();
self.known_tokens.insert(token.clone(), data);
self.known_tokens.get_key_value(&token).unwrap()
}
diff --git a/src/xwayland/xwm/mod.rs b/src/xwayland/xwm/mod.rs
index 52fd2c6..ba08190 100644
--- a/src/xwayland/xwm/mod.rs
+++ b/src/xwayland/xwm/mod.rs
@@ -1422,6 +1422,14 @@ where
}
surface.state.lock().unwrap().mapped_onto = Some(frame_win);
+
+ // A MapRequest can only happen when an X11 window does not have the
+ // override-redirect flag set. It's possible for a window to be created as
+ // override-redirect and later set the flag to false before mapping.
+ // In that case, we set the X11Surface's override-redirect state to false here
+ // to prevent `set_mapped` and `configure` from failing.
+ surface.state.lock().unwrap().override_redirect = false;
+
drop(_guard);
state.map_window_request(xwm_id, surface);
}
diff --git a/src/xwayland/xwm/surface.rs b/src/xwayland/xwm/surface.rs
index 8e9fbe3..09748b9 100644
--- a/src/xwayland/xwm/surface.rs
+++ b/src/xwayland/xwm/surface.rs
@@ -43,7 +43,6 @@ pub struct X11Surface {
xwm: Option<XwmId>,
client_scale: Option<Arc<AtomicU32>>,
window: X11Window,
- override_redirect: bool,
conn: Weak<RustConnection>,
atoms: super::Atoms,
pub(crate) state: Arc<Mutex<SharedSurfaceState>>,
@@ -61,6 +60,7 @@ pub(crate) struct SharedSurfaceState {
pub(super) wl_surface_serial: Option<u64>,
pub(super) mapped_onto: Option<X11Window>,
pub(super) geometry: Rectangle<i32, Logical>,
+ pub(super) override_redirect: bool,
// The associated wl_surface.
pub(crate) wl_surface: Option<WlSurface>,
@@ -170,7 +170,6 @@ impl X11Surface {
xwm: xwm.map(|wm| wm.id),
client_scale: xwm.map(|wm| wm.client_scale.clone()),
window,
- override_redirect,
conn,
atoms,
state: Arc::new(Mutex::new(SharedSurfaceState {
@@ -180,6 +179,7 @@ impl X11Surface {
wl_surface: None,
mapped_onto: None,
geometry,
+ override_redirect,
title: String::from(""),
class: String::from(""),
instance: String::from(""),
@@ -216,7 +216,7 @@ impl X11Surface {
///
/// It is an error to call this function on override redirect windows
pub fn set_mapped(&self, mapped: bool) -> Result<(), X11SurfaceError> {
- if self.override_redirect {
+ if self.is_override_redirect() {
return Err(X11SurfaceError::UnsupportedForOverrideRedirect);
}
@@ -251,7 +251,7 @@ impl X11Surface {
/// Returns if this window has the override redirect flag set or not
pub fn is_override_redirect(&self) -> bool {
- self.override_redirect
+ self.state.lock().unwrap().override_redirect
}
/// Returns if the window is currently mapped or not
@@ -272,7 +272,7 @@ impl X11Surface {
/// If `rect` is `None` a synthetic configure event with the existing state will be send.
pub fn configure(&self, rect: impl Into<Option<Rectangle<i32, Logical>>>) -> Result<(), X11SurfaceError> {
let rect = rect.into();
- if self.override_redirect && rect.is_some() {
+ if self.is_override_redirect() && rect.is_some() {
return Err(X11SurfaceError::UnsupportedForOverrideRedirect);
}