feat: dnd integration
This commit is contained in:
parent
c3e9e794b9
commit
228288dfdf
11 changed files with 589 additions and 14 deletions
126
dnd/src/lib.rs
Normal file
126
dnd/src/lib.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::c_void,
|
||||
sync::{mpsc::SendError, Arc},
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
#[cfg(all(
|
||||
unix,
|
||||
not(any(
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox"
|
||||
))
|
||||
))]
|
||||
#[path = "platform/linux.rs"]
|
||||
pub mod platform;
|
||||
|
||||
bitflags! {
|
||||
// Attributes can be applied to flags types
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct DndAction: u32 {
|
||||
const Copy = 0b00000001;
|
||||
const Move = 0b00000010;
|
||||
const Ask = 0b00000100;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DndEvent<T> {
|
||||
/// Dnd Offer event with the corresponding destination rectangle ID.
|
||||
Offer(Option<u128>, OfferEvent<T>),
|
||||
/// Dnd Source event.
|
||||
Source(SourceEvent),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SourceEvent {
|
||||
/// DnD operation ended.
|
||||
Finished,
|
||||
/// DnD Cancelled.
|
||||
Cancelled,
|
||||
/// DnD action chosen by the compositor.
|
||||
Action(DndAction),
|
||||
/// Mime accepted by destination.
|
||||
/// If [`None`], no mime types are accepted.
|
||||
Mime(Option<String>),
|
||||
/// DnD Dropped. The operation is still ongoing until receiving a
|
||||
/// [`SourceEvent::Finished`] event.
|
||||
Dropped,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OfferEvent<T> {
|
||||
Enter {
|
||||
x: f64,
|
||||
y: f64,
|
||||
mime_types: Vec<String>,
|
||||
surface: T,
|
||||
},
|
||||
Motion {
|
||||
x: f64,
|
||||
y: f64,
|
||||
},
|
||||
/// The offer is no longer on a DnD destination.
|
||||
LeaveDestination,
|
||||
/// The offer has left the surface.
|
||||
Leave,
|
||||
/// An offer was dropped
|
||||
Drop,
|
||||
/// If the selected action is ASK, the user must be presented with a
|
||||
/// choice. [`Clipboard::set_action`] should then be called before data
|
||||
/// can be requested and th DnD operation can be finished.
|
||||
SelectedAction(DndAction),
|
||||
Data {
|
||||
data: Vec<u8>,
|
||||
mime_type: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// A rectangle with a logical location and size relative to a [`DndSurface`]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Rectangle {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
}
|
||||
|
||||
pub trait Sender<T> {
|
||||
/// Send an event in the channel
|
||||
fn send(&self, t: DndEvent<T>) -> Result<(), SendError<DndEvent<T>>>;
|
||||
}
|
||||
|
||||
pub trait RawSurface {
|
||||
/// # Safety
|
||||
///
|
||||
/// returned pointer must be a valid pointer to the underlying surface, and it must
|
||||
/// remain valid for as long as `RawSurface` object is alive.
|
||||
unsafe fn get_ptr(&mut self) -> *mut c_void;
|
||||
}
|
||||
|
||||
/// A rectangle with a logical location and size relative to a [`DndSurface`]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DndDestinationRectangle {
|
||||
/// A unique ID
|
||||
pub id: u128,
|
||||
/// The rectangle representing this destination.
|
||||
pub rectangle: Rectangle,
|
||||
/// Accepted mime types in this rectangle
|
||||
pub mime_types: Vec<Cow<'static, str>>,
|
||||
/// Accepted actions in this rectangle
|
||||
pub actions: DndAction,
|
||||
/// Prefered action in this rectangle
|
||||
pub preferred: DndAction,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DndSurface(pub Arc<Box<dyn RawSurface + 'static + Send + Sync>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DataWrapper<T>(pub T);
|
||||
88
dnd/src/platform/linux.rs
Normal file
88
dnd/src/platform/linux.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
use std::{borrow::Cow, ffi::c_void, sync::Arc};
|
||||
|
||||
use crate::{DataWrapper, DndAction, DndSurface};
|
||||
use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType};
|
||||
|
||||
impl<
|
||||
T: mime::AllowedMimeTypes
|
||||
+ std::convert::TryFrom<(std::vec::Vec<u8>, String)>,
|
||||
> AllowedMimeTypes for DataWrapper<T>
|
||||
{
|
||||
fn allowed() -> Cow<'static, [MimeType]> {
|
||||
T::allowed()
|
||||
.into_iter()
|
||||
.map(|s| MimeType::from(Cow::Owned(s.to_string())))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TryFrom<(Vec<u8>, String)>> TryFrom<(Vec<u8>, MimeType)>
|
||||
for DataWrapper<T>
|
||||
{
|
||||
type Error = T::Error;
|
||||
|
||||
fn try_from(
|
||||
(data, mime): (Vec<u8>, MimeType),
|
||||
) -> Result<Self, Self::Error> {
|
||||
T::try_from((data, mime.to_string())).map(|d| DataWrapper(d))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: mime::AsMimeTypes> AsMimeTypes for DataWrapper<T> {
|
||||
fn available(&self) -> Cow<'static, [MimeType]> {
|
||||
self.0
|
||||
.available()
|
||||
.into_iter()
|
||||
.map(|m| MimeType::from(Cow::Owned(m.to_string())))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn as_bytes(&self, mime_type: &MimeType) -> Option<Cow<'static, [u8]>> {
|
||||
self.0.as_bytes(mime_type.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl smithay_clipboard::dnd::RawSurface for DndSurface {
|
||||
unsafe fn get_ptr(&mut self) -> *mut c_void {
|
||||
// XXX won't panic because this is only called once before it could be cloned
|
||||
Arc::get_mut(&mut self.0).unwrap().get_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sctk::reexports::client::protocol::wl_data_device_manager::DndAction>
|
||||
for DndAction
|
||||
{
|
||||
fn from(
|
||||
action: sctk::reexports::client::protocol::wl_data_device_manager::DndAction,
|
||||
) -> Self {
|
||||
let mut a = DndAction::empty();
|
||||
if action.contains(sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Copy) {
|
||||
a |= DndAction::Copy;
|
||||
}
|
||||
if action.contains(sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Move) {
|
||||
a |= DndAction::Move;
|
||||
}
|
||||
if action.contains(sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Ask) {
|
||||
a |= DndAction::Ask;
|
||||
}
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DndAction>
|
||||
for sctk::reexports::client::protocol::wl_data_device_manager::DndAction
|
||||
{
|
||||
fn from(action: DndAction) -> Self {
|
||||
let mut a = sctk::reexports::client::protocol::wl_data_device_manager::DndAction::empty();
|
||||
if action.contains(DndAction::Copy) {
|
||||
a |= sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Copy;
|
||||
}
|
||||
if action.contains(DndAction::Move) {
|
||||
a |= sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Move;
|
||||
}
|
||||
if action.contains(DndAction::Ask) {
|
||||
a |= sctk::reexports::client::protocol::wl_data_device_manager::DndAction::Ask;
|
||||
}
|
||||
a
|
||||
}
|
||||
}
|
||||
0
dnd/src/platform/mod.rs
Normal file
0
dnd/src/platform/mod.rs
Normal file
Loading…
Add table
Add a link
Reference in a new issue