x11: Implement file drag and drop (#360)
* x11: Implement file drag and drop * Fixed typo
This commit is contained in:
parent
d2dd82c146
commit
d18db208ff
|
@ -1,6 +1,7 @@
|
|||
# Unreleased
|
||||
|
||||
- Add support for `Touch` for emscripten backend.
|
||||
- Added support for `DroppedFile`, `HoveredFile`, and `HoveredFileCancelled` to X11 backend.
|
||||
|
||||
# Version 0.9.0 (2017-12-01)
|
||||
|
||||
|
|
|
@ -39,3 +39,4 @@ wayland-protocols = { version = "0.12.0", features = ["unstable_protocols"] }
|
|||
wayland-kbd = "0.13.0"
|
||||
wayland-window = "0.13.0"
|
||||
x11-dl = "2.8"
|
||||
percent-encoding = "1.0"
|
||||
|
|
|
@ -107,6 +107,8 @@ extern crate core_foundation;
|
|||
extern crate core_graphics;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate x11_dl;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
||||
#[macro_use]
|
||||
extern crate wayland_client;
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_uchar, c_ulong};
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
const DND_ATOMS_LEN: usize = 12;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DndAtoms {
|
||||
pub aware: ffi::Atom,
|
||||
pub enter: ffi::Atom,
|
||||
pub leave: ffi::Atom,
|
||||
pub drop: ffi::Atom,
|
||||
pub position: ffi::Atom,
|
||||
pub status: ffi::Atom,
|
||||
pub action_private: ffi::Atom,
|
||||
pub selection: ffi::Atom,
|
||||
pub finished: ffi::Atom,
|
||||
pub type_list: ffi::Atom,
|
||||
pub uri_list: ffi::Atom,
|
||||
pub none: ffi::Atom,
|
||||
}
|
||||
|
||||
impl DndAtoms {
|
||||
pub fn new(xconn: &Arc<XConnection>) -> Result<Self, XError> {
|
||||
let mut atoms = Vec::with_capacity(DND_ATOMS_LEN);
|
||||
|
||||
let mut names = [
|
||||
b"XdndAware\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndEnter\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndLeave\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndDrop\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndPosition\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndStatus\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndActionPrivate\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndSelection\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndFinished\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndTypeList\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"text/uri-list\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"None\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
];
|
||||
|
||||
unsafe {
|
||||
(xconn.xlib.XInternAtoms)(
|
||||
xconn.display,
|
||||
names.as_mut_ptr(),
|
||||
DND_ATOMS_LEN as c_int,
|
||||
ffi::False,
|
||||
atoms.as_mut_ptr(),
|
||||
);
|
||||
}
|
||||
xconn.check_errors()?;
|
||||
unsafe { atoms.set_len(DND_ATOMS_LEN); }
|
||||
Ok(DndAtoms {
|
||||
aware: atoms[0],
|
||||
enter: atoms[1],
|
||||
leave: atoms[2],
|
||||
drop: atoms[3],
|
||||
position: atoms[4],
|
||||
status: atoms[5],
|
||||
action_private: atoms[6],
|
||||
selection: atoms[7],
|
||||
finished: atoms[8],
|
||||
type_list: atoms[9],
|
||||
uri_list: atoms[10],
|
||||
none: atoms[11],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DndState {
|
||||
Accepted,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DndDataParseError {
|
||||
EmptyData,
|
||||
InvalidUtf8(Utf8Error),
|
||||
HostnameSpecified(String),
|
||||
UnexpectedProtocol(String),
|
||||
UnresolvablePath(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>,
|
||||
pub atoms: DndAtoms,
|
||||
// Populated by XdndEnter event handler
|
||||
pub version: Option<c_long>,
|
||||
pub type_list: Option<Vec<c_ulong>>,
|
||||
// Populated by XdndPosition event handler
|
||||
pub source_window: Option<c_ulong>,
|
||||
// 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, XError> {
|
||||
let atoms = DndAtoms::new(&xconn)?;
|
||||
Ok(Dnd {
|
||||
xconn,
|
||||
atoms,
|
||||
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: c_ulong,
|
||||
target_window: c_ulong,
|
||||
state: DndState,
|
||||
) {
|
||||
let (accepted, action) = match state {
|
||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
||||
};
|
||||
util::send_client_msg(
|
||||
&self.xconn,
|
||||
target_window,
|
||||
self.atoms.status,
|
||||
(this_window as c_long, accepted, 0, 0, action),
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe fn send_finished(
|
||||
&self,
|
||||
this_window: c_ulong,
|
||||
target_window: c_ulong,
|
||||
state: DndState,
|
||||
) {
|
||||
let (accepted, action) = match state {
|
||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
||||
};
|
||||
util::send_client_msg(
|
||||
&self.xconn,
|
||||
target_window,
|
||||
self.atoms.finished,
|
||||
(this_window as c_long, accepted, action, 0, 0),
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe fn get_type_list(
|
||||
&self,
|
||||
source_window: c_ulong,
|
||||
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
|
||||
util::get_property(
|
||||
&self.xconn,
|
||||
source_window,
|
||||
self.atoms.type_list,
|
||||
ffi::XA_ATOM,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) {
|
||||
(self.xconn.xlib.XConvertSelection)(
|
||||
self.xconn.display,
|
||||
self.atoms.selection,
|
||||
self.atoms.uri_list,
|
||||
self.atoms.selection,
|
||||
window,
|
||||
time,
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe fn read_data(
|
||||
&self,
|
||||
window: c_ulong,
|
||||
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
||||
util::get_property(
|
||||
&self.xconn,
|
||||
window,
|
||||
self.atoms.selection,
|
||||
self.atoms.uri_list,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_data(&self, data: &mut Vec<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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,12 +16,16 @@ use std::sync::atomic::{self, AtomicBool};
|
|||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
|
||||
use libc::{self, c_uchar, c_char, c_int};
|
||||
use libc::{self, c_uchar, c_char, c_int, c_ulong, c_long};
|
||||
|
||||
mod events;
|
||||
mod monitor;
|
||||
mod window;
|
||||
mod xdisplay;
|
||||
mod dnd;
|
||||
mod util;
|
||||
|
||||
use self::dnd::{Dnd, DndState};
|
||||
|
||||
// API TRANSITION
|
||||
//
|
||||
|
@ -33,6 +37,7 @@ mod xdisplay;
|
|||
pub struct EventsLoop {
|
||||
display: Arc<XConnection>,
|
||||
wm_delete_window: ffi::Atom,
|
||||
dnd: Dnd,
|
||||
windows: Arc<Mutex<HashMap<WindowId, WindowData>>>,
|
||||
devices: Mutex<HashMap<DeviceId, Device>>,
|
||||
xi2ext: XExtension,
|
||||
|
@ -55,6 +60,9 @@ impl EventsLoop {
|
|||
let wm_delete_window = unsafe { (display.xlib.XInternAtom)(display.display, b"WM_DELETE_WINDOW\0".as_ptr() as *const c_char, 0) };
|
||||
display.check_errors().expect("Failed to call XInternAtom");
|
||||
|
||||
let dnd = Dnd::new(Arc::clone(&display))
|
||||
.expect("Failed to call XInternAtoms when initializing drag and drop");
|
||||
|
||||
let xi2ext = unsafe {
|
||||
let mut result = XExtension {
|
||||
opcode: mem::uninitialized(),
|
||||
|
@ -93,13 +101,14 @@ impl EventsLoop {
|
|||
|
||||
let result = EventsLoop {
|
||||
pending_wakeup: Arc::new(AtomicBool::new(false)),
|
||||
display: display,
|
||||
wm_delete_window: wm_delete_window,
|
||||
display,
|
||||
wm_delete_window,
|
||||
dnd,
|
||||
windows: Arc::new(Mutex::new(HashMap::new())),
|
||||
devices: Mutex::new(HashMap::new()),
|
||||
xi2ext: xi2ext,
|
||||
root: root,
|
||||
wakeup_dummy_window: wakeup_dummy_window,
|
||||
xi2ext,
|
||||
root,
|
||||
wakeup_dummy_window,
|
||||
};
|
||||
|
||||
{
|
||||
|
@ -138,19 +147,17 @@ impl EventsLoop {
|
|||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
let xlib = &self.display.xlib;
|
||||
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
loop {
|
||||
// Get next event
|
||||
unsafe {
|
||||
// Ensure XNextEvent won't block
|
||||
let count = (xlib.XPending)(self.display.display);
|
||||
let count = (self.display.xlib.XPending)(self.display.display);
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
(xlib.XNextEvent)(self.display.display, &mut xev);
|
||||
(self.display.xlib.XNextEvent)(self.display.display, &mut xev);
|
||||
}
|
||||
self.process_event(&mut xev, &mut callback);
|
||||
}
|
||||
|
@ -161,12 +168,10 @@ impl EventsLoop {
|
|||
{
|
||||
self.pending_wakeup.store(false, atomic::Ordering::Relaxed);
|
||||
|
||||
let xlib = &self.display.xlib;
|
||||
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
|
||||
loop {
|
||||
unsafe { (xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary
|
||||
unsafe { (self.display.xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary
|
||||
|
||||
let mut control_flow = ControlFlow::Continue;
|
||||
|
||||
|
@ -187,7 +192,7 @@ impl EventsLoop {
|
|||
}
|
||||
}
|
||||
|
||||
fn process_event<F>(&self, xev: &mut ffi::XEvent, mut callback: F)
|
||||
fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
let xlib = &self.display.xlib;
|
||||
|
@ -210,11 +215,123 @@ impl EventsLoop {
|
|||
|
||||
if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window {
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Closed })
|
||||
} else {
|
||||
if self.pending_wakeup.load(atomic::Ordering::Relaxed) {
|
||||
self.pending_wakeup.store(false, atomic::Ordering::Relaxed);
|
||||
callback(Event::Awakened);
|
||||
} else if client_msg.message_type == self.dnd.atoms.enter {
|
||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
||||
let flags = client_msg.data.get_long(1);
|
||||
let version = flags >> 24;
|
||||
self.dnd.version = Some(version);
|
||||
let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1;
|
||||
if !has_more_types {
|
||||
let type_list = vec![
|
||||
client_msg.data.get_long(2) as c_ulong,
|
||||
client_msg.data.get_long(3) as c_ulong,
|
||||
client_msg.data.get_long(4) as c_ulong
|
||||
];
|
||||
self.dnd.type_list = Some(type_list);
|
||||
} else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) } {
|
||||
self.dnd.type_list = Some(more_types);
|
||||
}
|
||||
} else if client_msg.message_type == self.dnd.atoms.position {
|
||||
// This event occurs every time the mouse moves while a file's being dragged
|
||||
// over our window. We emit HoveredFile in response; while the Mac OS X backend
|
||||
// does that upon a drag entering, XDnD doesn't have access to the actual drop
|
||||
// data until this event. For parity with other platforms, we only emit
|
||||
// HoveredFile the first time, though if winit's API is later extended to
|
||||
// supply position updates with HoveredFile or another event, implementing
|
||||
// that here would be trivial.
|
||||
|
||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
||||
|
||||
// Equivalent to (x << shift) | y
|
||||
// where shift = mem::size_of::<c_short>() * 8
|
||||
// Note that coordinates are in "desktop space", not "window space"
|
||||
// (in x11 parlance, they're root window coordinates)
|
||||
//let packed_coordinates = client_msg.data.get_long(2);
|
||||
//let shift = mem::size_of::<libc::c_short>() * 8;
|
||||
//let x = packed_coordinates >> shift;
|
||||
//let y = packed_coordinates & !(x << shift);
|
||||
|
||||
// By our own state flow, version should never be None at this point.
|
||||
let version = self.dnd.version.unwrap_or(5);
|
||||
|
||||
// Action is specified in versions 2 and up, though we don't need it anyway.
|
||||
//let action = client_msg.data.get_long(4);
|
||||
|
||||
let accepted = if let Some(ref type_list) = self.dnd.type_list {
|
||||
type_list.contains(&self.dnd.atoms.uri_list)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if accepted {
|
||||
self.dnd.source_window = Some(source_window);
|
||||
unsafe {
|
||||
if self.dnd.result.is_none() {
|
||||
let time = if version >= 1 {
|
||||
client_msg.data.get_long(3) as c_ulong
|
||||
} else {
|
||||
// In version 0, time isn't specified
|
||||
ffi::CurrentTime
|
||||
};
|
||||
// This results in the SelectionNotify event below
|
||||
self.dnd.convert_selection(xwindow, time);
|
||||
}
|
||||
self.dnd.send_status(xwindow, source_window, DndState::Accepted);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
self.dnd.send_status(xwindow, source_window, DndState::Rejected);
|
||||
self.dnd.send_finished(xwindow, source_window, DndState::Rejected);
|
||||
}
|
||||
self.dnd.reset();
|
||||
}
|
||||
} else if client_msg.message_type == self.dnd.atoms.drop {
|
||||
if let Some(source_window) = self.dnd.source_window {
|
||||
if let Some(Ok(ref path_list)) = self.dnd.result {
|
||||
for path in path_list {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: wid,
|
||||
event: WindowEvent::DroppedFile(path.clone()),
|
||||
});
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
self.dnd.send_finished(xwindow, source_window, DndState::Accepted);
|
||||
}
|
||||
}
|
||||
self.dnd.reset();
|
||||
} else if client_msg.message_type == self.dnd.atoms.leave {
|
||||
self.dnd.reset();
|
||||
callback(Event::WindowEvent {
|
||||
window_id: wid,
|
||||
event: WindowEvent::HoveredFileCancelled,
|
||||
});
|
||||
} else if self.pending_wakeup.load(atomic::Ordering::Relaxed) {
|
||||
self.pending_wakeup.store(false, atomic::Ordering::Relaxed);
|
||||
callback(Event::Awakened);
|
||||
}
|
||||
}
|
||||
|
||||
ffi::SelectionNotify => {
|
||||
let xsel: &ffi::XSelectionEvent = xev.as_ref();
|
||||
if xsel.property == self.dnd.atoms.selection {
|
||||
let mut result = None;
|
||||
|
||||
// This is where we receive data from drag and drop
|
||||
if let Ok(mut data) = unsafe { self.dnd.read_data(xwindow) } {
|
||||
let parse_result = self.dnd.parse_data(&mut data);
|
||||
if let Ok(ref path_list) = parse_result {
|
||||
for path in path_list {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: wid,
|
||||
event: WindowEvent::HoveredFile(path.clone()),
|
||||
});
|
||||
}
|
||||
}
|
||||
result = Some(parse_result);
|
||||
}
|
||||
|
||||
self.dnd.result = result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_short, c_uchar, c_ulong};
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
|
||||
pub unsafe fn send_client_msg(
|
||||
xconn: &Arc<XConnection>,
|
||||
target_window: c_ulong,
|
||||
message_type: ffi::Atom,
|
||||
data: (c_long, c_long, c_long, c_long, c_long),
|
||||
) {
|
||||
let mut event: ffi::XClientMessageEvent = mem::uninitialized();
|
||||
event.type_ = ffi::ClientMessage;
|
||||
event.display = xconn.display;
|
||||
event.window = target_window;
|
||||
event.message_type = message_type;
|
||||
event.format = 32;
|
||||
event.data = ffi::ClientMessageData::new();
|
||||
event.data.set_long(0, data.0);
|
||||
event.data.set_long(1, data.1);
|
||||
event.data.set_long(2, data.2);
|
||||
event.data.set_long(3, data.3);
|
||||
event.data.set_long(4, data.4);
|
||||
|
||||
(xconn.xlib.XSendEvent)(
|
||||
xconn.display,
|
||||
target_window,
|
||||
ffi::False,
|
||||
ffi::NoEventMask,
|
||||
&mut event.into(),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GetPropertyError {
|
||||
XError(XError),
|
||||
TypeMismatch(ffi::Atom),
|
||||
FormatMismatch(c_int),
|
||||
NothingAllocated,
|
||||
}
|
||||
|
||||
pub unsafe fn get_property<T>(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
) -> Result<Vec<T>, GetPropertyError> {
|
||||
let mut data = Vec::new();
|
||||
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||
let mut actual_format: c_int = mem::uninitialized();
|
||||
let mut byte_count: c_ulong = mem::uninitialized();
|
||||
let mut bytes_after: c_ulong = mem::uninitialized();
|
||||
let mut buf: *mut c_uchar = ptr::null_mut();
|
||||
(xconn.xlib.XGetWindowProperty)(
|
||||
xconn.display,
|
||||
window,
|
||||
property,
|
||||
(data.len() / 4) as c_long,
|
||||
1024,
|
||||
ffi::False,
|
||||
property_type,
|
||||
&mut actual_type,
|
||||
&mut actual_format,
|
||||
&mut byte_count,
|
||||
&mut bytes_after,
|
||||
&mut buf,
|
||||
);
|
||||
|
||||
if let Err(e) = xconn.check_errors() {
|
||||
return Err(GetPropertyError::XError(e));
|
||||
}
|
||||
|
||||
if actual_type != property_type {
|
||||
return Err(GetPropertyError::TypeMismatch(actual_type));
|
||||
}
|
||||
|
||||
// Fun fact: actual_format ISN'T the size of the type; it's more like a really bad enum
|
||||
let format_mismatch = match actual_format as usize {
|
||||
8 => mem::size_of::<T>() != mem::size_of::<c_char>(),
|
||||
16 => mem::size_of::<T>() != mem::size_of::<c_short>(),
|
||||
32 => mem::size_of::<T>() != mem::size_of::<c_long>(),
|
||||
_ => true, // this won't actually be reached; the XError condition above is triggered
|
||||
};
|
||||
|
||||
if format_mismatch {
|
||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||
}
|
||||
|
||||
if !buf.is_null() {
|
||||
let mut buf =
|
||||
Vec::from_raw_parts(buf as *mut T, byte_count as usize, byte_count as usize);
|
||||
data.append(&mut buf);
|
||||
} else {
|
||||
return Err(GetPropertyError::NothingAllocated);
|
||||
}
|
||||
|
||||
done = bytes_after == 0;
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
|
@ -125,6 +125,24 @@ impl Window2 {
|
|||
win
|
||||
};
|
||||
|
||||
// Enable drag and drop
|
||||
unsafe {
|
||||
let atom_name: *const libc::c_char = b"XdndAware\0".as_ptr() as _;
|
||||
let atom = (display.xlib.XInternAtom)(display.display, atom_name, ffi::False);
|
||||
let version = &5; // Latest version; hasn't changed since 2002
|
||||
(display.xlib.XChangeProperty)(
|
||||
display.display,
|
||||
window,
|
||||
atom,
|
||||
ffi::XA_ATOM,
|
||||
32,
|
||||
ffi::PropModeReplace,
|
||||
version,
|
||||
1
|
||||
);
|
||||
display.check_errors().expect("Failed to set drag and drop properties");
|
||||
}
|
||||
|
||||
// Set ICCCM WM_CLASS property based on initial window title
|
||||
// Must be done *before* mapping the window by ICCCM 4.1.2.5
|
||||
unsafe {
|
||||
|
|
Loading…
Reference in New Issue