Compare commits
10 commits
1b6efd066e
...
859b02c88f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
859b02c88f | ||
|
|
5a3007def4 | ||
|
|
d099e82a4c | ||
|
|
ab1750a028 | ||
|
|
9b995a33a8 | ||
|
|
eefa50c3df | ||
|
|
2263426a09 | ||
|
|
a698efe935 | ||
|
|
ace619fd01 | ||
|
|
efe7a75e7b |
5 changed files with 308 additions and 54 deletions
24
Cargo.toml
24
Cargo.toml
|
|
@ -1,7 +1,10 @@
|
|||
[package]
|
||||
name = "smithay-clipboard"
|
||||
version = "0.8.0"
|
||||
authors = ["Kirill Chibisov <contact@kchibisov.com>", "Victor Berger <victor.berger@m4x.org>"]
|
||||
authors = [
|
||||
"Kirill Chibisov <contact@kchibisov.com>",
|
||||
"Victor Berger <victor.berger@m4x.org>",
|
||||
]
|
||||
edition = "2021"
|
||||
description = "Provides access to the wayland clipboard for client applications."
|
||||
repository = "https://github.com/smithay/smithay-clipboard"
|
||||
|
|
@ -12,16 +15,25 @@ rust-version = "1.65.0"
|
|||
|
||||
[dependencies]
|
||||
libc = "0.2.149"
|
||||
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", default-features = false, features = ["calloop"] }
|
||||
wayland-backend = { version = "0.3.3", default_features = false, features = ["client_system"] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.20", default-features = false, features = [
|
||||
"calloop",
|
||||
] }
|
||||
wayland-backend = { version = "0.3.3", default_features = false, features = [
|
||||
"client_system",
|
||||
] }
|
||||
raw-window-handle = { version = "0.6", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = "5.0.1"
|
||||
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", default-features = false, features = ["calloop", "xkbcommon"] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.20", default-features = false, features = [
|
||||
"calloop",
|
||||
"xkbcommon",
|
||||
] }
|
||||
thiserror = "1.0.57"
|
||||
url = "2.5.0"
|
||||
|
||||
[features]
|
||||
default = ["dlopen", "dnd"]
|
||||
default = ["dlopen", "dnd", "rwh-6"]
|
||||
rwh-6 = ["raw-window-handle"]
|
||||
dnd = []
|
||||
dlopen = ["wayland-backend/dlopen" ]
|
||||
dlopen = ["wayland-backend/dlopen"]
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use sctk::{
|
|||
delegate_compositor, delegate_keyboard, delegate_output, delegate_pointer, delegate_registry,
|
||||
delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, registry_handlers,
|
||||
};
|
||||
use smithay_clipboard::dnd::{DndDestinationRectangle, OfferEvent, Rectangle, SourceEvent};
|
||||
use smithay_clipboard::dnd::{DndDestinationRectangle, Icon, OfferEvent, Rectangle, SourceEvent};
|
||||
use smithay_clipboard::mime::{AllowedMimeTypes, AsMimeTypes, MimeType, ALLOWED_TEXT_MIME_TYPES};
|
||||
use smithay_clipboard::{Clipboard, SimpleClipboard};
|
||||
use thiserror::Error;
|
||||
|
|
@ -88,9 +88,9 @@ fn main() {
|
|||
smithay_clipboard::dnd::DndEvent::Offer(id, OfferEvent::Motion { x, y }) => {
|
||||
if id != state.offer_hover_id {
|
||||
state.offer_hover_id = id;
|
||||
if let Ok(data) = state.clipboard.peek_offer::<smithay_clipboard::text::Text>(
|
||||
MimeType::Text(smithay_clipboard::mime::Text::TextPlain),
|
||||
) {
|
||||
if let Ok(data) =
|
||||
state.clipboard.peek_offer::<smithay_clipboard::text::Text>(None)
|
||||
{
|
||||
println!("Peeked the data: {}", data.0);
|
||||
}
|
||||
}
|
||||
|
|
@ -116,9 +116,10 @@ fn main() {
|
|||
smithay_clipboard::dnd::DndEvent::Offer(id, OfferEvent::Enter { mime_types, .. }) => {
|
||||
println!("Received DnD Enter for {id:?}");
|
||||
state.offer_hover_id = id;
|
||||
if let Some(mime) = mime_types.get(0) {
|
||||
if let Ok(data) =
|
||||
state.clipboard.peek_offer::<smithay_clipboard::text::Text>(mime.clone())
|
||||
if let Some(mime) = mime_types.first() {
|
||||
if let Ok(data) = state
|
||||
.clipboard
|
||||
.peek_offer::<smithay_clipboard::text::Text>(Some(mime.clone()))
|
||||
{
|
||||
println!("Peeked the data: {}", data.0);
|
||||
}
|
||||
|
|
@ -264,6 +265,24 @@ impl CompositorHandler for SimpleWindow {
|
|||
) {
|
||||
self.draw(conn, qh);
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_output: &wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_output: &wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputHandler for SimpleWindow {
|
||||
|
|
@ -390,21 +409,33 @@ impl PointerHandler for SimpleWindow {
|
|||
match &e.kind {
|
||||
PointerEventKind::Press { button, .. } if *button == BTN_LEFT => {
|
||||
println!("Starting a drag!");
|
||||
|
||||
self.clipboard.start_dnd(
|
||||
false,
|
||||
self.window.wl_surface().clone(),
|
||||
None,
|
||||
Some(Icon::Buf {
|
||||
width: 256,
|
||||
height: 256,
|
||||
data: vec![0x99; 256 * 256 * 4],
|
||||
transparent: true,
|
||||
}),
|
||||
smithay_clipboard::text::Text("Clipboard Drag and Drop!".to_string()),
|
||||
DndAction::all(),
|
||||
);
|
||||
},
|
||||
PointerEventKind::Press { button, .. } if *button == BTN_RIGHT => {
|
||||
println!("Starting an internal drag!");
|
||||
|
||||
self.internal_dnd = true;
|
||||
self.clipboard.start_dnd(
|
||||
true,
|
||||
self.window.wl_surface().clone(),
|
||||
None,
|
||||
Some(Icon::Buf {
|
||||
width: 256,
|
||||
height: 256,
|
||||
data: vec![0xFF; 256 * 256 * 4],
|
||||
transparent: true,
|
||||
}),
|
||||
smithay_clipboard::text::Text(
|
||||
"Internal clipboard Drag and Drop!".to_string(),
|
||||
),
|
||||
|
|
@ -525,14 +556,25 @@ impl KeyboardHandler for SimpleWindow {
|
|||
) {
|
||||
}
|
||||
|
||||
fn repeat_key(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
_event: KeyEvent,
|
||||
) {
|
||||
}
|
||||
|
||||
fn update_modifiers(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_keyboard::WlKeyboard,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
_modifiers: Modifiers,
|
||||
_: u32,
|
||||
_raw_modifiers: sctk::seat::keyboard::RawModifiers,
|
||||
_layout: u32,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -576,7 +618,7 @@ impl SimpleWindow {
|
|||
};
|
||||
|
||||
// Draw to the window:
|
||||
canvas.chunks_exact_mut(4).enumerate().for_each(|(_, chunk)| {
|
||||
canvas.chunks_exact_mut(4).for_each(|chunk| {
|
||||
// ARGB color.
|
||||
let color = 0xFF181818u32;
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,16 @@ impl<T: RawSurface> DndSurface<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh-6")]
|
||||
impl<'a> RawSurface for raw_window_handle::WindowHandle<'a> {
|
||||
unsafe fn get_ptr(&mut self) -> *mut c_void {
|
||||
match self.as_raw() {
|
||||
raw_window_handle::RawWindowHandle::Wayland(handle) => handle.surface.as_ptr().cast(),
|
||||
_ => panic!("Unsupported window handle type."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RawSurface for WlSurface {
|
||||
unsafe fn get_ptr(&mut self) -> *mut c_void {
|
||||
self.id().as_ptr().cast()
|
||||
|
|
@ -135,7 +145,7 @@ pub enum DndRequest<T> {
|
|||
StartDnd {
|
||||
internal: bool,
|
||||
source: DndSurface<T>,
|
||||
icon: Option<DndSurface<T>>,
|
||||
icon: Option<Icon<DndSurface<T>>>,
|
||||
content: Box<dyn AsMimeTypes + Send>,
|
||||
actions: DndAction,
|
||||
},
|
||||
|
|
@ -167,6 +177,17 @@ impl<T> Sender<T> for calloop::channel::SyncSender<DndEvent<T>> {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum Icon<S> {
|
||||
Surface(S),
|
||||
/// Argb8888 or Xrgb8888 encoded image data pre-multiplied by alpha.
|
||||
Buf {
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: Vec<u8>,
|
||||
transparent: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T: RawSurface> Clipboard<T> {
|
||||
/// Set up DnD operations for the Clipboard
|
||||
pub fn init_dnd(
|
||||
|
|
@ -181,12 +202,17 @@ impl<T: RawSurface> Clipboard<T> {
|
|||
&self,
|
||||
internal: bool,
|
||||
source_surface: T,
|
||||
icon_surface: Option<T>,
|
||||
icon_surface: Option<Icon<T>>,
|
||||
content: D,
|
||||
actions: DndAction,
|
||||
) {
|
||||
let source = DndSurface::new(source_surface, &self.connection).unwrap();
|
||||
let icon = icon_surface.map(|s| DndSurface::new(s, &self.connection).unwrap());
|
||||
let icon = icon_surface.map(|i| match i {
|
||||
Icon::Surface(s) => Icon::Surface(DndSurface::new(s, &self.connection).unwrap()),
|
||||
Icon::Buf { width, height, data, transparent } => {
|
||||
Icon::Buf { width, height, data, transparent }
|
||||
},
|
||||
});
|
||||
_ = self.request_sender.send(crate::worker::Command::DndRequest(DndRequest::StartDnd {
|
||||
internal,
|
||||
source,
|
||||
|
|
@ -203,7 +229,7 @@ impl<T: RawSurface> Clipboard<T> {
|
|||
|
||||
/// Register a surface for receiving DnD offers
|
||||
/// Rectangles should be provided in order of decreasing priority.
|
||||
/// This method can be called multiple time for a single surface if the
|
||||
/// This method c~an be called multiple time for a single surface if the
|
||||
/// rectangles change.
|
||||
pub fn register_dnd_destination(&self, surface: T, rectangles: Vec<DndDestinationRectangle>) {
|
||||
let s = DndSurface::new(surface, &self.connection).unwrap();
|
||||
|
|
@ -223,8 +249,12 @@ impl<T: RawSurface> Clipboard<T> {
|
|||
/// Peek at the contents of a DnD offer
|
||||
pub fn peek_offer<D: AllowedMimeTypes + 'static>(
|
||||
&self,
|
||||
mime_type: MimeType,
|
||||
mime_type: Option<MimeType>,
|
||||
) -> std::io::Result<D> {
|
||||
let Some(mime_type) = mime_type.or_else(|| D::allowed().first().cloned()) else {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Other, "No mime type provided."));
|
||||
};
|
||||
|
||||
self.request_sender
|
||||
.send(crate::worker::Command::DndRequest(DndRequest::Peek(mime_type)))
|
||||
.map_err(|_| {
|
||||
|
|
|
|||
116
src/dnd/state.rs
116
src/dnd/state.rs
|
|
@ -9,6 +9,7 @@ use sctk::data_device_manager::WritePipe;
|
|||
use sctk::reexports::calloop::PostAction;
|
||||
use sctk::reexports::client::protocol::wl_data_device::WlDataDevice;
|
||||
use sctk::reexports::client::protocol::wl_data_device_manager::DndAction;
|
||||
use sctk::reexports::client::protocol::wl_shm::Format;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::Proxy;
|
||||
use wayland_backend::client::ObjectId;
|
||||
|
|
@ -16,7 +17,7 @@ use wayland_backend::client::ObjectId;
|
|||
use crate::mime::{AsMimeTypes, MimeType};
|
||||
use crate::state::{set_non_blocking, State};
|
||||
|
||||
use super::{DndDestinationRectangle, DndEvent, DndRequest, DndSurface, OfferEvent};
|
||||
use super::{DndDestinationRectangle, DndEvent, DndRequest, DndSurface, Icon, OfferEvent};
|
||||
|
||||
pub(crate) struct DndState<T> {
|
||||
pub(crate) sender: Option<Box<dyn crate::dnd::Sender<T>>>,
|
||||
|
|
@ -26,6 +27,7 @@ pub(crate) struct DndState<T> {
|
|||
source_actions: DndAction,
|
||||
selected_action: DndAction,
|
||||
selected_mime: Option<MimeType>,
|
||||
pub(crate) icon_surface: Option<WlSurface>,
|
||||
pub(crate) source_content: Option<Box<dyn AsMimeTypes>>,
|
||||
accept_ctr: u32,
|
||||
}
|
||||
|
|
@ -42,6 +44,7 @@ impl<T> Default for DndState<T> {
|
|||
selected_mime: None,
|
||||
source_content: None,
|
||||
accept_ctr: 1,
|
||||
icon_surface: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +52,12 @@ impl<T> Default for DndState<T> {
|
|||
impl<T> DndState<T> {
|
||||
pub(crate) fn selected_action(&mut self, a: DndAction) {
|
||||
self.selected_action = a;
|
||||
if let Some(tx) = self.sender.as_ref() {
|
||||
_ = tx.send(DndEvent::Offer(
|
||||
self.active_surface.as_ref().and_then(|(_, d)| d.as_ref().map(|d| d.id)),
|
||||
OfferEvent::SelectedAction(a),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -110,14 +119,37 @@ where
|
|||
}
|
||||
return (s.clone(), None);
|
||||
};
|
||||
if let (Some((action, preferred_action)), Some(mime_type), Some(dnd_state)) =
|
||||
(actions, mime, dnd_state.as_ref())
|
||||
{
|
||||
dnd_state.set_actions(action, preferred_action);
|
||||
self.dnd_state.selected_mime = Some(mime_type.clone());
|
||||
dnd_state
|
||||
.accept_mime_type(self.dnd_state.accept_ctr, Some(mime_type.to_string()));
|
||||
self.dnd_state.accept_ctr = self.dnd_state.accept_ctr.wrapping_add(1);
|
||||
if !had_dest.is_some_and(|old_id| old_id == dest.id) {
|
||||
if let (Some((action, preferred_action)), Some(mime_type), Some(dnd_state)) =
|
||||
(actions, mime, dnd_state.as_ref())
|
||||
{
|
||||
if let Some((tx, old_id)) = self.dnd_state.sender.as_ref().zip(had_dest) {
|
||||
_ = tx.send(DndEvent::Offer(
|
||||
Some(old_id),
|
||||
super::OfferEvent::LeaveDestination,
|
||||
));
|
||||
}
|
||||
if let Some(tx) = self.dnd_state.sender.as_ref() {
|
||||
_ = tx.send(DndEvent::Offer(Some(dest.id), OfferEvent::Enter {
|
||||
x,
|
||||
y,
|
||||
surface: s.s.clone(),
|
||||
mime_types: dest.mime_types.clone(),
|
||||
}));
|
||||
|
||||
_ = tx.send(DndEvent::Offer(
|
||||
Some(dest.id),
|
||||
OfferEvent::SelectedAction(self.dnd_state.selected_action),
|
||||
));
|
||||
}
|
||||
dnd_state.set_actions(action, preferred_action);
|
||||
self.dnd_state.selected_mime = Some(mime_type.clone());
|
||||
dnd_state.accept_mime_type(
|
||||
self.dnd_state.accept_ctr,
|
||||
Some(mime_type.to_string()),
|
||||
);
|
||||
self.dnd_state.accept_ctr = self.dnd_state.accept_ctr.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
(s.clone(), Some(dest))
|
||||
});
|
||||
|
|
@ -209,12 +241,7 @@ where
|
|||
}
|
||||
|
||||
pub(crate) fn offer_motion(&mut self, x: f64, y: f64, wl_data_device: &WlDataDevice) {
|
||||
let Some((surface, dest)) = self
|
||||
.dnd_state
|
||||
.active_surface
|
||||
.clone()
|
||||
.map(|(s, dest)| (s, dest.filter(|d| d.rectangle.contains(x, y))))
|
||||
else {
|
||||
let Some(surface) = self.dnd_state.active_surface.clone().map(|(s, _)| s) else {
|
||||
return;
|
||||
};
|
||||
let Some(data_device) = self
|
||||
|
|
@ -229,9 +256,7 @@ where
|
|||
// Ignore cancelled internal DnD
|
||||
return;
|
||||
}
|
||||
if dest.is_none() {
|
||||
self.update_active_surface(&surface.surface, x, y, drag_offer.as_ref());
|
||||
}
|
||||
self.update_active_surface(&surface.surface, x, y, drag_offer.as_ref());
|
||||
let id = self.cur_id();
|
||||
if let Some(tx) = self.dnd_state.sender.as_ref() {
|
||||
_ = tx.send(DndEvent::Offer(id, super::OfferEvent::Motion { x, y }));
|
||||
|
|
@ -251,7 +276,11 @@ where
|
|||
match r {
|
||||
DndRequest::InitDnd(sender) => self.dnd_state.sender = Some(sender),
|
||||
DndRequest::Surface(s, dests) => {
|
||||
self.dnd_state.destinations.insert(s.surface.id(), (s, dests));
|
||||
if dests.is_empty() {
|
||||
self.dnd_state.destinations.remove(&s.surface.id());
|
||||
} else {
|
||||
self.dnd_state.destinations.insert(s.surface.id(), (s, dests));
|
||||
}
|
||||
},
|
||||
DndRequest::StartDnd { internal, source, icon, content, actions } => {
|
||||
_ = self.start_dnd(internal, source, icon, content, actions);
|
||||
|
|
@ -260,8 +289,12 @@ where
|
|||
_ = self.user_selected_action(a);
|
||||
},
|
||||
DndRequest::DndEnd => {
|
||||
if let Some(s) = self.dnd_state.icon_surface.take() {
|
||||
_ = s.destroy();
|
||||
}
|
||||
self.dnd_state.source_content = None;
|
||||
self.dnd_state.dnd_source = None;
|
||||
self.pool.remove(&0);
|
||||
},
|
||||
DndRequest::Peek(mime_type) => {
|
||||
if let Err(err) = self.load_dnd(mime_type, true) {
|
||||
|
|
@ -275,7 +308,7 @@ where
|
|||
&mut self,
|
||||
internal: bool,
|
||||
source_surface: DndSurface<T>,
|
||||
icon: Option<DndSurface<T>>,
|
||||
mut icon: Option<Icon<DndSurface<T>>>,
|
||||
content: Box<dyn AsMimeTypes + Send>,
|
||||
actions: DndAction,
|
||||
) -> std::io::Result<()> {
|
||||
|
|
@ -294,11 +327,36 @@ where
|
|||
.as_ref()
|
||||
.ok_or_else(|| Error::new(ErrorKind::Other, "data device missing"))?;
|
||||
|
||||
let (icon_surface, buffer) = if let Some(i) = icon.take() {
|
||||
match i {
|
||||
Icon::Surface(s) => (Some(s.surface.clone()), None),
|
||||
Icon::Buf { data, width, height, transparent } => {
|
||||
let surface = self.compositor_state.create_surface(&self.queue_handle);
|
||||
self.pool.remove(&0);
|
||||
let (_, wl_buffer, buf) = self
|
||||
.pool
|
||||
.create_buffer(
|
||||
width as i32,
|
||||
width as i32 * 4,
|
||||
height as i32,
|
||||
&0,
|
||||
if transparent { Format::Argb8888 } else { Format::Xrgb8888 },
|
||||
)
|
||||
.map_err(|err| Error::new(ErrorKind::Other, err))?;
|
||||
buf.copy_from_slice(&data);
|
||||
|
||||
(Some(surface), Some((wl_buffer, width, height)))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if internal {
|
||||
DragSource::start_internal_drag(
|
||||
data_device,
|
||||
&source_surface.surface,
|
||||
icon.as_ref().map(|s| &s.surface),
|
||||
icon_surface.as_ref(),
|
||||
serial,
|
||||
)
|
||||
} else {
|
||||
|
|
@ -314,17 +372,21 @@ where
|
|||
)
|
||||
})
|
||||
.ok_or_else(|| Error::new(ErrorKind::Other, "data device manager missing"))?;
|
||||
source.start_drag(
|
||||
data_device,
|
||||
&source_surface.surface,
|
||||
icon.as_ref().map(|s| &s.surface),
|
||||
serial,
|
||||
);
|
||||
source.start_drag(data_device, &source_surface.surface, icon_surface.as_ref(), serial);
|
||||
|
||||
self.dnd_state.dnd_source = Some(source);
|
||||
self.dnd_state.source_content = Some(content);
|
||||
self.dnd_state.source_actions = actions;
|
||||
}
|
||||
|
||||
if let (Some((wl_buffer, width, height)), Some(surface)) = (buffer, icon_surface) {
|
||||
surface.damage_buffer(0, 0, width as i32, height as i32);
|
||||
surface.attach(Some(wl_buffer), 0, 0);
|
||||
surface.commit();
|
||||
|
||||
self.dnd_state.icon_surface = Some(surface);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
112
src/state.rs
112
src/state.rs
|
|
@ -7,20 +7,25 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
|||
use std::rc::Rc;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use sctk::compositor::{CompositorHandler, CompositorState};
|
||||
use sctk::data_device_manager::data_device::{DataDevice, DataDeviceHandler};
|
||||
use sctk::data_device_manager::data_offer::{DataOfferError, DataOfferHandler, DragOffer};
|
||||
use sctk::data_device_manager::data_source::{CopyPasteSource, DataSourceHandler};
|
||||
use sctk::data_device_manager::{DataDeviceManagerState, WritePipe};
|
||||
use sctk::output::{OutputHandler, OutputState};
|
||||
use sctk::primary_selection::device::{PrimarySelectionDevice, PrimarySelectionDeviceHandler};
|
||||
use sctk::primary_selection::selection::{PrimarySelectionSource, PrimarySelectionSourceHandler};
|
||||
use sctk::primary_selection::PrimarySelectionManagerState;
|
||||
use sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::registry::{ProvidesRegistryState, RegistryState};
|
||||
use sctk::seat::pointer::{PointerData, PointerEvent, PointerEventKind, PointerHandler};
|
||||
use sctk::seat::{Capability, SeatHandler, SeatState};
|
||||
use sctk::shm::multi::MultiPool;
|
||||
use sctk::shm::{Shm, ShmHandler};
|
||||
use sctk::{
|
||||
delegate_data_device, delegate_pointer, delegate_primary_selection, delegate_registry,
|
||||
delegate_seat, registry_handlers,
|
||||
delegate_compositor, delegate_data_device, delegate_output, delegate_pointer,
|
||||
delegate_primary_selection, delegate_registry, delegate_seat, delegate_shm, registry_handlers,
|
||||
};
|
||||
|
||||
use sctk::reexports::calloop::{LoopHandle, PostAction};
|
||||
|
|
@ -68,6 +73,10 @@ pub struct State<T> {
|
|||
data_selection_mime_types: Rc<Cow<'static, [MimeType]>>,
|
||||
#[cfg(feature = "dnd")]
|
||||
pub(crate) dnd_state: crate::dnd::state::DndState<T>,
|
||||
pub(crate) compositor_state: CompositorState,
|
||||
output_state: OutputState,
|
||||
pub(crate) shm: Shm,
|
||||
pub(crate) pool: MultiPool<u8>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
|
|
@ -90,6 +99,11 @@ impl<T: 'static + Clone> State<T> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let compositor_state =
|
||||
CompositorState::bind(globals, queue_handle).expect("wl_compositor not available");
|
||||
let output_state = OutputState::new(globals, queue_handle);
|
||||
let shm = Shm::bind(globals, queue_handle).expect("wl_shm not available");
|
||||
|
||||
let seat_state = SeatState::new(globals, queue_handle);
|
||||
for seat in seat_state.seats() {
|
||||
seats.insert(seat.id(), Default::default());
|
||||
|
|
@ -115,6 +129,10 @@ impl<T: 'static + Clone> State<T> {
|
|||
#[cfg(feature = "dnd")]
|
||||
dnd_state: DndState::default(),
|
||||
_phantom: PhantomData,
|
||||
compositor_state,
|
||||
output_state,
|
||||
pool: MultiPool::new(&shm).expect("Failed to create memory pool."),
|
||||
shm,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -475,6 +493,8 @@ impl<T: 'static + Clone> DataSourceHandler for State<T> {
|
|||
if let Some(s) = self.dnd_state.sender.as_ref() {
|
||||
_ = s.send(DndEvent::Source(crate::dnd::SourceEvent::Cancelled));
|
||||
}
|
||||
_ = self.pool.remove(&0);
|
||||
self.dnd_state.icon_surface = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -501,6 +521,8 @@ impl<T: 'static + Clone> DataSourceHandler for State<T> {
|
|||
if let Some(s) = self.dnd_state.sender.as_ref() {
|
||||
_ = s.send(DndEvent::Source(crate::dnd::SourceEvent::Dropped))
|
||||
}
|
||||
_ = self.pool.remove(&0);
|
||||
self.dnd_state.icon_surface = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -620,6 +642,92 @@ impl<T: 'static + Clone> Dispatch<WlKeyboard, ObjectId, State<T>> for State<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone> CompositorHandler for State<T> {
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &sctk::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_new_factor: i32,
|
||||
) {
|
||||
}
|
||||
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &sctk::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_new_transform: sctk::reexports::client::protocol::wl_output::Transform,
|
||||
) {
|
||||
}
|
||||
|
||||
fn frame(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &sctk::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: &WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: &WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone> OutputHandler for State<T> {
|
||||
fn output_state(&mut self) -> &mut sctk::output::OutputState {
|
||||
&mut self.output_state
|
||||
}
|
||||
|
||||
fn new_output(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: sctk::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn update_output(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: sctk::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn output_destroyed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: sctk::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone> ShmHandler for State<T> {
|
||||
fn shm_state(&mut self) -> &mut Shm {
|
||||
&mut self.shm
|
||||
}
|
||||
}
|
||||
|
||||
delegate_compositor!(@<T: 'static + Clone> State<T>);
|
||||
delegate_output!(@<T: 'static + Clone> State<T>);
|
||||
delegate_shm!(@<T: 'static + Clone> State<T>);
|
||||
delegate_seat!(@<T: 'static + Clone> State<T>);
|
||||
delegate_pointer!(@<T: 'static + Clone> State<T>);
|
||||
delegate_data_device!(@<T: 'static + Clone> State<T>);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue