gpu_display/
gpu_display_x.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#[path = "generated/xlib.rs"]
6#[allow(
7    dead_code,
8    non_snake_case,
9    non_camel_case_types,
10    non_upper_case_globals
11)]
12mod xlib;
13
14use std::cmp::max;
15use std::ffi::c_void;
16use std::ffi::CStr;
17use std::ffi::CString;
18use std::mem::transmute_copy;
19use std::mem::zeroed;
20use std::os::raw::c_ulong;
21use std::ptr::null;
22use std::ptr::null_mut;
23use std::ptr::NonNull;
24use std::rc::Rc;
25
26use base::AsRawDescriptor;
27use base::RawDescriptor;
28use base::VolatileSlice;
29use libc::shmat;
30use libc::shmctl;
31use libc::shmdt;
32use libc::shmget;
33use libc::IPC_CREAT;
34use libc::IPC_PRIVATE;
35use libc::IPC_RMID;
36use linux_input_sys::virtio_input_event;
37use vm_control::gpu::DisplayParameters;
38
39use crate::keycode_converter::KeycodeTranslator;
40use crate::keycode_converter::KeycodeTypes;
41use crate::DisplayT;
42use crate::EventDeviceKind;
43use crate::GpuDisplayError;
44use crate::GpuDisplayEvents;
45use crate::GpuDisplayFramebuffer;
46use crate::GpuDisplayResult;
47use crate::GpuDisplaySurface;
48use crate::SurfaceType;
49use crate::SysDisplayT;
50
51const BUFFER_COUNT: usize = 2;
52
53/// A wrapper for XFree that takes any type.
54/// SAFETY: It is caller's responsibility to ensure that `t` is valid for the entire duration of the
55/// call.
56unsafe fn x_free<T>(t: *mut T) {
57    xlib::XFree(t as *mut c_void);
58}
59
60#[derive(Clone)]
61struct XDisplay(Rc<NonNull<xlib::Display>>);
62impl Drop for XDisplay {
63    fn drop(&mut self) {
64        if Rc::strong_count(&self.0) == 1 {
65            // TODO(b/315870313): Add safety comment
66            #[allow(clippy::undocumented_unsafe_blocks)]
67            unsafe {
68                xlib::XCloseDisplay(self.as_ptr());
69            }
70        }
71    }
72}
73
74impl XDisplay {
75    /// Returns a pointer to the X display object.
76    fn as_ptr(&self) -> *mut xlib::Display {
77        self.0.as_ptr()
78    }
79
80    /// Sends any pending commands to the X server.
81    fn flush(&self) {
82        // TODO(b/315870313): Add safety comment
83        #[allow(clippy::undocumented_unsafe_blocks)]
84        unsafe {
85            xlib::XFlush(self.as_ptr());
86        }
87    }
88
89    /// Returns true of the XShm extension is supported on this display.
90    fn supports_shm(&self) -> bool {
91        // TODO(b/315870313): Add safety comment
92        #[allow(clippy::undocumented_unsafe_blocks)]
93        unsafe {
94            xlib::XShmQueryExtension(self.as_ptr()) != 0
95        }
96    }
97
98    /// Gets the default screen of this display.
99    fn default_screen(&self) -> Option<XScreen> {
100        Some(XScreen(NonNull::new(
101            // TODO(b/315870313): Add safety comment
102            #[allow(clippy::undocumented_unsafe_blocks)]
103            unsafe {
104                xlib::XDefaultScreenOfDisplay(self.as_ptr())
105            },
106        )?))
107    }
108
109    /// Blocks until the next event from the display is received and returns that event.
110    ///
111    /// Always flush before using this if any X commands where issued.
112    fn next_event(&self) -> XEvent {
113        // TODO(b/315870313): Add safety comment
114        #[allow(clippy::undocumented_unsafe_blocks)]
115        unsafe {
116            let mut ev = zeroed();
117            xlib::XNextEvent(self.as_ptr(), &mut ev);
118            ev.into()
119        }
120    }
121}
122
123impl AsRawDescriptor for XDisplay {
124    fn as_raw_descriptor(&self) -> RawDescriptor {
125        // TODO(b/315870313): Add safety comment
126        #[allow(clippy::undocumented_unsafe_blocks)]
127        unsafe {
128            xlib::XConnectionNumber(self.as_ptr())
129        }
130    }
131}
132
133struct XEvent(xlib::XEvent);
134impl From<xlib::XEvent> for XEvent {
135    fn from(ev: xlib::XEvent) -> XEvent {
136        XEvent(ev)
137    }
138}
139
140impl XEvent {
141    fn any(&self) -> xlib::XAnyEvent {
142        // All events have the same xany field.
143        // TODO(b/315870313): Add safety comment
144        #[allow(clippy::undocumented_unsafe_blocks)]
145        unsafe {
146            self.0.xany
147        }
148    }
149
150    fn type_(&self) -> u32 {
151        // All events have the same type_ field.
152        // TODO(b/315870313): Add safety comment
153        #[allow(clippy::undocumented_unsafe_blocks)]
154        unsafe {
155            self.0.type_ as u32
156        }
157    }
158
159    fn window(&self) -> xlib::Window {
160        self.any().window
161    }
162
163    // Some of the event types are dynamic so they need to be passed in.
164    fn as_enum(&self, shm_complete_type: u32) -> XEventEnum {
165        match self.type_() {
166            xlib::KeyPress | xlib::KeyRelease => XEventEnum::KeyEvent(
167                // TODO(b/315870313): Add safety comment
168                #[allow(clippy::undocumented_unsafe_blocks)]
169                unsafe {
170                    self.0.xkey
171                },
172            ),
173            xlib::ButtonPress => {
174                // TODO(b/315870313): Add safety comment
175                #[allow(clippy::undocumented_unsafe_blocks)]
176                XEventEnum::ButtonEvent {
177                    event: unsafe { self.0.xbutton },
178                    pressed: true,
179                }
180            }
181            xlib::ButtonRelease => {
182                // TODO(b/315870313): Add safety comment
183                #[allow(clippy::undocumented_unsafe_blocks)]
184                XEventEnum::ButtonEvent {
185                    event: unsafe { self.0.xbutton },
186                    pressed: false,
187                }
188            }
189            xlib::MotionNotify => XEventEnum::Motion(
190                // TODO(b/315870313): Add safety comment
191                #[allow(clippy::undocumented_unsafe_blocks)]
192                unsafe {
193                    self.0.xmotion
194                },
195            ),
196            xlib::Expose => XEventEnum::Expose,
197            xlib::ClientMessage => {
198                XEventEnum::ClientMessage(
199                    // TODO(b/315870313): Add safety comment
200                    #[allow(clippy::undocumented_unsafe_blocks)]
201                    unsafe {
202                        self.0.xclient.data.l[0] as u64
203                    },
204                )
205            }
206            t if t == shm_complete_type => {
207                // Because XShmCompletionEvent is not part of the XEvent union, simulate a union
208                // with transmute_copy. If the shm_complete_type turns out to be bogus, some of the
209                // data would be incorrect, but the common event fields would still be valid.
210                // TODO(b/315870313): Add safety comment
211                #[allow(clippy::undocumented_unsafe_blocks)]
212                let ev_completion: xlib::XShmCompletionEvent = unsafe { transmute_copy(&self.0) };
213                XEventEnum::ShmCompletionEvent(ev_completion.shmseg)
214            }
215            _ => XEventEnum::Unhandled,
216        }
217    }
218}
219
220enum XEventEnum {
221    KeyEvent(xlib::XKeyEvent),
222    ButtonEvent {
223        event: xlib::XButtonEvent,
224        pressed: bool,
225    },
226    Motion(xlib::XMotionEvent),
227    Expose,
228    ClientMessage(u64),
229    ShmCompletionEvent(xlib::ShmSeg),
230    // We don't care about most kinds of events,
231    Unhandled,
232}
233
234struct XScreen(NonNull<xlib::Screen>);
235
236impl XScreen {
237    fn as_ptr(&self) -> *mut xlib::Screen {
238        self.0.as_ptr()
239    }
240
241    /// Gets the screen number of this screen.
242    fn get_number(&self) -> i32 {
243        // TODO(b/315870313): Add safety comment
244        #[allow(clippy::undocumented_unsafe_blocks)]
245        unsafe {
246            xlib::XScreenNumberOfScreen(self.as_ptr())
247        }
248    }
249}
250
251struct Buffer {
252    display: XDisplay,
253    image: *mut xlib::XImage,
254    /// The documentation says XShmSegmentInfo must last at least as long as the XImage, which
255    /// probably precludes moving it as well.
256    segment_info: Box<xlib::XShmSegmentInfo>,
257    size: usize,
258    in_use: bool,
259}
260
261impl Drop for Buffer {
262    fn drop(&mut self) {
263        // TODO(b/315870313): Add safety comment
264        #[allow(clippy::undocumented_unsafe_blocks)]
265        unsafe {
266            xlib::XShmDetach(self.display.as_ptr(), self.segment_info.as_mut());
267            xlib::XDestroyImage(self.image);
268            shmdt(self.segment_info.shmaddr as *const _);
269            shmctl(self.segment_info.shmid, IPC_RMID, null_mut());
270        }
271    }
272}
273
274impl Buffer {
275    fn as_volatile_slice(&self) -> VolatileSlice {
276        // TODO(b/315870313): Add safety comment
277        #[allow(clippy::undocumented_unsafe_blocks)]
278        unsafe {
279            VolatileSlice::from_raw_parts(self.segment_info.shmaddr as *mut _, self.size)
280        }
281    }
282
283    fn stride(&self) -> usize {
284        // TODO(b/315870313): Add safety comment
285        #[allow(clippy::undocumented_unsafe_blocks)]
286        unsafe {
287            (*self.image).bytes_per_line as usize
288        }
289    }
290
291    fn bytes_per_pixel(&self) -> usize {
292        // TODO(b/315870313): Add safety comment
293        #[allow(clippy::undocumented_unsafe_blocks)]
294        let bytes_per_pixel = unsafe { (*self.image).bits_per_pixel / 8 };
295        bytes_per_pixel as usize
296    }
297}
298
299// Surfaces here are equivalent to XWindows.
300struct XSurface {
301    display: XDisplay,
302    visual: *mut xlib::Visual,
303    depth: u32,
304    window: xlib::Window,
305    gc: xlib::GC,
306    width: u32,
307    height: u32,
308
309    // Fields for handling the buffer swap chain.
310    buffers: [Option<Buffer>; BUFFER_COUNT],
311    buffer_next: usize,
312    buffer_completion_type: u32,
313
314    // Fields for handling window close requests
315    delete_window_atom: c_ulong,
316    close_requested: bool,
317}
318
319impl XSurface {
320    /// Returns index of the current (on-screen) buffer, or 0 if there are no buffers.
321    fn current_buffer(&self) -> usize {
322        match self.buffer_next.checked_sub(1) {
323            Some(i) => i,
324            None => self.buffers.len() - 1,
325        }
326    }
327
328    /// Draws the indicated buffer onto the screen.
329    fn draw_buffer(&mut self, buffer_index: usize) {
330        let buffer = match self.buffers.get_mut(buffer_index) {
331            Some(Some(b)) => b,
332            _ => {
333                // If there is no buffer, that means the framebuffer was never set and we should
334                // simply blank the window with arbitrary contents.
335                // TODO(b/315870313): Add safety comment
336                #[allow(clippy::undocumented_unsafe_blocks)]
337                unsafe {
338                    xlib::XClearWindow(self.display.as_ptr(), self.window);
339                }
340                return;
341            }
342        };
343        // Mark the buffer as in use. When the XShmCompletionEvent occurs, this will get marked
344        // false.
345        buffer.in_use = true;
346        // TODO(b/315870313): Add safety comment
347        #[allow(clippy::undocumented_unsafe_blocks)]
348        unsafe {
349            xlib::XShmPutImage(
350                self.display.as_ptr(),
351                self.window,
352                self.gc,
353                buffer.image,
354                0, // src x
355                0, // src y
356                0, // dst x
357                0, // dst y
358                self.width,
359                self.height,
360                true as i32, /* send XShmCompletionEvent event */
361            );
362            self.display.flush();
363        }
364    }
365
366    /// Gets the buffer at buffer_index, allocating it if necessary.
367    fn lazily_allocate_buffer(&mut self, buffer_index: usize) -> Option<&Buffer> {
368        if buffer_index >= self.buffers.len() {
369            return None;
370        }
371
372        if self.buffers[buffer_index].is_some() {
373            return self.buffers[buffer_index].as_ref();
374        }
375        // The buffer_index is valid and the buffer was never created, so we create it now.
376        // TODO(b/315870313): Add safety comment
377        #[allow(clippy::undocumented_unsafe_blocks)]
378        unsafe {
379            // The docs for XShmCreateImage imply that XShmSegmentInfo must be allocated to live at
380            // least as long as the XImage, which probably means it can't move either. Use a Box in
381            // order to fulfill those requirements.
382            let mut segment_info: Box<xlib::XShmSegmentInfo> = Box::new(zeroed());
383            let image = xlib::XShmCreateImage(
384                self.display.as_ptr(),
385                self.visual,
386                self.depth,
387                xlib::ZPixmap as i32,
388                null_mut(),
389                segment_info.as_mut(),
390                self.width,
391                self.height,
392            );
393            if image.is_null() {
394                return None;
395            }
396            let size = (*image)
397                .bytes_per_line
398                .checked_mul((*image).height)
399                .unwrap();
400            segment_info.shmid = shmget(IPC_PRIVATE, size as usize, IPC_CREAT | 0o777);
401            if segment_info.shmid == -1 {
402                xlib::XDestroyImage(image);
403                return None;
404            }
405            segment_info.shmaddr = shmat(segment_info.shmid, null_mut(), 0) as *mut _;
406            if segment_info.shmaddr == (-1isize) as *mut _ {
407                xlib::XDestroyImage(image);
408                shmctl(segment_info.shmid, IPC_RMID, null_mut());
409                return None;
410            }
411            (*image).data = segment_info.shmaddr;
412            segment_info.readOnly = true as i32;
413            xlib::XShmAttach(self.display.as_ptr(), segment_info.as_mut());
414            self.buffers[buffer_index] = Some(Buffer {
415                display: self.display.clone(),
416                image,
417                segment_info,
418                size: size as usize,
419                in_use: false,
420            });
421            self.buffers[buffer_index].as_ref()
422        }
423    }
424}
425
426impl GpuDisplaySurface for XSurface {
427    #[allow(clippy::unnecessary_cast)]
428    fn surface_descriptor(&self) -> u64 {
429        self.window as u64
430    }
431
432    fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
433        // Framebuffers are lazily allocated. If the next buffer is not in self.buffers, add it
434        // using push_new_buffer and then get its memory.
435        let framebuffer = self.lazily_allocate_buffer(self.buffer_next)?;
436        let bytes_per_pixel = framebuffer.bytes_per_pixel() as u32;
437        Some(GpuDisplayFramebuffer::new(
438            framebuffer.as_volatile_slice(),
439            framebuffer.stride() as u32,
440            bytes_per_pixel,
441        ))
442    }
443
444    fn next_buffer_in_use(&self) -> bool {
445        // Buffers that have not yet been made are not in use, hence unwrap_or(false).
446        self.buffers
447            .get(self.buffer_next)
448            .and_then(|b| Some(b.as_ref()?.in_use))
449            .unwrap_or(false)
450    }
451
452    fn close_requested(&self) -> bool {
453        self.close_requested
454    }
455
456    fn flip(&mut self) {
457        let current_buffer_index = self.buffer_next;
458        self.buffer_next = (self.buffer_next + 1) % self.buffers.len();
459        self.draw_buffer(current_buffer_index);
460    }
461
462    fn buffer_completion_type(&self) -> u32 {
463        self.buffer_completion_type
464    }
465
466    fn draw_current_buffer(&mut self) {
467        self.draw_buffer(self.current_buffer())
468    }
469
470    fn on_client_message(&mut self, client_data: u64) {
471        if client_data == self.delete_window_atom {
472            self.close_requested = true;
473        }
474    }
475
476    fn on_shm_completion(&mut self, shm_complete: u64) {
477        for buffer in self.buffers.iter_mut().flatten() {
478            if buffer.segment_info.shmseg == shm_complete {
479                buffer.in_use = false;
480            }
481        }
482    }
483}
484
485impl Drop for XSurface {
486    fn drop(&mut self) {
487        // SAFETY:
488        // Safe given it should always be of the correct type.
489        unsafe {
490            xlib::XFreeGC(self.display.as_ptr(), self.gc);
491            xlib::XDestroyWindow(self.display.as_ptr(), self.window);
492        }
493    }
494}
495
496pub struct DisplayX {
497    display: XDisplay,
498    screen: XScreen,
499    visual: *mut xlib::Visual,
500    keycode_translator: KeycodeTranslator,
501    current_event: Option<XEvent>,
502    mt_tracking_id: u16,
503}
504
505impl DisplayX {
506    pub fn open_display(display: Option<&str>) -> GpuDisplayResult<DisplayX> {
507        let display_cstr = match display.map(CString::new) {
508            Some(Ok(s)) => Some(s),
509            Some(Err(_)) => return Err(GpuDisplayError::InvalidPath),
510            None => None,
511        };
512
513        let keycode_translator = KeycodeTranslator::new(KeycodeTypes::XkbScancode);
514
515        // TODO(b/315870313): Add safety comment
516        #[allow(clippy::undocumented_unsafe_blocks)]
517        unsafe {
518            // Open the display
519            let display = match NonNull::new(xlib::XOpenDisplay(
520                display_cstr
521                    .as_ref()
522                    .map(|s| CStr::as_ptr(s))
523                    .unwrap_or(null()),
524            )) {
525                Some(display_ptr) => XDisplay(Rc::new(display_ptr)),
526                None => return Err(GpuDisplayError::Connect),
527            };
528
529            // Check for required extension.
530            if !display.supports_shm() {
531                return Err(GpuDisplayError::RequiredFeature("xshm extension"));
532            }
533
534            let screen = display
535                .default_screen()
536                .ok_or(GpuDisplayError::Connect)
537                .unwrap();
538            let screen_number = screen.get_number();
539
540            // Check for and save required visual (24-bit BGR for the default screen).
541            let mut visual_info_template = xlib::XVisualInfo {
542                visual: null_mut(),
543                visualid: 0,
544                screen: screen_number,
545                depth: 24,
546                class: 0,
547                red_mask: 0x00ff0000,
548                green_mask: 0x0000ff00,
549                blue_mask: 0x000000ff,
550                colormap_size: 0,
551                bits_per_rgb: 0,
552            };
553            let visual_info = xlib::XGetVisualInfo(
554                display.as_ptr(),
555                (xlib::VisualScreenMask
556                    | xlib::VisualDepthMask
557                    | xlib::VisualRedMaskMask
558                    | xlib::VisualGreenMaskMask
559                    | xlib::VisualBlueMaskMask) as i64,
560                &mut visual_info_template,
561                &mut 0,
562            );
563            if visual_info.is_null() {
564                return Err(GpuDisplayError::RequiredFeature("no matching visual"));
565            }
566            let visual = (*visual_info).visual;
567            x_free(visual_info);
568
569            Ok(DisplayX {
570                display,
571                screen,
572                visual,
573                keycode_translator,
574                current_event: None,
575                mt_tracking_id: 0,
576            })
577        }
578    }
579
580    pub fn next_tracking_id(&mut self) -> i32 {
581        let cur_id: i32 = self.mt_tracking_id as i32;
582        self.mt_tracking_id = self.mt_tracking_id.wrapping_add(1);
583        cur_id
584    }
585
586    pub fn current_tracking_id(&self) -> i32 {
587        self.mt_tracking_id as i32
588    }
589}
590
591impl DisplayT for DisplayX {
592    fn pending_events(&self) -> bool {
593        // TODO(b/315870313): Add safety comment
594        #[allow(clippy::undocumented_unsafe_blocks)]
595        unsafe {
596            xlib::XPending(self.display.as_ptr()) != 0
597        }
598    }
599
600    fn flush(&self) {
601        self.display.flush();
602    }
603
604    #[allow(clippy::unnecessary_cast)]
605    fn next_event(&mut self) -> GpuDisplayResult<u64> {
606        let ev = self.display.next_event();
607        let descriptor = ev.window() as u64;
608        self.current_event = Some(ev);
609        Ok(descriptor)
610    }
611
612    fn handle_next_event(
613        &mut self,
614        surface: &mut Box<dyn GpuDisplaySurface>,
615    ) -> Option<GpuDisplayEvents> {
616        // Should not panic since the common layer only calls this when an event exists.
617        let ev = self.current_event.take().unwrap();
618
619        match ev.as_enum(surface.buffer_completion_type()) {
620            XEventEnum::KeyEvent(key) => {
621                if let Some(linux_keycode) = self.keycode_translator.translate(key.keycode) {
622                    let events = vec![virtio_input_event::key(
623                        linux_keycode,
624                        key.type_ == xlib::KeyPress as i32,
625                        false,
626                    )];
627
628                    return Some(GpuDisplayEvents {
629                        events,
630                        device_type: EventDeviceKind::Keyboard,
631                    });
632                }
633            }
634            XEventEnum::ButtonEvent {
635                event: button_event,
636                pressed,
637            } => {
638                // We only support a single touch from button 1 (left mouse button).
639                // TODO(tutankhamen): slot is always 0, because all the input
640                // events come from mouse device, i.e. only one touch is possible at a time.
641                // Full MT protocol has to be implemented and properly wired later.
642                if button_event.button & xlib::Button1 != 0 {
643                    // The touch event *must* be first per the Linux input subsystem's guidance.
644                    let mut events = vec![virtio_input_event::multitouch_slot(0)];
645
646                    if pressed {
647                        events.push(virtio_input_event::multitouch_tracking_id(
648                            self.next_tracking_id(),
649                        ));
650                        events.push(virtio_input_event::multitouch_absolute_x(max(
651                            0,
652                            button_event.x,
653                        )));
654                        events.push(virtio_input_event::multitouch_absolute_y(max(
655                            0,
656                            button_event.y,
657                        )));
658                    } else {
659                        events.push(virtio_input_event::multitouch_tracking_id(-1));
660                    }
661
662                    return Some(GpuDisplayEvents {
663                        events,
664                        device_type: EventDeviceKind::Touchscreen,
665                    });
666                }
667            }
668            XEventEnum::Motion(motion) => {
669                if motion.state & xlib::Button1Mask != 0 {
670                    let events = vec![
671                        virtio_input_event::multitouch_slot(0),
672                        virtio_input_event::multitouch_tracking_id(self.current_tracking_id()),
673                        virtio_input_event::multitouch_absolute_x(max(0, motion.x)),
674                        virtio_input_event::multitouch_absolute_y(max(0, motion.y)),
675                    ];
676
677                    return Some(GpuDisplayEvents {
678                        events,
679                        device_type: EventDeviceKind::Touchscreen,
680                    });
681                }
682            }
683            XEventEnum::Expose => surface.draw_current_buffer(),
684            XEventEnum::ClientMessage(xclient_data) => {
685                surface.on_client_message(xclient_data);
686                return None;
687            }
688            XEventEnum::ShmCompletionEvent(shmseg) => {
689                surface.on_shm_completion(shmseg);
690                return None;
691            }
692            XEventEnum::Unhandled => return None,
693        }
694
695        None
696    }
697
698    fn create_surface(
699        &mut self,
700        parent_surface_id: Option<u32>,
701        _surface_id: u32,
702        _scanout_id: Option<u32>,
703        display_params: &DisplayParameters,
704        _surf_type: SurfaceType,
705    ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
706        if parent_surface_id.is_some() {
707            return Err(GpuDisplayError::Unsupported);
708        }
709
710        // TODO(b/315870313): Add safety comment
711        #[allow(clippy::undocumented_unsafe_blocks)]
712        unsafe {
713            let (width, height) = display_params.get_virtual_display_size();
714            let depth = xlib::XDefaultDepthOfScreen(self.screen.as_ptr()) as u32;
715
716            let black_pixel = xlib::XBlackPixelOfScreen(self.screen.as_ptr());
717
718            let window = xlib::XCreateSimpleWindow(
719                self.display.as_ptr(),
720                xlib::XRootWindowOfScreen(self.screen.as_ptr()),
721                0,
722                0,
723                width,
724                height,
725                1,
726                black_pixel,
727                black_pixel,
728            );
729
730            xlib::XStoreName(self.display.as_ptr(), window, c"crosvm".as_ptr());
731
732            let gc = xlib::XCreateGC(self.display.as_ptr(), window, 0, null_mut());
733
734            // Because the event is from an extension, its type must be calculated dynamically.
735            let buffer_completion_type =
736                xlib::XShmGetEventBase(self.display.as_ptr()) as u32 + xlib::ShmCompletion;
737
738            // Mark this window as responding to close requests.
739            let mut delete_window_atom =
740                xlib::XInternAtom(self.display.as_ptr(), c"WM_DELETE_WINDOW".as_ptr(), 0);
741            xlib::XSetWMProtocols(self.display.as_ptr(), window, &mut delete_window_atom, 1);
742
743            let size_hints = xlib::XAllocSizeHints();
744            (*size_hints).flags = (xlib::PMinSize | xlib::PMaxSize) as i64;
745            (*size_hints).max_width = width as i32;
746            (*size_hints).min_width = width as i32;
747            (*size_hints).max_height = height as i32;
748            (*size_hints).min_height = height as i32;
749            xlib::XSetWMNormalHints(self.display.as_ptr(), window, size_hints);
750            x_free(size_hints);
751
752            // We will use redraw the buffer when we are exposed.
753            xlib::XSelectInput(
754                self.display.as_ptr(),
755                window,
756                (xlib::ExposureMask
757                    | xlib::KeyPressMask
758                    | xlib::KeyReleaseMask
759                    | xlib::ButtonPressMask
760                    | xlib::ButtonReleaseMask
761                    | xlib::PointerMotionMask) as i64,
762            );
763
764            xlib::XClearWindow(self.display.as_ptr(), window);
765            xlib::XMapRaised(self.display.as_ptr(), window);
766
767            // Flush everything so that the window is visible immediately.
768            self.display.flush();
769
770            Ok(Box::new(XSurface {
771                display: self.display.clone(),
772                visual: self.visual,
773                depth,
774                window,
775                gc,
776                width,
777                height,
778                buffers: Default::default(),
779                buffer_next: 0,
780                buffer_completion_type,
781                delete_window_atom,
782                close_requested: false,
783            }))
784        }
785    }
786}
787
788impl SysDisplayT for DisplayX {}
789
790impl AsRawDescriptor for DisplayX {
791    fn as_raw_descriptor(&self) -> RawDescriptor {
792        self.display.as_raw_descriptor()
793    }
794}