1
1
mirror of https://github.com/wez/wezterm.git synced 2024-08-17 02:00:25 +03:00

Accept drag and drop of URLs from browsers and plain text on X11

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.
This commit is contained in:
Stefan Siegel 2024-04-18 04:36:15 +02:00 committed by Wez Furlong
parent b888c547db
commit 3d511bbd67
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
5 changed files with 94 additions and 5 deletions

View File

@ -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,
@ -1024,7 +1046,8 @@ impl TermWindow {
.escape(&path.to_string_lossy())
})
.collect::<Vec<_>>()
.join(" ");
.join(" ")
+ " ";
pane.send_paste(&paths)?;
Ok(true)
}

View File

@ -85,6 +85,8 @@ impl MyWindow {
| WindowEvent::FocusChanged(_)
| WindowEvent::DraggedFile(_)
| WindowEvent::DroppedFile(_)
| WindowEvent::DroppedUrl(_)
| WindowEvent::DroppedString(_)
| WindowEvent::PerformKeyAssignment(_)
| WindowEvent::MouseLeave
| WindowEvent::SetInnerSizeCompleted => {}

View File

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

View File

@ -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,
@ -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")?;
@ -762,6 +764,7 @@ impl XConnection {
atom_protocols,
atom_clipboard,
atom_texturilist,
atom_xmozurl,
atom_xdndaware,
atom_xdndtypelist,
atom_xdndselection,

View File

@ -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!(
@ -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| {