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, OsStr},
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            // The title should be set prior to mapping the window.
147            let title_bytes = format!("{}\0", title);
148            x11_sys::XStoreName(display, window, title_bytes.as_bytes().as_ptr() as *const c_char);
149
150            // Set the WM_CLASS before mapping the window.
151            // Based on <https://www.x.org/releases/X11R7.5/doc/man/man3/XSetWMProperties.3.html>
152            {
153                // Use the binary name by default (the first arg).
154                let class = std::env::args_os().next().as_ref()
155                    .and_then(|arg0| std::path::Path::new(arg0).file_name())
156                    .and_then(OsStr::to_str)
157                    .map(ToOwned::to_owned)
158                    .unwrap_or_else(|| String::from("Makepad"));
159                let instance = std::env::var("RESOURCE_NAME").ok()
160                    .unwrap_or_else(|| class.clone());
161                let wm_class = format!("{instance}\0{class}\0");
162
163                x11_sys::XChangeProperty(
164                    display,
165                    window,
166                    get_xlib_app_global().atoms.wm_class,
167                    get_xlib_app_global().atoms.string,
168                    // the wm_class is passed in as a string of bytes
169                    (core::mem::size_of::<u8>() * 8) as i32,
170                    x11_sys::PropModeReplace as i32,
171                    wm_class.as_ptr(),
172                    wm_class.len() as i32
173                );
174
175            }
176
177            // Map the window to the screen
178            x11_sys::XMapWindow(display, window);
179            x11_sys::XFlush(display);
180
181            // Explicitly set the window position after mapping
182            // This is necessary because some window managers might ignore the initial position
183            // and override it with their own heuristics.
184            if let Some(pos) = position {
185                x11_sys::XMoveWindow(display, window, pos.x as i32, pos.y as i32);
186                x11_sys::XFlush(display);
187            }
188            
189            let xic = x11_sys::XCreateIC(
190                get_xlib_app_global().xim,
191                x11_sys::XNInputStyle.as_ptr(),
192                (x11_sys::XIMPreeditNothing | x11_sys::XIMStatusNothing) as i32,
193                x11_sys::XNClientWindow.as_ptr(),
194                window,
195                x11_sys::XNFocusWindow.as_ptr(),
196                window,
197                ptr::null_mut() as *mut c_void
198            );
199            
200            // Create a window
201            get_xlib_app_global().window_map.insert(window, self);
202            
203            self.attributes = Some(attributes);
204            self.visual_info = Some(visual_info);
205            self.window = Some(window);
206            self.xic = Some(xic);
207            self.last_window_geom = self.get_window_geom();
208            
209            let new_geom = self.get_window_geom();
210            self.do_callback(XlibEvent::WindowGeomChange(WindowGeomChangeEvent {
211                    window_id: self.window_id,
212                    old_geom: new_geom.clone(),
213                    new_geom: new_geom
214                })
215            );
216        }
217    }
218    
219    fn restore_or_maximize(&self, add_remove: c_long) {
220        unsafe {
221            let default_screen = x11_sys::XDefaultScreen(get_xlib_app_global().display);
222            let root_window = x11_sys::XRootWindow(get_xlib_app_global().display, default_screen);
223            let mut xclient = x11_sys::XClientMessageEvent {
224                type_: x11_sys::ClientMessage as i32,
225                serial: 0,
226                send_event: 0,
227                display: get_xlib_app_global().display,
228                window: self.window.unwrap(),
229                message_type: get_xlib_app_global().atoms.net_wm_state,
230                format: 32,
231                data: {
232                    let mut msg = mem::zeroed::<x11_sys::XClientMessageEvent__bindgen_ty_1>();
233                    msg.l[0] = add_remove;
234                    msg.l[1] = get_xlib_app_global().atoms.new_wm_state_maximized_horz as c_long;
235                    msg.l[2] = get_xlib_app_global().atoms.new_wm_state_maximized_vert as c_long;
236                    msg
237                }
238            };
239            x11_sys::XSendEvent(
240                get_xlib_app_global().display,
241                root_window,
242                0,
243                (x11_sys::SubstructureNotifyMask | x11_sys::SubstructureRedirectMask) as c_long,
244                &mut xclient as *mut _ as *mut x11_sys::XEvent
245            );
246        }
247    }
248    
249    pub fn restore(&self) {
250        self.restore_or_maximize(_NET_WM_STATE_REMOVE);
251    }
252    
253    pub fn maximize(&self) {
254        self.restore_or_maximize(_NET_WM_STATE_ADD);
255    }
256    
257    pub fn close_window(&mut self) {
258        unsafe {
259            x11_sys::XDestroyWindow(get_xlib_app_global().display, self.window.unwrap());
260            self.window = None;
261            // lets remove us from the mapping
262            
263        }
264    }
265    
266    pub fn minimize(&self) {
267        unsafe {
268            let default_screen = x11_sys::XDefaultScreen(get_xlib_app_global().display);
269            x11_sys::XIconifyWindow(get_xlib_app_global().display, self.window.unwrap(), default_screen);
270            x11_sys::XFlush(get_xlib_app_global().display);
271        }
272    }
273    
274    pub fn set_topmost(&self, _topmost: bool) {
275    }
276    
277    pub fn get_is_topmost(&self) -> bool {
278        false
279    }
280    
281    pub fn get_window_geom(&self) -> WindowGeom {
282        WindowGeom {
283            xr_is_presenting: false,
284            can_fullscreen: false,
285            is_topmost: self.get_is_topmost(),
286            is_fullscreen: self.get_is_maximized(),
287            inner_size: self.get_inner_size(),
288            outer_size: self.get_outer_size(),
289            dpi_factor: self.get_dpi_factor(),
290            position: self.get_position()
291        }
292    }
293    
294    pub fn get_is_maximized(&self) -> bool {
295        let mut maximized = false;
296        unsafe {
297            let mut prop_type = mem::MaybeUninit::uninit();
298            let mut format = mem::MaybeUninit::uninit();
299            let mut n_item = mem::MaybeUninit::uninit();
300            let mut bytes_after = mem::MaybeUninit::uninit();
301            let mut properties = mem::MaybeUninit::uninit();
302            let result = x11_sys::XGetWindowProperty(
303                get_xlib_app_global().display,
304                self.window.unwrap(),
305                get_xlib_app_global().atoms.net_wm_state,
306                0,
307                !0,
308                0,
309                x11_sys::AnyPropertyType as c_ulong,
310                prop_type.as_mut_ptr(),
311                format.as_mut_ptr(),
312                n_item.as_mut_ptr(),
313                bytes_after.as_mut_ptr(),
314                properties.as_mut_ptr()
315            );
316            //let prop_type = prop_type.assume_init();
317            //let format = format.assume_init();
318            let n_item = n_item.assume_init();
319            //let bytes_after = bytes_after.assume_init();
320            let properties = properties.assume_init();
321            if result == 0 && properties != ptr::null_mut() {
322                let items = std::slice::from_raw_parts::<c_ulong>(properties as *mut _, n_item as usize);
323                for item in items {
324                    if *item == get_xlib_app_global().atoms.new_wm_state_maximized_horz
325                        || *item == get_xlib_app_global().atoms.new_wm_state_maximized_vert {
326                        maximized = true;
327                        break;
328                    }
329                }
330                x11_sys::XFree(properties as *mut _);
331            }
332        }
333        maximized
334    }
335    
336    pub fn set_ime_spot(&mut self, spot: DVec2) {
337        self.ime_spot = spot;
338    }
339    
340    pub fn get_position(&self) -> DVec2 {
341        unsafe {
342            let mut xwa = mem::MaybeUninit::uninit();
343            let display = get_xlib_app_global().display;
344            x11_sys::XGetWindowAttributes(
345                display,
346                self.window.unwrap(),
347                xwa.as_mut_ptr()
348            );
349            let xwa = xwa.assume_init();
350            return DVec2 {x: xwa.x as f64, y: xwa.y as f64}
351            /*
352            let mut child = mem::uninitialized();
353            let default_screen = X11_sys::XDefaultScreen(display);
354            let root_window = X11_sys::XRootWindow(display, default_screen);
355            let mut x:c_int = 0;
356            let mut y:c_int = 0;
357            X11_sys::XTranslateCoordinates(display, self.window.unwrap(), root_window, 0, 0, &mut x, &mut y, &mut child );
358            */
359        }
360    }
361    
362    pub fn get_inner_size(&self) -> DVec2 {
363        let dpi_factor = self.get_dpi_factor();
364        unsafe {
365            let mut xwa = mem::MaybeUninit::uninit();
366            let display = get_xlib_app_global().display;
367            x11_sys::XGetWindowAttributes(display, self.window.unwrap(), xwa.as_mut_ptr());
368            let xwa = xwa.assume_init();
369            return DVec2 {x: xwa.width as f64 / dpi_factor, y: xwa.height as f64 / dpi_factor}
370        }
371    }
372    
373    pub fn get_outer_size(&self) -> DVec2 {
374        unsafe {
375            let mut xwa = mem::MaybeUninit::uninit();
376            let display = get_xlib_app_global().display;
377            x11_sys::XGetWindowAttributes(display, self.window.unwrap(), xwa.as_mut_ptr());
378            let xwa = xwa.assume_init();
379            return DVec2 {x: xwa.width as f64, y: xwa.height as f64}
380        }
381    }
382    
383    pub fn set_position(&mut self, _pos: DVec2) {
384    }
385    
386    pub fn set_outer_size(&self, _size: DVec2) {
387    }
388    
389    pub fn set_inner_size(&self, _size: DVec2) {
390    }
391    
392    pub fn get_dpi_factor(&self) -> f64 {
393        unsafe {
394            //return 2.0;
395            let display = get_xlib_app_global().display;
396            let resource_string = x11_sys::XResourceManagerString(display);
397            if resource_string == std::ptr::null_mut() {
398                return 1.0
399            }
400            let db = x11_sys::XrmGetStringDatabase(resource_string);
401            let mut ty = mem::MaybeUninit::uninit();
402            let mut value = mem::MaybeUninit::uninit();
403            x11_sys::XrmGetResource(
404                db,
405                "Xft.dpi\0".as_ptr() as * const _,
406                "String\0".as_ptr() as * const _,
407                ty.as_mut_ptr(),
408                value.as_mut_ptr()
409            );
410            //let ty = ty.assume_init();
411            let value = value.assume_init();
412            if value.addr == std::ptr::null_mut() {
413                return 1.0; // TODO find some other way to figure it out
414            }
415            else {
416                let dpi: f64 = CStr::from_ptr(value.addr).to_str().unwrap().parse().unwrap();
417                return dpi / 96.0;
418            }
419        }
420    }
421    
422    pub fn time_now(&self) -> f64 {
423         get_xlib_app_global().time_now()
424    }
425
426    pub fn do_callback(&mut self, event: XlibEvent) {
427        get_xlib_app_global().do_callback(event);
428    }
429    
430    pub fn send_change_event(&mut self) {
431        
432        let mut new_geom = self.get_window_geom();
433        if new_geom.inner_size.x < self.last_window_geom.inner_size.x ||
434        new_geom.inner_size.y < self.last_window_geom.inner_size.y {
435            new_geom.is_fullscreen = false;
436        }
437        let old_geom = self.last_window_geom.clone();
438        self.last_window_geom = new_geom.clone();
439        
440        self.do_callback(XlibEvent::WindowGeomChange(WindowGeomChangeEvent {
441            window_id: self.window_id,
442            old_geom: old_geom,
443            new_geom: new_geom
444        }));
445        self.do_callback(XlibEvent::Paint);
446    }
447    
448    pub fn send_focus_event(&mut self) {
449        self.do_callback(XlibEvent::AppGotFocus);
450    }
451    
452    pub fn send_focus_lost_event(&mut self) {
453        self.do_callback(XlibEvent::AppLostFocus);
454    }
455    
456    pub fn send_mouse_down(&mut self, button: MouseButton, modifiers: KeyModifiers) {
457        self.do_callback(XlibEvent::MouseDown(MouseDownEvent {
458            button,
459            modifiers,
460            window_id: self.window_id,
461            abs: self.last_mouse_pos,
462            time: self.time_now(),
463            handled: Cell::new(Area::Empty),
464        }));
465    }
466    
467    pub fn send_mouse_up(&mut self, button: MouseButton, modifiers: KeyModifiers) {
468        self.do_callback(XlibEvent::MouseUp(MouseUpEvent {
469            button,
470            modifiers,
471            window_id: self.window_id,
472            abs: self.last_mouse_pos,
473            time: self.time_now()
474        }));
475    }
476    
477    pub fn send_mouse_move(&mut self, pos: DVec2, modifiers: KeyModifiers) {
478        self.last_mouse_pos = pos;
479        self.do_callback(XlibEvent::MouseMove(MouseMoveEvent {
480            window_id: self.window_id,
481            abs: pos,
482            modifiers,
483            time: self.time_now(),
484            handled: Cell::new(Area::Empty),
485        }));
486        
487    }
488    
489    pub fn send_close_requested_event(&mut self) -> bool {
490        let accept_close = Rc::new(Cell::new(true));
491        self.do_callback(XlibEvent::WindowCloseRequested(WindowCloseRequestedEvent {
492            window_id: self.window_id,
493            accept_close: accept_close.clone()
494        }));
495        if !accept_close.get() {
496            return false
497        }
498        true
499    }
500    
501    pub fn send_text_input(&mut self, input: String, replace_last: bool) {
502        self.do_callback(XlibEvent::TextInput(TextInputEvent {
503            input,
504            was_paste: false,
505            replace_last,
506        }))
507    }
508    
509}
510
511
512#[derive(Clone, Copy, PartialEq)]
513#[repr(C)]
514struct MwmHints {
515    pub flags: c_ulong,
516    pub functions: c_ulong,
517    pub decorations: c_ulong,
518    pub input_mode: c_long,
519    pub status: c_ulong,
520}
521
522pub const MWM_HINTS_FUNCTIONS: c_ulong = 1 << 0;
523pub const MWM_HINTS_DECORATIONS: c_ulong = 1 << 1;
524
525pub const MWM_FUNC_ALL: c_ulong = 1 << 0;
526pub const MWM_FUNC_RESIZE: c_ulong = 1 << 1;
527pub const MWM_FUNC_MOVE: c_ulong = 1 << 2;
528pub const MWM_FUNC_MINIMIZE: c_ulong = 1 << 3;
529pub const MWM_FUNC_MAXIMIZE: c_ulong = 1 << 4;
530pub const MWM_FUNC_CLOSE: c_ulong = 1 << 5;
531pub const _NET_WM_MOVERESIZE_SIZE_TOPLEFT: c_long = 0;
532pub const _NET_WM_MOVERESIZE_SIZE_TOP: c_long = 1;
533pub const _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: c_long = 2;
534pub const _NET_WM_MOVERESIZE_SIZE_RIGHT: c_long = 3;
535pub const _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: c_long = 4;
536pub const _NET_WM_MOVERESIZE_SIZE_BOTTOM: c_long = 5;
537pub const _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: c_long = 6;
538pub const _NET_WM_MOVERESIZE_SIZE_LEFT: c_long = 7;
539pub const _NET_WM_MOVERESIZE_MOVE: c_long = 8;/* movement only */
540pub const _NET_WM_MOVERESIZE_SIZE_KEYBOARD: c_long = 9;/* size via keyboard */
541pub const _NET_WM_MOVERESIZE_MOVE_KEYBOARD: c_long = 10;
542
543pub const _NET_WM_STATE_REMOVE: c_long = 0;/* remove/unset property */
544pub const _NET_WM_STATE_ADD: c_long = 1;/* add/set property */
545pub const _NET_WM_STATE_TOGGLE: c_long = 2;/* toggle property  */
546
547/* move via keyboard */
548
549pub struct Dnd {
550    pub atoms: DndAtoms,
551    pub display: *mut x11_sys::Display,
552    pub type_list: Option<Vec<x11_sys::Atom >>,
553    pub selection: Option<CString>,
554}
555
556impl Dnd {
557    pub unsafe fn new(display: *mut x11_sys::Display) -> Dnd {
558        Dnd {
559            atoms: DndAtoms::new(display),
560            display,
561            type_list: None,
562            selection: None,
563        }
564    }
565    
566    /// Enables drag-and-drop for the given window.
567    pub unsafe fn enable_for_window(&mut self, window: x11_sys::Window) {
568        // To enable drag-and-drop for a window, we need to set the XDndAware property of the window
569        // to the version of XDnd we support.
570        
571        // I took this value from the Winit source code. Apparently, this is the latest version, and
572        // hasn't changed since 2002.
573        let version = 5 as c_ulong;
574        
575        x11_sys::XChangeProperty(
576            self.display,
577            window,
578            self.atoms.aware,
579            4, // XA_ATOM
580            32,
581            x11_sys::PropModeReplace as std::os::raw::c_int,
582            &version as *const c_ulong as *const std::os::raw::c_uchar,
583            1
584        );
585    }
586    
587    /// Handles a XDndEnter event.
588    pub unsafe fn handle_enter_event(&mut self, event: &x11_sys::XClientMessageEvent) {
589        // The XDndEnter event is sent by the source window when a drag begins. That is, the mouse
590        // enters the client rectangle of the target window. The target window is supposed to
591        // respond to this by requesting the list of types supported by the source.
592        
593        let source_window = event.data.l[0] as x11_sys::Window;
594        let has_more_types = event.data.l[1] & (1 << 0) != 0;
595        
596        // If the has_more_types flags is set, we have to obtain the list of supported types from
597        // the XDndTypeList property. Otherwise, we can obtain the list of supported types from the
598        // event itself.
599        self.type_list = Some(if has_more_types {
600            self.get_type_list_property(source_window)
601        } else {
602            event.data.l[2..4]
603                .iter()
604                .map( | &l | l as x11_sys::Atom)
605                .filter( | &atom | atom != x11_sys::None as x11_sys::Atom)
606                .collect()
607        });
608    }
609    
610    /// Handles a XDndDrop event.
611    pub unsafe fn handle_drop_event(&mut self, event: &x11_sys::XClientMessageEvent) {
612        // The XDndLeave event is sent by the source window when a drag is confirmed. That is, the
613        // mouse button is released while the mouse is inside the client rectangle of the target
614        // window. The target window is supposed to respond to this by requesting that the selection
615        // representing the thing being dragged is converted to the appropriate data type (in our
616        // case, a URI list). The source window, in turn, is supposed to respond this by sending a
617        // selection event containing the data to the source window.
618        
619        let target_window = event.window as x11_sys::Window;
620        self.convert_selection(target_window);
621        self.type_list = None;
622    }
623    
624    /// Handles a XDndLeave event.
625    pub unsafe fn handle_leave_event(&mut self, _event: &x11_sys::XClientMessageEvent) {
626        // The XDndLeave event is sent by the source window when a drag is canceled. That is, the
627        // mouse leaves the client rectangle of the target window. The target window is supposed to
628        // repsond this this by pretending the drag never happened.
629        
630        self.type_list = None;
631    }
632    
633    /// Handles a XDndPosition event.
634    pub unsafe fn handle_position_event(&mut self, event: &x11_sys::XClientMessageEvent) {
635        // The XDndPosition event is sent by the source window after the XDndEnter event, every time
636        // the mouse is moved. The target window is supposed to respond to this by sending a status
637        // event to the source window notifying whether it can accept the drag at this position.
638        
639        let target_window = event.window as x11_sys::Window;
640        let source_window = event.data.l[0] as x11_sys::Window;
641        
642        // For now we accept te drag if and only if the list of types supported by the source
643        // includes a uri list.
644        //
645        // TODO: Extend this test by taking into account the position of the mouse as well.
646        let accepted = self.type_list.as_ref().map_or(false, | type_list | type_list.contains(&self.atoms.uri_list));
647        
648        // Notify the source window whether we can accept the drag at this position.
649        self.send_status_event(source_window, target_window, accepted);
650        
651        // If this is the first time we've accepted the drag, request that the drag-and-drop
652        // selection be converted to a URI list. The target window is supposed to respond to this by
653        // sending a XSelectionEvent containing the URI list.
654        
655        // Since this is an asynchronous operation, its possible for another XDndPosition event to
656        // come in before the response to the first conversion request has been received. In this
657        // case, a second conversion request will be sent, the response to which will be ignored.
658        if accepted && self.selection.is_none() {
659        }
660    }
661    
662    /// Handles a XSelectionEvent.
663    pub unsafe fn handle_selection_event(&mut self, _event: &x11_sys::XSelectionEvent) {
664        // The XSelectionEvent is sent by the source window in response to a request by the source
665        // window to convert the selection representing the thing being dragged to the appropriate
666        // data type. This request is always sent in response to a XDndDrop event, so this event
667        // should only be received after a drop operation has completed.
668        
669        //let source_window = event.requestor;
670        //let selection = CString::new(self.get_selection_property(source_window)).unwrap();
671        
672        // TODO: Actually use the selection
673    }
674    
675    /// Gets the XDndSelection property from the source window.
676    pub unsafe fn get_selection_property(&mut self, source_window: x11_sys::Window) -> Vec< std::os::raw::c_uchar> {
677        let mut selection = Vec::new();
678        let mut offset = 0;
679        let length = 1024;
680        let mut actual_type = 0;
681        let mut actual_format = 0;
682        let mut nitems = 0;
683        let mut bytes_after = 0;
684        let mut prop = ptr::null_mut();
685        loop {
686            x11_sys::XGetWindowProperty(
687                self.display,
688                source_window,
689                self.atoms.selection,
690                offset,
691                length,
692                x11_sys::False as std::os::raw::c_int,
693                self.atoms.uri_list,
694                &mut actual_type,
695                &mut actual_format,
696                &mut nitems,
697                &mut bytes_after,
698                &mut prop,
699            );
700            selection.extend_from_slice(std::slice::from_raw_parts(prop as *mut  std::os::raw::c_uchar, nitems as usize));
701            x11_sys::XFree(prop as *mut c_void);
702            if bytes_after == 0 {
703                break;
704            }
705            offset += length;
706        };
707        selection
708    }
709    
710    /// Gets the XDndTypeList property from the source window.
711    pub unsafe fn get_type_list_property(&mut self, source_window: x11_sys::Window) -> Vec<x11_sys::Atom> {
712        let mut type_list = Vec::new();
713        let mut offset = 0;
714        let length = 1024;
715        let mut actual_type = 0;
716        let mut actual_format = 0;
717        let mut nitems = 0;
718        let mut bytes_after = 0;
719        let mut prop = ptr::null_mut();
720        loop {
721            x11_sys::XGetWindowProperty(
722                self.display,
723                source_window,
724                self.atoms.type_list,
725                offset,
726                length,
727                x11_sys::False as  std::os::raw::c_int,
728                4, // XA_ATOM,
729                &mut actual_type,
730                &mut actual_format,
731                &mut nitems,
732                &mut bytes_after,
733                &mut prop,
734            );
735            type_list.extend_from_slice(std::slice::from_raw_parts(prop as *mut x11_sys::Atom, nitems as usize));
736            x11_sys::XFree(prop as *mut c_void);
737            if bytes_after == 0 {
738                break;
739            }
740            offset += length;
741        };
742        type_list
743    }
744    
745    /// Sends a XDndStatus event to the target window.
746    pub unsafe fn send_status_event(&mut self, source_window: x11_sys::Window, target_window: x11_sys::Window, accepted: bool) {
747        x11_sys::XSendEvent(
748            self.display,
749            source_window,
750            x11_sys::False as  std::os::raw::c_int,
751            x11_sys::NoEventMask as  std::os::raw::c_long,
752            &mut x11_sys::XClientMessageEvent {
753                type_: x11_sys::ClientMessage as  std::os::raw::c_int,
754                serial: 0,
755                send_event: 0,
756                display: self.display,
757                window: source_window,
758                message_type: self.atoms.status,
759                format: 32,
760                data: {
761                    let mut data = mem::zeroed::<x11_sys::XClientMessageEvent__bindgen_ty_1>();
762                    data.l[0] = target_window as c_long;
763                    data.l[1] = if accepted {1 << 0} else {0};
764                    data.l[2] = 0;
765                    data.l[3] = 0;
766                    data.l[4] = if accepted {self.atoms.action_private} else {self.atoms.none} as c_long;
767                    data
768                }
769            } as *mut x11_sys::XClientMessageEvent as *mut x11_sys::XEvent
770        );
771        x11_sys::XFlush(self.display);
772    }
773    
774    // Requests that the selection representing the thing being dragged is converted to the
775    // appropriate data type (in our case, a URI list).
776    pub unsafe fn convert_selection(&self, target_window: x11_sys::Window) {
777        x11_sys::XConvertSelection(
778            self.display,
779            self.atoms.selection,
780            self.atoms.uri_list,
781            self.atoms.selection,
782            target_window,
783            x11_sys::CurrentTime as x11_sys::Time,
784        );
785    }
786}
787
788pub struct DndAtoms {
789    pub action_private: x11_sys::Atom,
790    pub aware: x11_sys::Atom,
791    pub drop: x11_sys::Atom,
792    pub enter: x11_sys::Atom,
793    pub leave: x11_sys::Atom,
794    pub none: x11_sys::Atom,
795    pub position: x11_sys::Atom,
796    pub selection: x11_sys::Atom,
797    pub status: x11_sys::Atom,
798    pub type_list: x11_sys::Atom,
799    pub uri_list: x11_sys::Atom,
800}
801
802impl DndAtoms {
803    pub unsafe fn new(display: *mut x11_sys::Display) -> DndAtoms {
804        DndAtoms {
805            action_private: x11_sys::XInternAtom(display, "XdndActionPrivate\0".as_ptr() as *const _, 0),
806            aware: x11_sys::XInternAtom(display, "XdndAware\0".as_ptr() as *const _, 0),
807            drop: x11_sys::XInternAtom(display, "XdndDrop\0".as_ptr() as *const _, 0),
808            enter: x11_sys::XInternAtom(display, "XdndEnter\0".as_ptr() as *const _, 0),
809            leave: x11_sys::XInternAtom(display, "XdndLeave\0".as_ptr() as *const _, 0),
810            none: x11_sys::XInternAtom(display, "None\0".as_ptr() as *const _, 0),
811            position: x11_sys::XInternAtom(display, "XdndPosition\0".as_ptr() as *const _, 0),
812            selection: x11_sys::XInternAtom(display, "XdndSelection\0".as_ptr() as *const _, 0),
813            status: x11_sys::XInternAtom(display, "XdndStatus\0".as_ptr() as *const _, 0),
814            type_list: x11_sys::XInternAtom(display, "XdndTypeList\0".as_ptr() as *const _, 0),
815            uri_list: x11_sys::XInternAtom(display, "text/uri-list\0".as_ptr() as *const _, 0),
816        }
817    }
818}