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