Skip to content

Commit

Permalink
Implement wlr_data_control protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
xDarksome committed May 6, 2023
1 parent 8f6ad62 commit 2b39656
Show file tree
Hide file tree
Showing 14 changed files with 1,090 additions and 76 deletions.
2 changes: 2 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub struct StaticConfig {
pub active_hint: u8,
#[serde(default = "default_gaps")]
pub gaps: (u8, u8),
pub data_control_enabled: bool,
}

#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -235,6 +236,7 @@ impl Config {
tiling_enabled: false,
active_hint: default_active_hint(),
gaps: default_gaps(),
data_control_enabled: false,
}
}

Expand Down
22 changes: 20 additions & 2 deletions src/shell/focus/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
shell::{element::CosmicMapped, Shell, Workspace},
state::Common,
state::{Common, SelectionSources},
utils::prelude::*,
wayland::handlers::xdg_shell::PopupGrabData,
xwayland::XWaylandState,
Expand All @@ -11,7 +11,7 @@ use smithay::{
input::Seat,
utils::{IsAlive, Serial, SERIAL_COUNTER},
};
use std::cell::RefCell;
use std::cell::{RefCell, RefMut};
use tracing::{debug, trace};

use self::target::{KeyboardFocusTarget, WindowGroup};
Expand Down Expand Up @@ -165,6 +165,24 @@ impl Shell {
}

