data_device: let the compositor decide the action choice strategy
This commit is contained in:
parent
81956c8fd0
commit
35645596a8
|
@ -16,6 +16,7 @@ use smithay::wayland_server::{calloop::EventLoop, Display};
|
|||
mod shaders;
|
||||
mod glium_drawer;
|
||||
mod input_handler;
|
||||
mod misc;
|
||||
#[cfg(feature = "tty_launch")]
|
||||
mod raw_drm;
|
||||
mod shell;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
use smithay::wayland_server::protocol::wl_data_device_manager::DndAction;
|
||||
|
||||
pub fn dnd_action_chooser(available: DndAction, preferred: DndAction) -> DndAction {
|
||||
// if the preferred action is valid (a single action) and in the available actions, use it
|
||||
// otherwise, follow a fallback stategy
|
||||
if [DndAction::Move, DndAction::Copy, DndAction::Ask].contains(&preferred)
|
||||
&& available.contains(preferred)
|
||||
{
|
||||
preferred
|
||||
} else if available.contains(DndAction::Ask) {
|
||||
DndAction::Ask
|
||||
} else if available.contains(DndAction::Copy) {
|
||||
DndAction::Copy
|
||||
} else if available.contains(DndAction::Move) {
|
||||
DndAction::Move
|
||||
} else {
|
||||
DndAction::empty()
|
||||
}
|
||||
}
|
|
@ -108,7 +108,7 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
|
||||
let udev_session_id = notifier.register(&mut udev_backend);
|
||||
|
||||
init_data_device(&mut display.borrow_mut(), log.clone());
|
||||
init_data_device(&mut display.borrow_mut(), ::misc::dnd_action_chooser, log.clone());
|
||||
|
||||
let (mut w_seat, _) = Seat::new(&mut display.borrow_mut(), session.seat(), log.clone());
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
|
|||
|
||||
let (compositor_token, _, _, window_map) = init_shell(display, log.clone());
|
||||
|
||||
init_data_device(display, log.clone());
|
||||
init_data_device(display, ::misc::dnd_action_chooser, log.clone());
|
||||
|
||||
let (mut seat, _) = Seat::new(display, "winit".into(), log.clone());
|
||||
|
||||
|
|
|
@ -12,21 +12,23 @@ use wayland::seat::{AxisFrame, PointerGrab, PointerInnerHandle, Seat};
|
|||
|
||||
use super::{with_source_metadata, SeatData};
|
||||
|
||||
pub(crate) struct DnDGrab {
|
||||
pub(crate) struct DnDGrab<F: 'static> {
|
||||
data_source: Option<Resource<wl_data_source::WlDataSource>>,
|
||||
current_focus: Option<Resource<wl_surface::WlSurface>>,
|
||||
pending_offers: Vec<Resource<wl_data_offer::WlDataOffer>>,
|
||||
offer_data: Option<Arc<Mutex<OfferData>>>,
|
||||
origin: Resource<wl_surface::WlSurface>,
|
||||
seat: Seat,
|
||||
action_choice: Arc<Mutex<F>>,
|
||||
}
|
||||
|
||||
impl DnDGrab {
|
||||
impl<F: 'static> DnDGrab<F> {
|
||||
pub(crate) fn new(
|
||||
source: Option<Resource<wl_data_source::WlDataSource>>,
|
||||
origin: Resource<wl_surface::WlSurface>,
|
||||
seat: Seat,
|
||||
) -> DnDGrab {
|
||||
action_choice: Arc<Mutex<F>>,
|
||||
) -> DnDGrab<F> {
|
||||
DnDGrab {
|
||||
data_source: source,
|
||||
current_focus: None,
|
||||
|
@ -34,11 +36,15 @@ impl DnDGrab {
|
|||
offer_data: None,
|
||||
origin,
|
||||
seat,
|
||||
action_choice,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerGrab for DnDGrab {
|
||||
impl<F> PointerGrab for DnDGrab<F>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||
{
|
||||
fn motion(
|
||||
&mut self,
|
||||
_handle: &mut PointerInnerHandle,
|
||||
|
@ -96,8 +102,14 @@ impl PointerGrab for DnDGrab {
|
|||
// create a data offer
|
||||
let offer = client
|
||||
.create_resource::<wl_data_offer::WlDataOffer>(device.version())
|
||||
.map(|offer| implement_dnd_data_offer(offer, source.clone(), offer_data.clone()))
|
||||
.unwrap();
|
||||
.map(|offer| {
|
||||
implement_dnd_data_offer(
|
||||
offer,
|
||||
source.clone(),
|
||||
offer_data.clone(),
|
||||
self.action_choice.clone(),
|
||||
)
|
||||
}).unwrap();
|
||||
// advertize the offer to the client
|
||||
device.send(wl_data_device::Event::DataOffer { id: offer.clone() });
|
||||
with_source_metadata(source, |meta| {
|
||||
|
@ -221,11 +233,15 @@ struct OfferData {
|
|||
chosen_action: DndAction,
|
||||
}
|
||||
|
||||
fn implement_dnd_data_offer(
|
||||
fn implement_dnd_data_offer<F>(
|
||||
offer: NewResource<wl_data_offer::WlDataOffer>,
|
||||
source: Resource<wl_data_source::WlDataSource>,
|
||||
offer_data: Arc<Mutex<OfferData>>,
|
||||
) -> Resource<wl_data_offer::WlDataOffer> {
|
||||
action_choice: Arc<Mutex<F>>,
|
||||
) -> Resource<wl_data_offer::WlDataOffer>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||
{
|
||||
use self::wl_data_offer::Request;
|
||||
offer.implement(
|
||||
move |req, offer| {
|
||||
|
@ -296,20 +312,12 @@ fn implement_dnd_data_offer(
|
|||
let source_actions =
|
||||
with_source_metadata(&source, |meta| meta.dnd_action).unwrap_or(DndAction::empty());
|
||||
let possible_actions = source_actions & DndAction::from_bits_truncate(dnd_actions);
|
||||
if possible_actions.contains(preferred_action) {
|
||||
// chose this one
|
||||
data.chosen_action = preferred_action;
|
||||
} else {
|
||||
if possible_actions.contains(DndAction::Ask) {
|
||||
data.chosen_action = DndAction::Ask;
|
||||
} else if possible_actions.contains(DndAction::Copy) {
|
||||
data.chosen_action = DndAction::Copy;
|
||||
} else if possible_actions.contains(DndAction::Move) {
|
||||
data.chosen_action = DndAction::Move;
|
||||
} else {
|
||||
data.chosen_action = DndAction::None;
|
||||
}
|
||||
}
|
||||
data.chosen_action =
|
||||
(&mut *action_choice.lock().unwrap())(possible_actions, preferred_action);
|
||||
// check that the user provided callback respects that one precise action should be chosen
|
||||
debug_assert!(
|
||||
[DndAction::Move, DndAction::Copy, DndAction::Ask].contains(&data.chosen_action)
|
||||
);
|
||||
offer.send(wl_data_offer::Event::Action {
|
||||
dnd_action: data.chosen_action.to_raw(),
|
||||
});
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
use std::sync::Mutex;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use wayland_server::{
|
||||
protocol::{wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source},
|
||||
protocol::{
|
||||
wl_data_device,
|
||||
wl_data_device_manager::{self, DndAction},
|
||||
wl_data_offer, wl_data_source,
|
||||
},
|
||||
Client, Display, Global, NewResource, Resource,
|
||||
};
|
||||
|
||||
|
@ -121,17 +125,25 @@ impl SeatData {
|
|||
}
|
||||
|
||||
/// Initialize the data device global
|
||||
pub fn init_data_device<L>(
|
||||
///
|
||||
/// You need to provide a `(DndAction, DndAction) -> DndAction` closure that will arbitrate
|
||||
/// the choice of action resulting from a drag'n'drop session. Its first argument is the set of
|
||||
/// available actions (which is the intersection of the actions supported by the source and targets)
|
||||
/// and the second argument is the preferred action reported by the target. If no action should be
|
||||
/// chosen (and thus the drag'n'drop should abort on drop), return `DndAction::empty()`.
|
||||
pub fn init_data_device<F, L>(
|
||||
display: &mut Display,
|
||||
action_choice: F,
|
||||
logger: L,
|
||||
) -> Global<wl_data_device_manager::WlDataDeviceManager>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "data_device_mgr"));
|
||||
|
||||
let action_choice = Arc::new(Mutex::new(action_choice));
|
||||
let global = display.create_global(3, move |new_ddm, _version| {
|
||||
implement_ddm(new_ddm, log.clone());
|
||||
implement_ddm(new_ddm, action_choice.clone(), log.clone());
|
||||
});
|
||||
|
||||
global
|
||||
|
@ -153,10 +165,14 @@ pub fn set_data_device_focus(seat: &Seat, client: Option<Client>) {
|
|||
seat_data.lock().unwrap().set_focus(client);
|
||||
}
|
||||
|
||||
fn implement_ddm(
|
||||
fn implement_ddm<F>(
|
||||
new_ddm: NewResource<wl_data_device_manager::WlDataDeviceManager>,
|
||||
action_choice: Arc<Mutex<F>>,
|
||||
log: ::slog::Logger,
|
||||
) -> Resource<wl_data_device_manager::WlDataDeviceManager> {
|
||||
) -> Resource<wl_data_device_manager::WlDataDeviceManager>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||
{
|
||||
use self::wl_data_device_manager::Request;
|
||||
new_ddm.implement(
|
||||
move |req, _ddm| match req {
|
||||
|
@ -169,7 +185,8 @@ fn implement_ddm(
|
|||
seat.user_data()
|
||||
.insert_if_missing(|| Mutex::new(SeatData::new(log.clone())));
|
||||
let seat_data = seat.user_data().get::<Mutex<SeatData>>().unwrap();
|
||||
let data_device = implement_data_device(id, seat.clone(), log.clone());
|
||||
let data_device =
|
||||
implement_data_device(id, seat.clone(), action_choice.clone(), log.clone());
|
||||
seat_data.lock().unwrap().known_devices.push(data_device);
|
||||
}
|
||||
None => {
|
||||
|
@ -182,11 +199,15 @@ fn implement_ddm(
|
|||
)
|
||||
}
|
||||
|
||||
fn implement_data_device(
|
||||
fn implement_data_device<F>(
|
||||
new_dd: NewResource<wl_data_device::WlDataDevice>,
|
||||
seat: Seat,
|
||||
action_choice: Arc<Mutex<F>>,
|
||||
log: ::slog::Logger,
|
||||
) -> Resource<wl_data_device::WlDataDevice> {
|
||||
) -> Resource<wl_data_device::WlDataDevice>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + Send + 'static,
|
||||
{
|
||||
use self::wl_data_device::Request;
|
||||
new_dd.implement(
|
||||
move |req, dd| match req {
|
||||
|
@ -200,7 +221,10 @@ fn implement_data_device(
|
|||
if let Some(pointer) = seat.get_pointer() {
|
||||
if pointer.has_grab(serial) {
|
||||
// The StartDrag is in response to a pointer implicit grab, all is good
|
||||
pointer.set_grab(dnd_grab::DnDGrab::new(source, origin, seat.clone()), serial);
|
||||
pointer.set_grab(
|
||||
dnd_grab::DnDGrab::new(source, origin, seat.clone(), action_choice.clone()),
|
||||
serial,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue