Something went wrong. Try again.
A hardware-accelerated GPU terminal emulator focusing to run in desktops and browsers.
Something went wrong. Try again.
5.6 kB · 188 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189use std::io;use std::os::raw::*;use std::path::{Path, PathBuf};use std::str::Utf8Error;use std::sync::Arc;
use percent_encoding::percent_decode;use x11rb::protocol::xproto::{self, ConnectionExt};
use super::atoms::AtomName::None as DndNone;use super::atoms::*;use super::{util, CookieResultExt, X11Error, XConnection};
#[derive(Debug, Clone, Copy)]pub enum DndState { Accepted, Rejected,}
#[derive(Debug)]pub enum DndDataParseError { EmptyData, InvalidUtf8(#[allow(dead_code)] Utf8Error), HostnameSpecified(#[allow(dead_code)] String), UnexpectedProtocol(#[allow(dead_code)] String), UnresolvablePath(#[allow(dead_code)] io::Error),}
impl From<Utf8Error> for DndDataParseError { fn from(e: Utf8Error) -> Self { DndDataParseError::InvalidUtf8(e) }}
impl From<io::Error> for DndDataParseError { fn from(e: io::Error) -> Self { DndDataParseError::UnresolvablePath(e) }}
pub struct Dnd { xconn: Arc<XConnection>, // Populated by XdndEnter event handler pub version: Option<c_long>, pub type_list: Option<Vec<xproto::Atom>>, // Populated by XdndPosition event handler pub source_window: Option<xproto::Window>, // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) pub result: Option<Result<Vec<PathBuf>, DndDataParseError>>,}
impl Dnd { pub fn new(xconn: Arc<XConnection>) -> Result<Self, X11Error> { Ok(Dnd { xconn, version: None, type_list: None, source_window: None, result: None, }) }
pub fn reset(&mut self) { self.version = None; self.type_list = None; self.source_window = None; self.result = None; }
pub unsafe fn send_status( &self, this_window: xproto::Window, target_window: xproto::Window, state: DndState, ) -> Result<(), X11Error> { let atoms = self.xconn.atoms(); let (accepted, action) = match state { DndState::Accepted => (1, atoms[XdndActionPrivate]), DndState::Rejected => (0, atoms[DndNone]), }; self.xconn .send_client_msg( target_window, target_window, atoms[XdndStatus] as _, None, [this_window, accepted, 0, 0, action as _], )? .ignore_error();
Ok(()) }
pub unsafe fn send_finished( &self, this_window: xproto::Window, target_window: xproto::Window, state: DndState, ) -> Result<(), X11Error> { let atoms = self.xconn.atoms(); let (accepted, action) = match state { DndState::Accepted => (1, atoms[XdndActionPrivate]), DndState::Rejected => (0, atoms[DndNone]), }; self.xconn .send_client_msg( target_window, target_window, atoms[XdndFinished] as _, None, [this_window, accepted, action as _, 0, 0], )? .ignore_error();
Ok(()) }
pub unsafe fn get_type_list( &self, source_window: xproto::Window, ) -> Result<Vec<xproto::Atom>, util::GetPropertyError> { let atoms = self.xconn.atoms(); self.xconn.get_property( source_window, atoms[XdndTypeList], xproto::Atom::from(xproto::AtomEnum::ATOM), ) }
pub unsafe fn convert_selection( &self, window: xproto::Window, time: xproto::Timestamp, ) { let atoms = self.xconn.atoms(); self.xconn .xcb_connection() .convert_selection( window, atoms[XdndSelection], atoms[TextUriList], atoms[XdndSelection], time, ) .expect_then_ignore_error("Failed to send XdndSelection event") }
pub unsafe fn read_data( &self, window: xproto::Window, ) -> Result<Vec<c_uchar>, util::GetPropertyError> { let atoms = self.xconn.atoms(); self.xconn .get_property(window, atoms[XdndSelection], atoms[TextUriList]) }
pub fn parse_data( &self, data: &mut [c_uchar], ) -> Result<Vec<PathBuf>, DndDataParseError> { if !data.is_empty() { let mut path_list = Vec::new(); let decoded = percent_decode(data).decode_utf8()?.into_owned(); for uri in decoded.split("\r\n").filter(|u| !u.is_empty()) { // The format is specified as protocol://host/path // However, it's typically simply protocol:///path let path_str = if uri.starts_with("file://") { let path_str = uri.replace("file://", ""); if !path_str.starts_with('/') { // A hostname is specified // Supporting this case is beyond the scope of my mental health return Err(DndDataParseError::HostnameSpecified(path_str)); } path_str } else { // Only the file protocol is supported return Err(DndDataParseError::UnexpectedProtocol(uri.to_owned())); };
let path = Path::new(&path_str).canonicalize()?; path_list.push(path); } Ok(path_list) } else { Err(DndDataParseError::EmptyData) } }}