data_device: let the compositor decide the action choice strategy

This commit is contained in:
Victor Berger 2018-11-21 16:01:34 +01:00
parent 81956c8fd0
commit 35645596a8
6 changed files with 87 additions and 35 deletions

View File

@ -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;

19
anvil/src/misc.rs Normal file
View File

@ -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()
}
}

View File

@ -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());

View File

@ -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());

View File

@ -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(),
});

View File

@ -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;
}
}