makepad_platform/os/linux/x11/
xlib_window.rs

1use {
2    std::{
3        mem,
4        cell::Cell,
5        rc::Rc,
6        os::raw::{c_ulong, c_long, c_void, c_char},
7        ptr,
8        ffi::{CStr,CString}, 
9    },
10    self::super::{
11        x11_sys,
12        xlib_event::XlibEvent,
13        xlib_app::*,
14    },
15    crate::{
16        area::Area,
17        window::WindowId,
18        makepad_math::{DVec2},
19        event::*,
20        cursor::MouseCursor,
21    },
22};
23
24#[derive(Clone)]
25pub struct XlibWindow {
26    pub window: Option<c_ulong>,
27    pub xic: Option<x11_sys::XIC>,
28    pub attributes: Option<x11_sys::XSetWindowAttributes>,
29    pub visual_info: Option<x11_sys::XVisualInfo>,
30    //pub child_windows: Vec<XlibChildWindow>,
31    
32    pub last_nc_mode: Option<c_long>,
33    pub window_id: WindowId,
34    pub last_window_geom: WindowGeom,
35    
36    pub ime_spot: DVec2,
37    pub current_cursor: MouseCursor,
38    pub last_mouse_pos: DVec2,
39}
40/*
41#[derive(Clone)]
42pub struct XlibChildWindow {
43    pub window: c_ulong,
44    visible: bool,
45    x: i32,
46    y: i32,
47    w: u32,
48    h: u32
49}*/
50
51impl XlibWindow {
52    
53    pub fn new(window_id: WindowId) -> XlibWindow {
54        
55        XlibWindow {
56            window: None,
57            xic: None,
58            attributes: None,
59            visual_info: None,
60            //child_windows: Vec::new(),
61            window_id,
62            last_window_geom: WindowGeom::default(),
63            last_nc_mode: None,
64            ime_spot: DVec2::default(),
65            current_cursor: MouseCursor::Default,
66            last_mouse_pos: DVec2::default(),
67        }
68    }
69    
70    pub fn init(&mut self, title: &str, size: DVec2, position: Option<DVec2>, visual_info: x11_sys::XVisualInfo, custom_window_chrome: bool) {
71        unsafe {
72            let display = get_xlib_app_global().display;
73            
74            // The default screen of the display
75            let default_screen = x11_sys::XDefaultScreen(display);
76            
77            // The root window of the default screen
78            let root_window = x11_sys::XRootWindow(display, default_screen);
79            
80            let mut attributes = mem::zeroed::<x11_sys::XSetWindowAttributes>();
81            
82            attributes.border_pixel = 0;
83            //attributes.override_redirect = 1;
84            attributes.colormap =
85            x11_sys::XCreateColormap(display, root_window, visual_info.visual, x11_sys::AllocNone as i32);
86            attributes.event_mask = (
87                x11_sys::ExposureMask
88                    | x11_sys::StructureNotifyMask
89                    | x11_sys::ButtonMotionMask
90                    | x11_sys::PointerMotionMask
91                    | x11_sys::ButtonPressMask
92                    | x11_sys::ButtonReleaseMask
93                    | x11_sys::KeyPressMask
94                    | x11_sys::KeyReleaseMask
95                    | x11_sys::VisibilityChangeMask
96                    | x11_sys::FocusChangeMask
97                    | x11_sys::EnterWindowMask
98                    | x11_sys::LeaveWindowMask
99            ) as c_long;
100            
101            let dpi_factor = self.get_dpi_factor();
102            // Create a window
103            let window = x11_sys::XCreateWindow(
104                display,
105                root_window,
106                if position.is_some() {position.unwrap().x}else {150.0} as i32,
107                if position.is_some() {position.unwrap().y}else {60.0} as i32,
108                (size.x * dpi_factor) as u32,
109                (size.y * dpi_factor) as u32,
110                0,
111                visual_info.depth,
112                x11_sys::InputOutput as u32,
113                visual_info.visual,
114                (x11_sys::CWBorderPixel | x11_sys::CWColormap | x11_sys::CWEventMask) as c_ulong, // | X11_sys::CWOverrideRedirect,
115                &mut attributes,
116            );
117            
118            // Tell the window manager that we want to be notified when the window is closed
119            x11_sys::XSetWMProtocols(display, window, &mut get_xlib_app_global().atoms.wm_delete_window, 1);
120            
121            if custom_window_chrome {
122                let hints = MwmHints {
123                    flags: MWM_HINTS_DECORATIONS,
124                    functions: 0,
125                    decorations: 0,
126                    input_mode: 0,
127                    status: 0,
128                };
129                
130                let atom_motif_wm_hints = get_xlib_app_global().atoms.motif_wm_hints;
131                
132                x11_sys::XChangeProperty(
133                    display,
134                    window,
135                    atom_motif_wm_hints,
136                    atom_motif_wm_hints,
137                    32,
138                    x11_sys::PropModeReplace as i32,
139                    &hints as *const _ as *const u8,
140                    5
141                );
142            }
143            
144            get_xlib_app_global().dnd.enable_for_window(window);
145            
146            // Map the window to the screen
147            x11_sys::XMapWindow(display, window);
148            x11_sys::XFlush(display);
149            
150            let title_bytes = format!("{}\0", title);
151            x11_sys::XStoreName(display, window, title_bytes.as_bytes().as_ptr() as *const c_char);
152            
153            let xic = x11_sys::XCreateIC(
154                get_xlib_app_global().xim,
155                x11_sys::XNInputStyle.as_ptr(),
156                (x11_sys::XIMPreeditNothing | x11_sys::XIMStatusNothing) as i32,
157                x11_sys::XNClientWindow.as_ptr(),
158                window,
159                x11_sys::XNFocusWindow.as_ptr(),
160                window,
161                ptr::null_mut() as *mut c_void
162            );
163            
164            // Create a window
165            get_xlib_app_global().window_map.insert(window, self);
166            
167            self.attributes = Some(attributes);
168            self.visual_info = Some(visual_info);
169            self.window = Some(window);
170            self.xic = Some(xic);
171            self.last_window_geom = self.get_window_geom();
172            
173            let new_geom = self.get_window_geom();
174            self.do_callback(XlibEvent::WindowGeomChange(WindowGeomChangeEvent {
175                    window_id: self.window_id,
176                    old_geom: new_geom.clone(),
177                    new_geom: new_geom
178                })
179            );
180        }
181    }
182    
183    fn restore_or_maximize(&self, add_remove: c_long) {
184        unsafe {
185            let default_screen = x11_sys::XDefaultScreen(get_xlib_app_global().display);
186            let root_window = x11_sys::XRootWindow(get_xlib_app_global().display, default_screen);
187            let mut xclient = x11_sys::XClientMessageEvent {
188                type_: x11_sys::ClientMessage as i32,
189                serial: 0,
190                send_event: 0,
191                display: get_xlib_app_global().display,
192                window: self.window.unwrap(),
193                message_type: get_xlib_app_global().atoms.net_wm_state,
194                format: 32,
195                data: {
196                    let mut msg = mem::zeroed::<x11_sys::XClientMessageEvent__bindgen_ty_1>();
197                    msg.l[0] = add_remove;
198                    msg.l[1] = get_xlib_app_global().atoms.new_wm_state_maximized_horz as c_long;
199                    msg.l[2] = get_xlib_app_global().atoms.new_wm_state_maximized_vert as c_long;
200                    msg
201                }
202            };
203            x11_sys::XSendEvent(
204                get_xlib_app_global().display,
205                root_window,
206                0,
207                (x11_sys::SubstructureNotifyMask | x11_sys::SubstructureRedirectMask) as c_long,
208                &mut xclient as *mut _ as *mut x11_sys::XEvent
209            );
210        }
211    }
212    
213    pub fn restore(&self) {
214        self.restore_or_maximize(_NET_WM_STATE_REMOVE);
215    }
216    
217    pub fn maximize(&self) {
218        self.restore_or_maximize(_NET_WM_STATE_ADD);
219    }
220    
221    pub fn close_window(&mut self) {
222        unsafe {
223            x11_sys::XDestroyWindow(get_xlib_app_global().display, self.window.unwrap());
224            self.window = None;
225            // lets remove us from the mapping
226            
227        }
228    }
229    
230    pub fn minimize(&self) {
231        unsafe {
232            let default_screen = x11_sys::XDefaultScreen(get_xlib_app_global().display);
233            x11_sys::XIconifyWindow(get_xlib_app_global().display, self.window.unwrap(), default_screen);
234            x11_sys::XFlush(get_xlib_app_global().display);
235        }
236    }
237    
238    pub fn set_topmost(&self, _topmost: bool) {
239    }
240    
241    pub fn get_is_topmost(&self) -> bool {
242        false
243    }
244    
245    pub fn get_window_geom(&self) -> WindowGeom {
246        WindowGeom {
247            xr_is_presenting: false,
248            can_fullscreen: false,
249            is_topmost: self.get_is_topmost(),
250            is_fullscreen: self.get_is_maximized(),
251            inner_size: self.get_inner_size(),
252            outer_size: self.get_outer_size(),
253            dpi_factor: self.get_dpi_factor(),
254            position: self.get_position()
255        }
256    }
257    
258    pub fn get_is_maximized(&self) -> bool {
259        let mut maximized = false;
260        unsafe {
261            let mut prop_type = mem::MaybeUninit::uninit();
262            let mut format = mem::MaybeUninit::uninit();
263            let mut n_item = mem::MaybeUninit::uninit();
264            let mut bytes_after = mem::MaybeUninit::uninit();
265            let mut properties = mem::MaybeUninit::uninit();
266            let result = x11_sys::XGetWindowProperty(
267                get_xlib_app_global().display,
268                self.window.unwrap(),
269                get_xlib_app_global().atoms.net_wm_state,
270                0,
271                !0,
272                0,
273                x11_sys::AnyPropertyType as c_ulong,
274                prop_type.as_mut_ptr(),
275                format.as_mut_ptr(),
276                n_item.as_mut_ptr(),
277                bytes_after.as_mut_ptr(),
278                properties.as_mut_ptr()
279            );
280            //let prop_type = prop_type.assume_init();
281            //let format = format.assume_init();
282            let n_item = n_item.assume_init();
283            //let bytes_after = bytes_after.assume_init();
284            let properties = properties.assume_init();
285            if result == 0 && properties != ptr::null_mut() {
286                let items = std::slice::from_raw_parts::<c_ulong>(properties as *mut _, n_item as usize);
287                for item in items {
288                    if *item == get_xlib_app_global().atoms.new_wm_state_maximized_horz
289                        || *item == get_xlib_app_global().atoms.new_wm_state_maximized_vert {
290                        maximized = true;
291                        break;
292                    }
293                }
294                x11_sys::XFree(properties as *mut _);
295            }
296        }
297        maximized
298    }
299    
300    pub fn set_ime_spot(&mut self, spot: DVec2) {
301        self.ime_spot = spot;
302    }
303    
304    pub fn get_position(&self) -> DVec2 {
305        unsafe {
306            let mut xwa = mem::MaybeUninit::uninit();
307            let display = get_xlib_app_global().display;
308            x11_sys::XGetWindowAttributes(
309                display,
310                self.window.unwrap(),
311                xwa.as_mut_ptr()
312            );
313            let xwa = xwa.assume_init();
314            return DVec2 {x: xwa.x as f64, y: xwa.y as f64}
315            /*
316            let mut child = mem::uninitialized();
317            let default_screen = X11_sys::XDefaultScreen(display);
318            let root_window = X11_sys::XRootWindow(display, default_screen);
319            let mut x:c_int = 0;
320            let mut y:c_int = 0;
321            X11_sys::XTranslateCoordinates(display, self.window.unwrap(), root_window, 0, 0, &mut x, &mut y, &mut child );
322            */
323        }
324    }
325    
326    pub fn get_inner_size(&self) -> DVec2 {
327        let dpi_factor = self.get_dpi_factor();
328        unsafe {
329            let mut xwa = mem::MaybeUninit::uninit();
330            let display = get_xlib_app_global().display;
331            x11_sys::XGetWindowAttributes(display, self.window.unwrap(), xwa.as_mut_ptr());
332            let xwa = xwa.assume_init();
333            return DVec2 {x: xwa.width as f64 / dpi_factor, y: xwa.height as f64 / dpi_factor}
334        }
335    }
336    
337    pub fn get_outer_size(&self) -> DVec2 {
338        unsafe {
339            let mut xwa = mem::MaybeUninit::uninit();
340            let display = get_xlib_app_global().display;
341            x11_sys::XGetWindowAttributes(display, self.window.unwrap(), xwa.as_mut_ptr());
342            let xwa = xwa.assume_init();
343            return DVec2 {x: xwa.width as f64, y: xwa.height as f64}
344        }
345    }
346    
347    pub fn set_position(&mut self, _pos: DVec2) {
348    }
349    
350    pub fn set_outer_size(&self, _size: DVec2) {
351    }
352    
353    pub fn set_inner_size(&self, _size: DVec2) {
354    }
355    
356    pub fn get_dpi_factor(&self) -> f64 {
357        unsafe {
358            //return 2.0;
359            let display = get_xlib_app_global().display;
360            let resource_string = x11_sys::XResourceManagerString(display);
361            if resource_string == std::ptr::null_mut() {
362                return 1.0
363            }
364            let db = x11_sys::XrmGetStringDatabase(resource_string);
365            let mut ty = mem::MaybeUninit::uninit();
366            let mut value = mem::MaybeUninit::uninit();
367            x11_sys::XrmGetResource(
368                db,
369                "Xft.dpi\0".as_ptr() as * const _,
370                "String\0".as_ptr() as * const _,
371                ty.as_mut_ptr(),
372                value.as_mut_ptr()
373            );
374            //let ty = ty.assume_init();
375            let value = value.assume_init();
376            if value.addr == std::ptr::null_mut() {
377                return 1.0; // TODO find some other way to figure it out
378            }
379            else {
380                let dpi: f64 = CStr::from_ptr(value.addr).to_str().unwrap().parse().unwrap();
381                return dpi / 96.0;
382            }
383        }
384    }
385    
386    pub fn time_now(&self) -> f64 {
387         get_xlib_app_global().time_now()
388    }
389
390    pub fn do_callback(&mut self, event: XlibEvent) {
391        get_xlib_app_global().do_callback(event);
392    }
393    
394    pub fn send_change_event(&mut self) {
395        
396        let mut new_geom = self.get_window_geom();
397        if new_geom.inner_size.x < self.last_window_geom.inner_size.x ||
398        new_geom.inner_size.y < self.last_window_geom.inner_size.y {
399            new_geom.is_fullscreen = false;
400        }
401        let old_geom = self.last_window_geom.clone();
402        self.last_window_geom = new_geom.clone();
403        
404        self.do_callback(XlibEvent::WindowGeomChange(WindowGeomChangeEvent {
405            window_id: self.window_id,
406            old_geom: old_geom,
407            new_geom: new_geom
408        }));
409        self.do_callback(XlibEvent::Paint);
410    }
411    
412    pub fn send_focus_event(&mut self) {
413        self.do_callback(XlibEvent::AppGotFocus);
414    }
415    
416    pub fn send_focus_lost_event(&mut self) {
417        self.do_callback(XlibEvent::AppLostFocus);
418    }
419    
420    pub fn send_mouse_down(&mut self, button: usize, modifiers: KeyModifiers) {
421        self.do_callback(XlibEvent::MouseDown(MouseDownEvent {
422            button,
423            modifiers,
424            window_id: self.window_id,
425            abs: self.last_mouse_pos,
426            time: self.time_now(),
427            handled: Cell::new(Area::Empty),
428        }));
429    }
430    
431    pub fn send_mouse_up(&mut self, button: usize, modifiers: KeyModifiers) {
432        self.do_callback(XlibEvent::MouseUp(MouseUpEvent {
433            button,
434            modifiers,
435            window_id: self.window_id,
436            abs: self.last_mouse_pos,
437            time: self.time_now()
438        }));
439    }
440    
441    pub fn send_mouse_move(&mut self, pos: DVec2, modifiers: KeyModifiers) {
442        self.last_mouse_pos = pos;
443        self.do_callback(XlibEvent::MouseMove(MouseMoveEvent {
444            window_id: self.window_id,
445            abs: pos,
446            modifiers: modifiers,
447            time: self.time_now(),
448            handled: Cell::new(Area::Empty),
449        }));
450        
451    }
452    
453    pub fn send_close_requested_event(&mut self) -> bool {
454        let accept_close = Rc::new(Cell::new(true));
455        self.do_callback(XlibEvent::WindowCloseRequested(WindowCloseRequestedEvent {
456            window_id: self.window_id,
457            accept_close: accept_close.clone()
458        }));
459        if !accept_close.get() {
460            return false
461        }
462        true
463    }
464    
465    pub fn send_text_input(&mut self, input: String, replace_last: bool) {
466        self.do_callback(XlibEvent::TextInput(TextInputEvent {
467            input: input,
468            was_paste: false,
469            replace_last: replace_last
470        }))
471    }
472    
473}
474
475
476#[derive(Clone, Copy, PartialEq)]
477#[repr(C)]
478struct MwmHints {
479    pub flags: c_ulong,
480    pub functions: c_ulong,
481    pub decorations: c_ulong,
482    pub input_mode: c_long,
483    pub status: c_ulong,
484}
485
486pub const MWM_HINTS_FUNCTIONS: c_ulong = 1 << 0;
487pub const MWM_HINTS_DECORATIONS: c_ulong = 1 << 1;
488
489pub const MWM_FUNC_ALL: c_ulong = 1 << 0;
490pub const MWM_FUNC_RESIZE: c_ulong = 1 << 1;
491pub const MWM_FUNC_MOVE: c_ulong = 1 << 2;
492pub const MWM_FUNC_MINIMIZE: c_ulong = 1 << 3;
493pub const MWM_FUNC_MAXIMIZE: c_ulong = 1 << 4;
494pub const MWM_FUNC_CLOSE: c_ulong = 1 << 5;
495pub const _NET_WM_MOVERESIZE_SIZE_TOPLEFT: c_long = 0;
496pub const _NET_WM_MOVERESIZE_SIZE_TOP: c_long = 1;
497pub const _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: c_long = 2;
498pub const _NET_WM_MOVERESIZE_SIZE_RIGHT: c_long = 3;
499pub const _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: c_long = 4;
500pub const _NET_WM_MOVERESIZE_SIZE_BOTTOM: c_long = 5;
501pub const _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: c_long = 6;
502pub const _NET_WM_MOVERESIZE_SIZE_LEFT: c_long = 7;
503pub const _NET_WM_MOVERESIZE_MOVE: c_long = 8;/* movement only */
504pub const _NET_WM_MOVERESIZE_SIZE_KEYBOARD: c_long = 9;/* size via keyboard */
505pub const _NET_WM_MOVERESIZE_MOVE_KEYBOARD: c_long = 10;
506
507pub const _NET_WM_STATE_REMOVE: c_long = 0;/* remove/unset property */
508pub const _NET_WM_STATE_ADD: c_long = 1;/* add/set property */
509pub const _NET_WM_STATE_TOGGLE: c_long = 2;/* toggle property  */
510
511/* move via keyboard */
512
513pub struct Dnd {
514    pub atoms: DndAtoms,
515    pub display: *mut x11_sys::Display,
516    pub type_list: Option<Vec<x11_sys::Atom >>,
517    pub selection: Option<CString>,
518}
519
520impl Dnd {
521    pub unsafe fn new(display: *mut x11_sys::Display) -> Dnd {
522        Dnd {
523            atoms: DndAtoms::new(display),
524            display,
525            type_list: None,
526            selection: None,
527        }
528    }
529    
530    /// Enables drag-and-drop for the given window.
531    pub unsafe fn enable_for_window(&mut self, window: x11_sys::Window) {
532        // To enable drag-and-drop for a window, we need to set the XDndAware property of the window
533        // to the version of XDnd we support.
534        
535        // I took this value from the Winit source code. Apparently, this is the latest version, and
536        // hasn't changed since 2002.
537        let version = 5 as c_ulong;
538        
539        x11_sys::XChangeProperty(
540            self.display,
541            window,
542            self.atoms.aware,
543            4, // XA_ATOM
544            32,
545            x11_sys::PropModeReplace as std::os::raw::c_int,
546            &version as *const c_ulong as *const std::os::raw::c_uchar,
547            1
548        );
549    }
550    
551    /// Handles a XDndEnter event.
552    pub unsafe fn handle_enter_event(&mut self, event: &x11_sys::XClientMessageEvent) {
553        // The XDndEnter event is sent by the source window when a drag begins. That is, the mouse
554        // enters the client rectangle of the target window. The target window is supposed to
555        // respond to this by requesting the list of types supported by the source.
556        
557        let source_window = event.data.l[0] as x11_sys::Window;
558        let has_more_types = event.data.l[1] & (1 << 0) != 0;
559        
560        // If the has_more_types flags is set, we have to obtain the list of supported types from
561        // the XDndTypeList property. Otherwise, we can obtain the list of supported types from the
562        // event itself.
563        self.type_list = Some(if has_more_types {
564            self.get_type_list_property(source_window)
565        } else {
566            event.data.l[2..4]
567                .iter()
568                .map( | &l | l as x11_sys::Atom)
569                .filter( | &atom | atom != x11_sys::None as x11_sys::Atom)
570                .collect()
571        });
572    }
573    
574    /// Handles a XDndDrop event.
575    pub unsafe fn handle_drop_event(&mut self, event: &x11_sys::XClientMessageEvent) {
576        // The XDndLeave event is sent by the source window when a drag is confirmed. That is, the
577        // mouse button is released while the mouse is inside the client rectangle of the target
578        // window. The target window is supposed to respond to this by requesting that the selection
579        // representing the thing being dragged is converted to the appropriate data type (in our
580        // case, a URI list). The source window, in turn, is supposed to respond this by sending a
581        // selection event containing the data to the source window.
582        
583        let target_window = event.window as x11_sys::Window;
584        self.convert_selection(target_window);
585        self.type_list = None;
586    }
587    
588    /// Handles a XDndLeave event.
589    pub unsafe fn handle_leave_event(&mut self, _event: &x11_sys::XClientMessageEvent) {
590        // The XDndLeave event is sent by the source window when a drag is canceled. That is, the
591        // mouse leaves the client rectangle of the target window. The target window is supposed to
592        // repsond this this by pretending the drag never happened.
593        
594        self.type_list = None;
595    }
596    
597    /// Handles a XDndPosition event.
598    pub unsafe fn handle_position_event(&mut self, event: &x11_sys::XClientMessageEvent) {
599        // The XDndPosition event is sent by the source window after the XDndEnter event, every time
600        // the mouse is moved. The target window is supposed to respond to this by sending a status
601        // event to the source window notifying whether it can accept the drag at this position.
602        
603        let target_window = event.window as x11_sys::Window;
604        let source_window = event.data.l[0] as x11_sys::Window;
605        
606        // For now we accept te drag if and only if the list of types supported by the source
607        // includes a uri list.
608        //
609        // TODO: Extend this test by taking into account the position of the mouse as well.
610        let accepted = self.type_list.as_ref().map_or(false, | type_list | type_list.contains(&self.atoms.uri_list));
611        
612        // Notify the source window whether we can accept the drag at this position.
613        self.send_status_event(source_window, target_window, accepted);
614        
615        // If this is the first time we've accepted the drag, request that the drag-and-drop
616        // selection be converted to a URI list. The target window is supposed to respond to this by
617        // sending a XSelectionEvent containing the URI list.
618        
619        // Since this is an asynchronous operation, its possible for another XDndPosition event to
620        // come in before the response to the first conversion request has been received. In this
621        // case, a second conversion request will be sent, the response to which will be ignored.
622        if accepted && self.selection.is_none() {
623        }
624    }
625    
626    /// Handles a XSelectionEvent.
627    pub unsafe fn handle_selection_event(&mut self, _event: &x11_sys::XSelectionEvent) {
628        // The XSelectionEvent is sent by the source window in response to a request by the source
629        // window to convert the selection representing the thing being dragged to the appropriate
630        // data type. This request is always sent in response to a XDndDrop event, so this event
631        // should only be received after a drop operation has completed.
632        
633        //let source_window = event.requestor;
634        //let selection = CString::new(self.get_selection_property(source_window)).unwrap();
635        
636        // TODO: Actually use the selection
637    }
638    
639    /// Gets the XDndSelection property from the source window.
640    pub unsafe fn get_selection_property(&mut self, source_window: x11_sys::Window) -> Vec< std::os::raw::c_uchar> {
641        let mut selection = Vec::new();
642        let mut offset = 0;
643        let length = 1024;
644        let mut actual_type = 0;
645        let mut actual_format = 0;
646        let mut nitems = 0;
647        let mut bytes_after = 0;
648        let mut prop = ptr::null_mut();
649        loop {
650            x11_sys::XGetWindowProperty(
651                self.display,
652                source_window,
653                self.atoms.selection,
654                offset,
655                length,
656                x11_sys::False as std::os::raw::c_int,
657                self.atoms.uri_list,
658                &mut actual_type,
659                &mut actual_format,
660                &mut nitems,
661                &mut bytes_after,
662                &mut prop,
663            );
664            selection.extend_from_slice(std::slice::from_raw_parts(prop as *mut  std::os::raw::c_uchar, nitems as usize));
665            x11_sys::XFree(prop as *mut c_void);
666            if bytes_after == 0 {
667                break;
668            }
669            offset += length;
670        };
671        selection
672    }
673    
674    /// Gets the XDndTypeList property from the source window.
675    pub unsafe fn get_type_list_property(&mut self, source_window: x11_sys::Window) -> Vec<x11_sys::Atom> {
676        let mut type_list = Vec::new();
677        let mut offset = 0;
678        let length = 1024;
679        let mut actual_type = 0;
680        let mut actual_format = 0;
681        let mut nitems = 0;
682        let mut bytes_after = 0;
683        let mut prop = ptr::null_mut();
684        loop {
685            x11_sys::XGetWindowProperty(
686                self.display,
687                source_window,
688                self.atoms.type_list,
689                offset,
690                length,
691                x11_sys::False as  std::os::raw::c_int,
692                4, // XA_ATOM,
693                &mut actual_type,
694                &mut actual_format,
695                &mut nitems,
696                &mut bytes_after,
697                &mut prop,
698            );
699            type_list.extend_from_slice(std::slice::from_raw_parts(prop as *mut x11_sys::Atom, nitems as usize));
700            x11_sys::XFree(prop as *mut c_void);
701            if bytes_after == 0 {
702                break;
703            }
704            offset += length;
705        };
706        type_list
707    }
708    
709    /// Sends a XDndStatus event to the target window.
710    pub unsafe fn send_status_event(&mut self, source_window: x11_sys::Window, target_window: x11_sys::Window, accepted: bool) {
711        x11_sys::XSendEvent(
712            self.display,
713            source_window,
714            x11_sys::False as  std::os::raw::c_int,
715            x11_sys::NoEventMask as  std::os::raw::c_long,
716            &mut x11_sys::XClientMessageEvent {
717                type_: x11_sys::ClientMessage as  std::os::raw::c_int,
718                serial: 0,
719                send_event: 0,
720                display: self.display,
721                window: source_window,
722                message_type: self.atoms.status,
723                format: 32,
724                data: {
725                    let mut data = mem::zeroed::<x11_sys::XClientMessageEvent__bindgen_ty_1>();
726                    data.l[0] = target_window as c_long;
727                    data.l[1] = if accepted {1 << 0} else {0};
728                    data.l[2] = 0;
729                    data.l[3] = 0;
730                    data.l[4] = if accepted {self.atoms.action_private} else {self.atoms.none} as c_long;
731                    data
732                }
733            } as *mut x11_sys::XClientMessageEvent as *mut x11_sys::XEvent
734        );
735        x11_sys::XFlush(self.display);
736    }
737    
738    // Requests that the selection representing the thing being dragged is converted to the
739    // appropriate data type (in our case, a URI list).
740    pub unsafe fn convert_selection(&self, target_window: x11_sys::Window) {
741        x11_sys::XConvertSelection(
742            self.display,
743            self.atoms.selection,
744            self.atoms.uri_list,
745            self.atoms.selection,
746            target_window,
747            x11_sys::CurrentTime as x11_sys::Time,
748        );
749    }
750}
751
752pub struct DndAtoms {
753    pub action_private: x11_sys::Atom,
754    pub aware: x11_sys::Atom,
755    pub drop: x11_sys::Atom,
756    pub enter: x11_sys::Atom,
757    pub leave: x11_sys::Atom,
758    pub none: x11_sys::Atom,
759    pub position: x11_sys::Atom,
760    pub selection: x11_sys::Atom,
761    pub status: x11_sys::Atom,
762    pub type_list: x11_sys::Atom,
763    pub uri_list: x11_sys::Atom,
764}
765
766impl DndAtoms {
767    pub unsafe fn new(display: *mut x11_sys::Display) -> DndAtoms {
768        DndAtoms {
769            action_private: x11_sys::XInternAtom(display, "XdndActionPrivate\0".as_ptr() as *const _, 0),
770            aware: x11_sys::XInternAtom(display, "XdndAware\0".as_ptr() as *const _, 0),
771            drop: x11_sys::XInternAtom(display, "XdndDrop\0".as_ptr() as *const _, 0),
772            enter: x11_sys::XInternAtom(display, "XdndEnter\0".as_ptr() as *const _, 0),
773            leave: x11_sys::XInternAtom(display, "XdndLeave\0".as_ptr() as *const _, 0),
774            none: x11_sys::XInternAtom(display, "None\0".as_ptr() as *const _, 0),
775            position: x11_sys::XInternAtom(display, "XdndPosition\0".as_ptr() as *const _, 0),
776            selection: x11_sys::XInternAtom(display, "XdndSelection\0".as_ptr() as *const _, 0),
777            status: x11_sys::XInternAtom(display, "XdndStatus\0".as_ptr() as *const _, 0),
778            type_list: x11_sys::XInternAtom(display, "XdndTypeList\0".as_ptr() as *const _, 0),
779            uri_list: x11_sys::XInternAtom(display, "text/uri-list\0".as_ptr() as *const _, 0),
780        }
781    }
782}