Skip to content

Commit

Permalink
Read the pipe without blocking
Browse files Browse the repository at this point in the history
  • Loading branch information
SludgePhD committed Aug 19, 2022
1 parent 9ee3ce7 commit 9430d56
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 39 deletions.
1 change: 0 additions & 1 deletion src/platform_impl/linux/wayland/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ impl<T: 'static> EventLoop<T> {
event_sink,
window_user_requests,
window_compositor_updates,
display: display.clone(),
}),
event_loop_handle,
output_manager,
Expand Down
5 changes: 0 additions & 5 deletions src/platform_impl/linux/wayland/event_loop/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

use std::collections::HashMap;

use wayland_client::Display;

use super::EventSink;
use crate::platform_impl::wayland::window::shim::{
WindowCompositorUpdate, WindowHandle, WindowUserRequest,
Expand All @@ -30,7 +28,4 @@ pub struct WinitState {
/// and requests from winit's windows are being forwarded to them either via
/// `WindowUpdate` or buffer on the associated with it `WindowHandle`.
pub window_map: HashMap<WindowId, WindowHandle>,

/// Handle to the display, to flush events when needed.
pub display: Display,
}
113 changes: 84 additions & 29 deletions src/platform_impl/linux/wayland/seat/dnd/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use std::{
io::{self, BufRead, BufReader},
io::{self, Read},
os::unix::prelude::{AsRawFd, RawFd},
path::PathBuf,
str,
};

use percent_encoding::percent_decode_str;
use sctk::data_device::{DataOffer, DndEvent};
use wayland_client::Display;
use sctk::{
data_device::{DataOffer, DndEvent, ReadPipe},
reexports::calloop::{generic::Generic, Interest, LoopHandle, Mode, PostAction},
};

use crate::{
dpi::PhysicalPosition,
Expand All @@ -27,11 +31,10 @@ pub(super) fn handle_dnd(event: DndEvent<'_>, inner: &mut DndInner, winit_state:
..
} => {
let window_id = make_wid(&surface);

if let Ok(paths) = parse_offer(&winit_state.display, offer) {
inner.window_id = Some(window_id);
offer.accept(Some(MIME_TYPE.into()));
let _ = parse_offer(&inner.loop_handle, offer, move |paths, winit_state| {
if !paths.is_empty() {
offer.accept(Some(MIME_TYPE.into()));

winit_state.event_sink.push_window_event(
WindowEvent::CursorEntered {
device_id: crate::event::DeviceId(
Expand All @@ -56,21 +59,20 @@ pub(super) fn handle_dnd(event: DndEvent<'_>, inner: &mut DndInner, winit_state:
.event_sink
.push_window_event(WindowEvent::HoveredFile(path), window_id);
}
inner.window_id = Some(window_id);
}
}
});
}
DndEvent::Drop { offer: Some(offer) } => {
if let Some(window_id) = inner.window_id {
inner.window_id = None;

if let Ok(paths) = parse_offer(&winit_state.display, offer) {
let _ = parse_offer(&inner.loop_handle, offer, move |paths, winit_state| {
for path in paths {
winit_state
.event_sink
.push_window_event(WindowEvent::DroppedFile(path), window_id);
}
}
});
}
}
DndEvent::Leave => {
Expand Down Expand Up @@ -108,29 +110,82 @@ pub(super) fn handle_dnd(event: DndEvent<'_>, inner: &mut DndInner, winit_state:
}
}

fn parse_offer(display: &Display, offer: &DataOffer) -> io::Result<Vec<PathBuf>> {
fn parse_offer(
loop_handle: &LoopHandle<'static, WinitState>,
offer: &DataOffer,
mut callback: impl FnMut(Vec<PathBuf>, &mut WinitState) + 'static,
) -> io::Result<()> {
let can_accept = offer.with_mime_types(|types| types.iter().any(|s| s == MIME_TYPE));
if can_accept {
// Format: https://www.iana.org/assignments/media-types/text/uri-list
let mut paths = Vec::new();
let pipe = offer.receive(MIME_TYPE.into())?;
let _ = display.flush();
for line in BufReader::new(pipe).lines() {
let line = line?;
if line.starts_with('#') {
continue;
read_pipe_nonblocking(pipe, loop_handle, move |bytes, winit_state| {
// Format: https://www.iana.org/assignments/media-types/text/uri-list
let mut paths = Vec::new();
for line in bytes.split(|b| *b == b'\n') {
let line = match str::from_utf8(line) {
Ok(line) => line,
Err(_) => continue,
};

if line.starts_with('#') {
continue;
}

let decoded = match percent_decode_str(&line).decode_utf8() {
Ok(decoded) => decoded,
Err(_) => continue,
};
if let Some(path) = decoded.strip_prefix("file://") {
paths.push(PathBuf::from(path));
}
}
callback(paths, winit_state);
})?;
}
Ok(())
}

let decoded = match percent_decode_str(&line).decode_utf8() {
Ok(decoded) => decoded,
Err(_) => continue,
};
if let Some(path) = decoded.strip_prefix("file://") {
paths.push(PathBuf::from(path));
fn read_pipe_nonblocking(
pipe: ReadPipe,
loop_handle: &LoopHandle<'static, WinitState>,
mut callback: impl FnMut(Vec<u8>, &mut WinitState) + 'static,
) -> io::Result<()> {
unsafe {
make_fd_nonblocking(pipe.as_raw_fd())?;
}

let mut content = Vec::<u8>::with_capacity(u16::MAX as usize);
let mut reader_buffer = [0; u16::MAX as usize];
let reader = Generic::new(pipe, Interest::READ, Mode::Level);

let _ = loop_handle.insert_source(reader, move |_, file, winit_state| {
let action = loop {
match file.read(&mut reader_buffer) {
Ok(0) => {
let data = std::mem::take(&mut content);
callback(data, winit_state);
break PostAction::Remove;
}
Ok(n) => content.extend_from_slice(&reader_buffer[..n]),
Err(err) if err.kind() == io::ErrorKind::WouldBlock => break PostAction::Continue,
Err(_) => break PostAction::Remove,
}
}
Ok(paths)
} else {
Ok(Vec::new())
};

Ok(action)
});
Ok(())
}

unsafe fn make_fd_nonblocking(raw_fd: RawFd) -> io::Result<()> {
let flags = libc::fcntl(raw_fd, libc::F_GETFL);
if flags < 0 {
return Err(io::Error::from_raw_os_error(flags));
}
let result = libc::fcntl(raw_fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
if result < 0 {
return Err(io::Error::from_raw_os_error(result));
}

Ok(())
}
14 changes: 11 additions & 3 deletions src/platform_impl/linux/wayland/seat/dnd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod handlers;

use sctk::data_device::DataDevice;
use sctk::{data_device::DataDevice, reexports::calloop::LoopHandle};
use wayland_client::{
protocol::{wl_data_device_manager::WlDataDeviceManager, wl_seat::WlSeat},
Attached,
Expand All @@ -13,8 +13,15 @@ pub(crate) struct Dnd {
}

impl Dnd {
pub fn new(seat: &Attached<WlSeat>, manager: &WlDataDeviceManager) -> Self {
let mut inner = DndInner { window_id: None };
pub fn new(
seat: &Attached<WlSeat>,
manager: &WlDataDeviceManager,
loop_handle: LoopHandle<'static, WinitState>,
) -> Self {
let mut inner = DndInner {
loop_handle,
window_id: None,
};
let data_device =
DataDevice::init_for_seat(manager, seat, move |event, mut dispatch_data| {
let winit_state = dispatch_data.get::<WinitState>().unwrap();
Expand All @@ -27,6 +34,7 @@ impl Dnd {
}

struct DndInner {
loop_handle: LoopHandle<'static, WinitState>,
/// Window ID of the currently hovered window.
window_id: Option<WindowId>,
}
6 changes: 5 additions & 1 deletion src/platform_impl/linux/wayland/seat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ impl SeatManagerInner {
if seat_data.defunct {
seat_info.dnd = None;
} else {
seat_info.dnd = Some(Dnd::new(seat, data_device_manager));
seat_info.dnd = Some(Dnd::new(
seat,
data_device_manager,
self.loop_handle.clone(),
));
}
}
}
Expand Down

0 comments on commit 9430d56

Please sign in to comment.