diff --git a/window/src/os/x11/window.rs b/window/src/os/x11/window.rs index 18024097a..da0de8227 100644 --- a/window/src/os/x11/window.rs +++ b/window/src/os/x11/window.rs @@ -18,8 +18,10 @@ use raw_window_handle::{ }; use std::any::Any; use std::convert::TryInto; +use std::path::PathBuf; use std::rc::{Rc, Weak}; use std::sync::{Arc, Mutex}; +use url::Url; use wezterm_font::FontConfiguration; use wezterm_input_types::{KeyCode, KeyEvent, KeyboardLedStatus, Modifiers}; use xcb::x::{Atom, PropMode}; @@ -1116,84 +1118,16 @@ impl XWindowInner { 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::>() - .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::>() - .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::>(); + let data = decode_dropped_url_string(prop.value()); + let urls = parse_xmozurl_list(&data); 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| { - if line.starts_with('#') || line.trim().is_empty() { - // text/uri-list: Any lines beginning with the '#' character - // are comment lines and are ignored during processing - return None; - } - use url::Url; - let url = Url::parse(line) - .map_err(|err| { - log::error!( - "Error parsing dropped file line {} as url: {:#}", - line, - err - ); - }) - .ok()?; - url.to_file_path() - .map_err(|_| { - log::error!( - "Error converting url {} from line {} to pathbuf", - url, - line - ); - }) - .ok() - }) - .collect::>(); + let paths = parse_texturi_list(prop.value()); self.events.dispatch(WindowEvent::DroppedFile(paths)); } } Err(err) => { - log::error!("clipboard: err while getting clipboard property: {:?}", err); + log::error!("clipboard: err while getting clipboard property: {err:#}"); } } conn.send_request_no_reply_log(&xcb::x::SendEvent { @@ -2163,6 +2097,67 @@ impl WindowOps for XWindow { } } +fn parse_texturi_list(url_list: &[u8]) -> Vec { + String::from_utf8_lossy(url_list) + .lines() + .filter_map(|line| { + if line.starts_with('#') || line.trim().is_empty() { + // text/uri-list: Any lines beginning with the '#' character + // are comment lines and are ignored during processing + return None; + } + let url = Url::parse(line) + .map_err(|err| { + log::error!("Error parsing dropped file line {line} as url: {err:#}"); + }) + .ok()?; + url.to_file_path() + .map_err(|_| { + log::error!("Error converting url {url:?} from line {line} to pathbuf"); + }) + .ok() + }) + .collect() +} + +fn parse_xmozurl_list(url_list: &str) -> Vec { + url_list + .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 {line} as url: {err:#}"); + }) + .ok() + }) + .collect() +} + +/// Data may be UTF16 in either byte order, or UTF8 +fn decode_dropped_url_string(raw: &[u8]) -> String { + if raw.len() >= 2 && ((raw[0], raw[1]) == (0xfe, 0xff) || (raw[0] != 0x00 && raw[1] == 0x00)) { + String::from_utf16_lossy( + raw.chunks_exact(2) + .map(|x: &[u8]| u16::from(x[1]) << 8 | u16::from(x[0])) + .collect::>() + .as_slice(), + ) + } else if raw.len() >= 2 + && ((raw[0], raw[1]) == (0xff, 0xfe) || (raw[0] == 0x00 && raw[1] != 0x00)) + { + String::from_utf16_lossy( + raw.chunks_exact(2) + .map(|x: &[u8]| u16::from(x[0]) << 8 | u16::from(x[1])) + .collect::>() + .as_slice(), + ) + } else { + String::from_utf8_lossy(raw).to_string() + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[repr(u32)] enum NetWmStateAction {