Skip to content

Commit

Permalink
Accept drag and drop of URLs from browsers and plain text on X11
Browse files Browse the repository at this point in the history
For filenames and urls an additional space is inserted after the last
item to enable adding more files and urls with another drag-and-drop
operation without the need to manually enter the space in between.
  • Loading branch information
ssiegel authored and wez committed May 5, 2024
1 parent b888c54 commit 3d511bb
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 5 deletions.
25 changes: 24 additions & 1 deletion wezterm-gui/src/termwindow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,28 @@ impl TermWindow {
}
Ok(true)
}
WindowEvent::DroppedString(text) => {
let pane = match self.get_active_pane_or_overlay() {
Some(pane) => pane,
None => return Ok(true),
};
pane.send_paste(text.as_str())?;
Ok(true)
}
WindowEvent::DroppedUrl(urls) => {
let pane = match self.get_active_pane_or_overlay() {
Some(pane) => pane,
None => return Ok(true),
};
let urls = urls
.iter()
.map(|url| self.config.quote_dropped_files.escape(&url.to_string()))
.collect::<Vec<_>>()
.join(" ")
+ " ";
pane.send_paste(urls.as_str())?;
Ok(true)
}
WindowEvent::DroppedFile(paths) => {
let pane = match self.get_active_pane_or_overlay() {
Some(pane) => pane,
Expand All @@ -1024,7 +1046,8 @@ impl TermWindow {
.escape(&path.to_string_lossy())
})
.collect::<Vec<_>>()
.join(" ");
.join(" ")
+ " ";
pane.send_paste(&paths)?;
Ok(true)
}
Expand Down
2 changes: 2 additions & 0 deletions window/examples/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ impl MyWindow {
| WindowEvent::FocusChanged(_)
| WindowEvent::DraggedFile(_)
| WindowEvent::DroppedFile(_)
| WindowEvent::DroppedUrl(_)
| WindowEvent::DroppedString(_)
| WindowEvent::PerformKeyAssignment(_)
| WindowEvent::MouseLeave
| WindowEvent::SetInnerSizeCompleted => {}
Expand Down
7 changes: 7 additions & 0 deletions window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::any::Any;
use std::path::PathBuf;
use std::rc::Rc;
use thiserror::Error;
use url::Url;
pub mod bitmaps;
pub use wezterm_color_types as color;
mod configuration;
Expand Down Expand Up @@ -205,6 +206,12 @@ pub enum WindowEvent {
// Called when the files are dropped into the window
DroppedFile(Vec<PathBuf>),

// Called when urls are dropped into the window
DroppedUrl(Vec<Url>),

// Called when text is dropped into the window
DroppedString(String),

/// Called by menubar dispatching stuff on some systems
PerformKeyAssignment(config::keyassignment::KeyAssignment),

Expand Down
3 changes: 3 additions & 0 deletions window/src/os/x11/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub struct XConnection {
pub atom_targets: Atom,
pub atom_clipboard: Atom,
pub atom_texturilist: Atom,
pub atom_xmozurl: Atom,
pub atom_xdndaware: Atom,
pub atom_xdndtypelist: Atom,
pub atom_xdndselection: Atom,
Expand Down Expand Up @@ -626,6 +627,7 @@ impl XConnection {
let atom_targets = Self::intern_atom(&conn, "TARGETS")?;
let atom_clipboard = Self::intern_atom(&conn, "CLIPBOARD")?;
let atom_texturilist = Self::intern_atom(&conn, "text/uri-list")?;
let atom_xmozurl = Self::intern_atom(&conn, "text/x-moz-url")?;
let atom_xdndaware = Self::intern_atom(&conn, "XdndAware")?;
let atom_xdndtypelist = Self::intern_atom(&conn, "XdndTypeList")?;
let atom_xdndselection = Self::intern_atom(&conn, "XdndSelection")?;
Expand Down Expand Up @@ -762,6 +764,7 @@ impl XConnection {
atom_protocols,
atom_clipboard,
atom_texturilist,
atom_xmozurl,
atom_xdndaware,
atom_xdndtypelist,
atom_xdndselection,
Expand Down
62 changes: 58 additions & 4 deletions window/src/os/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,10 +576,17 @@ impl XWindowInner {
};
}
self.drag_and_drop.target_type = xcb::x::ATOM_NONE;
for t in &self.drag_and_drop.src_types {
if *t == conn.atom_texturilist {
self.drag_and_drop.target_type = conn.atom_texturilist;
for t in [
conn.atom_texturilist,
conn.atom_xmozurl,
conn.atom_utf8_string,
] {
if self.drag_and_drop.src_types.contains(&t) {
self.drag_and_drop.target_type = t;
break;
}
}
for t in &self.drag_and_drop.src_types {
log::trace!("types offered: {}", conn.atom_name(*t));
}
log::trace!(
Expand Down Expand Up @@ -1105,7 +1112,54 @@ impl XWindowInner {
long_length: u32::max_value(),
}) {
Ok(prop) => {
if selection.target() == conn.atom_texturilist {
if selection.target() == conn.atom_utf8_string {
let text = String::from_utf8_lossy(prop.value()).to_string();
self.events.dispatch(WindowEvent::DroppedString(text));
} else if selection.target() == conn.atom_xmozurl {
let raw = prop.value();
let data;
if raw.len() >= 2
&& ((raw[0], raw[1]) == (0xfe, 0xff)
|| (raw[0] != 0x00 && raw[1] == 0x00))
{
data = String::from_utf16_lossy(
raw.chunks_exact(2)
.map(|x: &[u8]| u16::from(x[1]) << 8 | u16::from(x[0]))
.collect::<Vec<u16>>()
.as_slice(),
);
} else if raw.len() >= 2
&& ((raw[0], raw[1]) == (0xff, 0xfe)
|| (raw[0] == 0x00 && raw[1] != 0x00))
{
data = String::from_utf16_lossy(
raw.chunks_exact(2)
.map(|x: &[u8]| u16::from(x[0]) << 8 | u16::from(x[1]))
.collect::<Vec<u16>>()
.as_slice(),
);
} else {
data = String::from_utf8_lossy(prop.value()).to_string();
}
use url::Url;
let urls = data
.lines()
.step_by(2)
.filter_map(|line| {
// the lines alternate between the urls and their titles
Url::parse(line)
.map_err(|err| {
log::error!(
"Error parsing dropped file line {} as url: {:#}",
line,
err
);
})
.ok()
})
.collect::<Vec<_>>();
self.events.dispatch(WindowEvent::DroppedUrl(urls));
} else if selection.target() == conn.atom_texturilist {
let paths = String::from_utf8_lossy(prop.value())
.lines()
.filter_map(|line| {
Expand Down

0 comments on commit 3d511bb

Please sign in to comment.