impl Common {
fn selection_sources_inner(seat: &Seat<State>) -> &RefCell<SelectionSources> {
seat.user_data()
.insert_if_missing(|| RefCell::new(SelectionSources::default()));
seat.user_data().get::<RefCell<SelectionSources>>().unwrap()
}

pub fn selection_sources(seat: &Seat<State>) -> SelectionSources {
*Self::selection_sources_inner(seat).borrow()
}

pub fn update_selection_sources(
seat: &Seat<State>,
f: impl FnOnce(RefMut<'_, SelectionSources>),
) {
let sources = Self::selection_sources_inner(seat).borrow_mut();
f(sources)
}

pub fn set_focus(
state: &mut State,
target: Option<&KeyboardFocusTarget>,
Expand Down
29 changes: 29 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
shell::{layout::floating::SeatMoveGrabState, Shell},
utils::prelude::*,
wayland::protocols::{
data_control,
drm::WlDrmState,
output_configuration::OutputConfigurationState,
screencopy::{BufferParams, ScreencopyState, Session as ScreencopySession},
Expand Down Expand Up @@ -62,6 +63,7 @@ use smithay::{
shm::ShmState,
viewporter::ViewporterState,
},
xwayland::xwm::XwmId,
};
use tracing::error;

Expand Down Expand Up @@ -117,6 +119,7 @@ pub struct Common {
pub output_configuration_state: OutputConfigurationState<State>,
pub presentation_state: PresentationState,
pub primary_selection_state: PrimarySelectionState,
pub data_control_state: Option<data_control::State<State>>,
pub screencopy_state: ScreencopyState,
pub seat_state: SeatState<State>,
pub shm_state: ShmState,
Expand All @@ -129,6 +132,26 @@ pub struct Common {
pub xwayland_state: Option<XWaylandState>,
}

#[derive(Clone, Copy, Default)]
pub enum SelectionSource {
#[default]
Native,
DataControl,
XWayland(XwmId),
}

#[derive(Clone, Copy, Default)]
pub struct SelectionSources {
pub clipboard: SelectionSource,
pub primary_selection: SelectionSource,
}

impl SelectionSources {
pub fn tuple(self) -> (SelectionSource, SelectionSource) {
(self.clipboard, self.primary_selection)
}
}

pub enum BackendData {
X11(X11State),
Winit(WinitState),
Expand Down Expand Up @@ -261,6 +284,11 @@ impl State {
let kde_decoration_state = KdeDecorationState::new::<Self>(&dh, Mode::Client);
let xdg_decoration_state = XdgDecorationState::new::<Self>(&dh);

let data_control_state = config
.static_conf
.data_control_enabled
.then(|| data_control::State::<Self>::new(dh));

let shell = Shell::new(&config, dh);

State {
Expand Down Expand Up @@ -299,6 +327,7 @@ impl State {
output_configuration_state,
presentation_state,
primary_selection_state,
data_control_state,
viewporter_state,
wl_drm_state,
kde_decoration_state,
Expand Down
109 changes: 109 additions & 0 deletions src/wayland/handlers/data_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-3.0-only

use crate::state::{Common, SelectionSource, State};
use crate::wayland::protocols::data_control::{self, SelectionType};
use smithay::wayland::data_device::{
clear_data_device_selection, request_data_device_client_selection, set_data_device_selection,
};
use smithay::wayland::primary_selection::{
clear_primary_selection, request_primary_client_selection, set_primary_selection,
};
use smithay::xwayland::xwm;
use tracing::{error, warn};

use std::os::unix::io::OwnedFd;

impl data_control::Handler for State {
fn state(&mut self) -> &mut data_control::State<Self> {
self.common.data_control_state.as_mut().unwrap()
}

fn new_selection(&mut self, ty: SelectionType, mime_types: Option<Vec<String>>) {
if let Some(state) = self.common.xwayland_state.as_mut() {
if let Some(xwm) = state.xwm.as_mut() {
if let Err(err) = xwm.new_selection(ty.into(), mime_types.clone()) {
warn!(?err, "Failed to set Xwayland DataControl selection");
}
}
}

let dh = &self.common.display_handle;
let seat = self.common.last_active_seat();
match (ty, mime_types) {
(SelectionType::Clipboard, Some(mt)) => set_data_device_selection(dh, seat, mt, ()),
(SelectionType::Clipboard, None) => clear_data_device_selection(dh, seat),
(SelectionType::Primary, Some(mt)) => set_primary_selection(dh, seat, mt, ()),
(SelectionType::Primary, None) => clear_primary_selection(dh, seat),
}

Common::update_selection_sources(&seat, |mut sources| match ty {
SelectionType::Clipboard => sources.clipboard = SelectionSource::DataControl,
SelectionType::Primary => sources.primary_selection = SelectionSource::DataControl,
});
}

fn send_selection(&mut self, ty: SelectionType, mime_type: String, fd: OwnedFd) {
let seat = self.common.last_active_seat();
match (ty, Common::selection_sources(seat).tuple()) {
(SelectionType::Clipboard, (SelectionSource::Native, _)) => {
if let Err(err) = request_data_device_client_selection(seat, mime_type, fd) {
error!(
?err,
"Failed to request current wayland clipboard for DataControl.",
);
}
}
(SelectionType::Primary, (_, SelectionSource::Native)) => {
if let Err(err) = request_primary_client_selection(seat, mime_type, fd) {
error!(
?err,
"Failed to request current wayland primary selection for DataControl.",
);
}
}
(SelectionType::Clipboard, (SelectionSource::XWayland(_), _))
| (SelectionType::Primary, (_, SelectionSource::XWayland(_))) => {
if let Some(xwm) = self
.common
.xwayland_state
.as_mut()
.and_then(|xstate| xstate.xwm.as_mut())
{
if let Err(err) = xwm.send_selection(
ty.into(),
mime_type,
fd,
self.common.event_loop_handle.clone(),
) {
warn!(
?err,
"Failed to send primary selection (X11 -> DataControl)."
);
}
}
}
(SelectionType::Clipboard, (SelectionSource::DataControl, _))
| (SelectionType::Primary, (_, SelectionSource::DataControl)) => {}
}
}
}

impl From<SelectionType> for xwm::SelectionType {
fn from(ty: SelectionType) -> Self {
match ty {
SelectionType::Clipboard => xwm::SelectionType::Clipboard,
SelectionType::Primary => xwm::SelectionType::Clipboard,
}
}
}

impl From<xwm::SelectionType> for SelectionType {
fn from(ty: xwm::SelectionType) -> Self {
match ty {
xwm::SelectionType::Clipboard => SelectionType::Clipboard,
xwm::SelectionType::Primary => SelectionType::Clipboard,
}
}
}

data_control::delegate!(State);
86 changes: 63 additions & 23 deletions src/wayland/handlers/data_device.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only

use crate::state::State;
use crate::{
state::{Common, SelectionSource, State},
wayland::protocols::data_control,
};
use smithay::{
delegate_data_device,
input::Seat,
Expand All @@ -10,7 +13,7 @@ use smithay::{
with_source_metadata, ClientDndGrabHandler, DataDeviceHandler, DataDeviceState,
ServerDndGrabHandler,
},
xwayland::xwm::{SelectionType, XwmId},
xwayland::xwm::SelectionType,
};
use std::{cell::RefCell, os::unix::io::OwnedFd};
use tracing::warn;
Expand Down Expand Up @@ -51,29 +54,46 @@ impl ClientDndGrabHandler for State {
}
impl ServerDndGrabHandler for State {}
impl DataDeviceHandler for State {
type SelectionUserData = XwmId;
type SelectionUserData = ();

fn data_device_state(&self) -> &DataDeviceState {
&self.common.data_device_state
}

fn new_selection(&mut self, source: Option<WlDataSource>) {
let Ok(mime_types) = source
.map(|s| with_source_metadata(&s, |metadata| metadata.mime_types.clone()))
.transpose() else { return };

if let Some(state) = self.common.xwayland_state.as_mut() {
if let Some(xwm) = state.xwm.as_mut() {
if let Some(source) = &source {
if let Ok(Err(err)) = with_source_metadata(source, |metadata| {
xwm.new_selection(
SelectionType::Clipboard,
Some(metadata.mime_types.clone()),
)
}) {
if let Some(mime_types) = &mime_types {
if let Err(err) =
xwm.new_selection(SelectionType::Clipboard, Some(mime_types.clone()))
{
warn!(?err, "Failed to set Xwayland clipboard selection.");
}
} else if let Err(err) = xwm.new_selection(SelectionType::Clipboard, None) {
warn!(?err, "Failed to clear Xwayland clipboard selection.");
}
}
}

let seat = self.common.last_active_seat().clone();
if self.common.data_control_state.is_some() {
let dh = self.common.display_handle.clone();
data_control::set_selection(
self,
&seat,
&dh,
data_control::SelectionType::Clipboard,
mime_types,
);
}

Common::update_selection_sources(&seat, |mut sources| {
sources.clipboard = SelectionSource::Native
});
}

fn send_selection(
Expand All @@ -82,20 +102,40 @@ impl DataDeviceHandler for State {
fd: OwnedFd,
_user_data: &Self::SelectionUserData,
) {
if let Some(xwm) = self
.common
.xwayland_state
.as_mut()
.and_then(|xstate| xstate.xwm.as_mut())
{
if let Err(err) = xwm.send_selection(
SelectionType::Clipboard,
mime_type,
fd,
self.common.event_loop_handle.clone(),
) {
warn!(?err, "Failed to send clipboard (X11 -> Wayland).");
let seat = self.common.last_active_seat();
match Common::selection_sources(seat).clipboard {
SelectionSource::XWayland(_) => {
if let Some(xwm) = self
.common
.xwayland_state
.as_mut()
.and_then(|xstate| xstate.xwm.as_mut())
{
if let Err(err) = xwm.send_selection(
SelectionType::Clipboard,
mime_type,
fd,
self.common.event_loop_handle.clone(),
) {
warn!(?err, "Failed to send clipboard (X11 -> Wayland).");
}
}
}
SelectionSource::DataControl => {
if self.common.data_control_state.is_some() {
let seat = seat.clone();
if let Err(err) = data_control::request_selection(
self,
&seat,
data_control::SelectionType::Clipboard,
mime_type,
fd,
) {
warn!(?err, "Failed to send clipboard (X11 -> DataControl).");
};
}
}
SelectionSource::Native => {}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/wayland/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

pub mod buffer;
pub mod compositor;
pub mod data_control;
pub mod data_device;
pub mod decoration;
pub mod dmabuf;
Expand Down
Loading

0 comments on commit 2b39656

Please sign in to comment.