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 base::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 use vm_control::gpu::DisplayParameters;
38
39 use crate::keycode_converter::KeycodeTranslator;
40 use crate::keycode_converter::KeycodeTypes;
41 use crate::DisplayT;
42 use crate::EventDeviceKind;
43 use crate::GpuDisplayError;
44 use crate::GpuDisplayEvents;
45 use crate::GpuDisplayFramebuffer;
46 use crate::GpuDisplayResult;
47 use crate::GpuDisplaySurface;
48 use crate::SurfaceType;
49 use crate::SysDisplayT;
50
51 const 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.
x_free<T>(t: *mut T)56 unsafe fn x_free<T>(t: *mut T) {
57 xlib::XFree(t as *mut c_void);
58 }
59
60 #[derive(Clone)]
61 struct XDisplay(Rc<NonNull<xlib::Display>>);
62 impl Drop for XDisplay {
drop(&mut self)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
74 impl XDisplay {
75 /// Returns a pointer to the X display object.
as_ptr(&self) -> *mut xlib::Display76 fn as_ptr(&self) -> *mut xlib::Display {
77 self.0.as_ptr()
78 }
79
80 /// Sends any pending commands to the X server.
flush(&self)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.
supports_shm(&self) -> bool90 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.
default_screen(&self) -> Option<XScreen>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.
next_event(&self) -> XEvent112 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
123 impl AsRawDescriptor for XDisplay {
as_raw_descriptor(&self) -> RawDescriptor124 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
133 struct XEvent(xlib::XEvent);
134 impl From<xlib::XEvent> for XEvent {
from(ev: xlib::XEvent) -> XEvent135 fn from(ev: xlib::XEvent) -> XEvent {
136 XEvent(ev)
137 }
138 }
139
140 impl XEvent {
any(&self) -> xlib::XAnyEvent141 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
type_(&self) -> u32150 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
window(&self) -> xlib::Window159 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.
as_enum(&self, shm_complete_type: u32) -> XEventEnum164 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
220 enum 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
234 struct XScreen(NonNull<xlib::Screen>);
235
236 impl XScreen {
as_ptr(&self) -> *mut xlib::Screen237 fn as_ptr(&self) -> *mut xlib::Screen {
238 self.0.as_ptr()
239 }
240
241 /// Gets the screen number of this screen.
get_number(&self) -> i32242 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
251 struct 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
261 impl Drop for Buffer {
drop(&mut self)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
274 impl Buffer {
as_volatile_slice(&self) -> VolatileSlice275 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
stride(&self) -> usize283 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
bytes_per_pixel(&self) -> usize291 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.
300 struct 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
319 impl XSurface {
320 /// Returns index of the current (on-screen) buffer, or 0 if there are no buffers.
current_buffer(&self) -> usize321 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.
draw_buffer(&mut self, buffer_index: usize)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.
lazily_allocate_buffer(&mut self, buffer_index: usize) -> Option<&Buffer>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
426 impl GpuDisplaySurface for XSurface {
427 #[allow(clippy::unnecessary_cast)]
surface_descriptor(&self) -> u64428 fn surface_descriptor(&self) -> u64 {
429 self.window as u64
430 }
431
framebuffer(&mut self) -> Option<GpuDisplayFramebuffer>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
next_buffer_in_use(&self) -> bool444 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
close_requested(&self) -> bool452 fn close_requested(&self) -> bool {
453 self.close_requested
454 }
455
flip(&mut self)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
buffer_completion_type(&self) -> u32462 fn buffer_completion_type(&self) -> u32 {
463 self.buffer_completion_type
464 }
465
draw_current_buffer(&mut self)466 fn draw_current_buffer(&mut self) {
467 self.draw_buffer(self.current_buffer())
468 }
469
on_client_message(&mut self, client_data: u64)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
on_shm_completion(&mut self, shm_complete: u64)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
485 impl Drop for XSurface {
drop(&mut self)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
496 pub 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
505 impl DisplayX {
open_display(display: Option<&str>) -> GpuDisplayResult<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
next_tracking_id(&mut self) -> i32580 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
current_tracking_id(&self) -> i32586 pub fn current_tracking_id(&self) -> i32 {
587 self.mt_tracking_id as i32
588 }
589 }
590
591 impl DisplayT for DisplayX {
pending_events(&self) -> bool592 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
flush(&self)600 fn flush(&self) {
601 self.display.flush();
602 }
603
604 #[allow(clippy::unnecessary_cast)]
next_event(&mut self) -> GpuDisplayResult<u64>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
handle_next_event( &mut self, surface: &mut Box<dyn GpuDisplaySurface>, ) -> Option<GpuDisplayEvents>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
create_surface( &mut self, parent_surface_id: Option<u32>, _surface_id: u32, _scanout_id: Option<u32>, display_params: &DisplayParameters, _surf_type: SurfaceType, ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>>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(
731 self.display.as_ptr(),
732 window,
733 CStr::from_bytes_with_nul(b"crosvm\0").unwrap().as_ptr(),
734 );
735
736 let gc = xlib::XCreateGC(self.display.as_ptr(), window, 0, null_mut());
737
738 // Because the event is from an extension, its type must be calculated dynamically.
739 let buffer_completion_type =
740 xlib::XShmGetEventBase(self.display.as_ptr()) as u32 + xlib::ShmCompletion;
741
742 // Mark this window as responding to close requests.
743 let mut delete_window_atom = xlib::XInternAtom(
744 self.display.as_ptr(),
745 CStr::from_bytes_with_nul(b"WM_DELETE_WINDOW\0")
746 .unwrap()
747 .as_ptr(),
748 0,
749 );
750 xlib::XSetWMProtocols(self.display.as_ptr(), window, &mut delete_window_atom, 1);
751
752 let size_hints = xlib::XAllocSizeHints();
753 (*size_hints).flags = (xlib::PMinSize | xlib::PMaxSize) as i64;
754 (*size_hints).max_width = width as i32;
755 (*size_hints).min_width = width as i32;
756 (*size_hints).max_height = height as i32;
757 (*size_hints).min_height = height as i32;
758 xlib::XSetWMNormalHints(self.display.as_ptr(), window, size_hints);
759 x_free(size_hints);
760
761 // We will use redraw the buffer when we are exposed.
762 xlib::XSelectInput(
763 self.display.as_ptr(),
764 window,
765 (xlib::ExposureMask
766 | xlib::KeyPressMask
767 | xlib::KeyReleaseMask
768 | xlib::ButtonPressMask
769 | xlib::ButtonReleaseMask
770 | xlib::PointerMotionMask) as i64,
771 );
772
773 xlib::XClearWindow(self.display.as_ptr(), window);
774 xlib::XMapRaised(self.display.as_ptr(), window);
775
776 // Flush everything so that the window is visible immediately.
777 self.display.flush();
778
779 Ok(Box::new(XSurface {
780 display: self.display.clone(),
781 visual: self.visual,
782 depth,
783 window,
784 gc,
785 width,
786 height,
787 buffers: Default::default(),
788 buffer_next: 0,
789 buffer_completion_type,
790 delete_window_atom,
791 close_requested: false,
792 }))
793 }
794 }
795 }
796
797 impl SysDisplayT for DisplayX {}
798
799 impl AsRawDescriptor for DisplayX {
as_raw_descriptor(&self) -> RawDescriptor800 fn as_raw_descriptor(&self) -> RawDescriptor {
801 self.display.as_raw_descriptor()
802 }
803 }
804