// Copyright 2019 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #[path = "generated/xlib.rs"] #[allow( dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals )] mod xlib; use linux_input_sys::virtio_input_event; use std::cmp::max; use std::collections::BTreeMap; use std::ffi::{c_void, CStr, CString}; use std::mem::{transmute_copy, zeroed}; use std::num::NonZeroU32; use std::os::raw::c_ulong; use std::os::unix::io::{AsRawFd, RawFd}; use std::ptr::{null, null_mut, NonNull}; use std::rc::{Rc, Weak}; use std::time::Duration; use libc::{shmat, shmctl, shmdt, shmget, IPC_CREAT, IPC_PRIVATE, IPC_RMID}; use crate::{ keycode_converter::KeycodeTranslator, keycode_converter::KeycodeTypes, DisplayT, EventDevice, EventDeviceKind, GpuDisplayError, GpuDisplayFramebuffer, }; use data_model::VolatileSlice; use sys_util::{error, PollContext, PollToken, WatchingEvents}; const BUFFER_COUNT: usize = 2; type ObjectId = NonZeroU32; /// A wrapper for XFree that takes any type. unsafe fn x_free(t: *mut T) { xlib::XFree(t as *mut c_void); } #[derive(Clone)] struct XDisplay(Rc>); impl Drop for XDisplay { fn drop(&mut self) { if Rc::strong_count(&self.0) == 1 { unsafe { xlib::XCloseDisplay(self.as_ptr()); } } } } impl XDisplay { fn as_ptr(&self) -> *mut xlib::Display { self.0.as_ptr() } /// Returns true of the XShm extension is supported on this display. fn supports_shm(&self) -> bool { unsafe { xlib::XShmQueryExtension(self.as_ptr()) != 0 } } /// Gets the default screen of this display. fn default_screen(&self) -> Option { Some(XScreen(NonNull::new(unsafe { xlib::XDefaultScreenOfDisplay(self.as_ptr()) })?)) } /// Returns true if there are events that are on the queue. fn pending_events(&self) -> bool { unsafe { xlib::XPending(self.as_ptr()) != 0 } } /// Sends any pending commands to the X server. fn flush(&self) { unsafe { xlib::XFlush(self.as_ptr()); } } /// Blocks until the next event from the display is received and returns that event. /// /// Always flush before using this if any X commands where issued. fn next_event(&self) -> XEvent { unsafe { let mut ev = zeroed(); xlib::XNextEvent(self.as_ptr(), &mut ev); ev.into() } } } impl AsRawFd for XDisplay { fn as_raw_fd(&self) -> RawFd { unsafe { xlib::XConnectionNumber(self.as_ptr()) } } } struct XEvent(xlib::XEvent); impl From for XEvent { fn from(ev: xlib::XEvent) -> XEvent { XEvent(ev) } } impl XEvent { fn any(&self) -> xlib::XAnyEvent { // All events have the same xany field. unsafe { self.0.xany } } fn type_(&self) -> u32 { // All events have the same type_ field. unsafe { self.0.type_ as u32 } } fn window(&self) -> xlib::Window { self.any().window } // Some of the event types are dynamic so they need to be passed in. fn as_enum(&self, shm_complete_type: u32) -> XEventEnum { match self.type_() { xlib::KeyPress | xlib::KeyRelease => XEventEnum::KeyEvent(unsafe { self.0.xkey }), xlib::ButtonPress => XEventEnum::ButtonEvent { event: unsafe { self.0.xbutton }, pressed: true, }, xlib::ButtonRelease => XEventEnum::ButtonEvent { event: unsafe { self.0.xbutton }, pressed: false, }, xlib::MotionNotify => XEventEnum::Motion(unsafe { self.0.xmotion }), xlib::Expose => XEventEnum::Expose, xlib::ClientMessage => { XEventEnum::ClientMessage(unsafe { self.0.xclient.data.l[0] as u64 }) } t if t == shm_complete_type => { // Because XShmCompletionEvent is not part of the XEvent union, simulate a union // with transmute_copy. If the shm_complete_type turns out to be bogus, some of the // data would be incorrect, but the common event fields would still be valid. let ev_completion: xlib::XShmCompletionEvent = unsafe { transmute_copy(&self.0) }; XEventEnum::ShmCompletionEvent(ev_completion.shmseg) } _ => XEventEnum::Unhandled, } } } enum XEventEnum { KeyEvent(xlib::XKeyEvent), ButtonEvent { event: xlib::XButtonEvent, pressed: bool, }, Motion(xlib::XMotionEvent), Expose, ClientMessage(u64), ShmCompletionEvent(xlib::ShmSeg), // We don't care about most kinds of events, Unhandled, } struct XScreen(NonNull); impl XScreen { fn as_ptr(&self) -> *mut xlib::Screen { self.0.as_ptr() } /// Gets the screen number of this screen. fn get_number(&self) -> i32 { unsafe { xlib::XScreenNumberOfScreen(self.as_ptr()) } } } struct Buffer { display: XDisplay, image: *mut xlib::XImage, /// The documentation says XShmSegmentInfo must last at least as long as the XImage, which /// probably precludes moving it as well. segment_info: Box, size: usize, in_use: bool, } impl Drop for Buffer { fn drop(&mut self) { unsafe { xlib::XShmDetach(self.display.as_ptr(), self.segment_info.as_mut()); xlib::XDestroyImage(self.image); shmdt(self.segment_info.shmaddr as *const _); shmctl(self.segment_info.shmid, IPC_RMID, null_mut()); } } } impl Buffer { fn as_volatile_slice(&self) -> VolatileSlice { unsafe { VolatileSlice::new(self.segment_info.shmaddr as *mut _, self.size as u64) } } fn stride(&self) -> usize { unsafe { (*self.image).bytes_per_line as usize } } fn bytes_per_pixel(&self) -> usize { let bytes_per_pixel = unsafe { (*self.image).bits_per_pixel / 8 }; bytes_per_pixel as usize } } // Surfaces here are equivalent to XWindows. struct Surface { display: XDisplay, visual: *mut xlib::Visual, depth: u32, window: xlib::Window, gc: xlib::GC, width: u32, height: u32, event_devices: BTreeMap, keycode_translator: KeycodeTranslator, // Fields for handling the buffer swap chain. buffers: [Option; BUFFER_COUNT], buffer_next: usize, buffer_completion_type: u32, // Fields for handling window close requests delete_window_atom: c_ulong, close_requested: bool, } impl Surface { fn create( display: XDisplay, screen: &XScreen, visual: *mut xlib::Visual, width: u32, height: u32, ) -> Result { let keycode_translator = KeycodeTranslator::new(KeycodeTypes::XkbScancode); unsafe { let depth = xlib::XDefaultDepthOfScreen(screen.as_ptr()) as u32; let black_pixel = xlib::XBlackPixelOfScreen(screen.as_ptr()); let window = xlib::XCreateSimpleWindow( display.as_ptr(), xlib::XRootWindowOfScreen(screen.as_ptr()), 0, 0, width, height, 1, black_pixel, black_pixel, ); let gc = xlib::XCreateGC(display.as_ptr(), window, 0, null_mut()); // Because the event is from an extension, its type must be calculated dynamically. let buffer_completion_type = xlib::XShmGetEventBase(display.as_ptr()) as u32 + xlib::ShmCompletion; // Mark this window as responding to close requests. let mut delete_window_atom = xlib::XInternAtom( display.as_ptr(), CStr::from_bytes_with_nul(b"WM_DELETE_WINDOW\0") .unwrap() .as_ptr(), 0, ); xlib::XSetWMProtocols(display.as_ptr(), window, &mut delete_window_atom, 1); let size_hints = xlib::XAllocSizeHints(); (*size_hints).flags = (xlib::PMinSize | xlib::PMaxSize) as i64; (*size_hints).max_width = width as i32; (*size_hints).min_width = width as i32; (*size_hints).max_height = height as i32; (*size_hints).min_height = height as i32; xlib::XSetWMNormalHints(display.as_ptr(), window, size_hints); x_free(size_hints); // We will use redraw the buffer when we are exposed. xlib::XSelectInput( display.as_ptr(), window, (xlib::ExposureMask | xlib::KeyPressMask | xlib::KeyReleaseMask | xlib::ButtonPressMask | xlib::ButtonReleaseMask | xlib::PointerMotionMask) as i64, ); xlib::XClearWindow(display.as_ptr(), window); xlib::XMapRaised(display.as_ptr(), window); // Flush everything so that the window is visible immediately. display.flush(); Ok(Surface { display, visual, depth, window, gc, width, height, event_devices: Default::default(), keycode_translator, buffers: Default::default(), buffer_next: 0, buffer_completion_type, delete_window_atom, close_requested: false, }) } } /// Returns index of the current (on-screen) buffer, or 0 if there are no buffers. fn current_buffer(&self) -> usize { match self.buffer_next.checked_sub(1) { Some(i) => i, None => self.buffers.len() - 1, } } fn dispatch_to_event_devices( &mut self, events: &[virtio_input_event], device_type: EventDeviceKind, ) { for event_device in self.event_devices.values_mut() { if event_device.kind() != device_type { continue; } if let Err(e) = event_device.send_report(events.iter().cloned()) { error!("error sending events to event device: {}", e); } } } fn handle_event(&mut self, ev: XEvent) { match ev.as_enum(self.buffer_completion_type) { XEventEnum::KeyEvent(key) => { if let Some(linux_keycode) = self.keycode_translator.translate(key.keycode) { let events = &[virtio_input_event::key( linux_keycode, key.type_ == xlib::KeyPress as i32, )]; self.dispatch_to_event_devices(events, EventDeviceKind::Keyboard); } } XEventEnum::ButtonEvent { event: button_event, pressed, } => { // We only support a single touch from button 1 (left mouse button). if button_event.button & xlib::Button1 != 0 { // The touch event *must* be first per the Linux input subsystem's guidance. let events = &[ virtio_input_event::touch(pressed), virtio_input_event::absolute_x(max(0, button_event.x) as u32), virtio_input_event::absolute_y(max(0, button_event.y) as u32), ]; self.dispatch_to_event_devices(events, EventDeviceKind::Touchscreen); } } XEventEnum::Motion(motion) => { if motion.state & xlib::Button1Mask != 0 { let events = &[ virtio_input_event::touch(true), virtio_input_event::absolute_x(max(0, motion.x) as u32), virtio_input_event::absolute_y(max(0, motion.y) as u32), ]; self.dispatch_to_event_devices(events, EventDeviceKind::Touchscreen); } } XEventEnum::Expose => self.draw_buffer(self.current_buffer()), XEventEnum::ClientMessage(xclient_data) => { if xclient_data == self.delete_window_atom { self.close_requested = true; } } XEventEnum::ShmCompletionEvent(shmseg) => { // Find the buffer associated with this event and mark it as not in use. for buffer_opt in self.buffers.iter_mut() { if let Some(buffer) = buffer_opt { if buffer.segment_info.shmseg == shmseg { buffer.in_use = false; } } } } XEventEnum::Unhandled => {} } } /// Draws the indicated buffer onto the screen. fn draw_buffer(&mut self, buffer_index: usize) { let buffer = match self.buffers.get_mut(buffer_index) { Some(Some(b)) => b, _ => { // If there is no buffer, that means the framebuffer was never set and we should // simply blank the window with arbitrary contents. unsafe { xlib::XClearWindow(self.display.as_ptr(), self.window); } return; } }; // Mark the buffer as in use. When the XShmCompletionEvent occurs, this will get marked // false. buffer.in_use = true; unsafe { xlib::XShmPutImage( self.display.as_ptr(), self.window, self.gc, buffer.image, 0, // src x 0, // src y 0, // dst x 0, // dst y self.width, self.height, true as i32, /* send XShmCompletionEvent event */ ); self.display.flush(); } } /// Gets the buffer at buffer_index, allocating it if necessary. fn lazily_allocate_buffer(&mut self, buffer_index: usize) -> Option<&Buffer> { if buffer_index >= self.buffers.len() { return None; } if self.buffers[buffer_index].is_some() { return self.buffers[buffer_index].as_ref(); } // The buffer_index is valid and the buffer was never created, so we create it now. unsafe { // The docs for XShmCreateImage imply that XShmSegmentInfo must be allocated to live at // least as long as the XImage, which probably means it can't move either. Use a Box in // order to fulfill those requirements. let mut segment_info: Box = Box::new(zeroed()); let image = xlib::XShmCreateImage( self.display.as_ptr(), self.visual, self.depth, xlib::ZPixmap as i32, null_mut(), segment_info.as_mut(), self.width, self.height, ); if image.is_null() { return None; } let size = (*image) .bytes_per_line .checked_mul((*image).height) .unwrap(); segment_info.shmid = shmget(IPC_PRIVATE, size as usize, IPC_CREAT | 0o777); if segment_info.shmid == -1 { xlib::XDestroyImage(image); return None; } segment_info.shmaddr = shmat(segment_info.shmid, null_mut(), 0) as *mut _; if segment_info.shmaddr == (-1isize) as *mut _ { xlib::XDestroyImage(image); shmctl(segment_info.shmid, IPC_RMID, null_mut()); return None; } (*image).data = segment_info.shmaddr; segment_info.readOnly = true as i32; xlib::XShmAttach(self.display.as_ptr(), segment_info.as_mut()); self.buffers[buffer_index] = Some(Buffer { display: self.display.clone(), image, segment_info, size: size as usize, in_use: false, }); self.buffers[buffer_index].as_ref() } } /// Gets the next framebuffer, allocating if necessary. fn framebuffer(&mut self) -> Option { // Framebuffers are lazily allocated. If the next buffer is not in self.buffers, add it // using push_new_buffer and then get its memory. let framebuffer = self.lazily_allocate_buffer(self.buffer_next)?; let bytes_per_pixel = framebuffer.bytes_per_pixel() as u32; Some(GpuDisplayFramebuffer::new( framebuffer.as_volatile_slice(), framebuffer.stride() as u32, bytes_per_pixel, )) } /// True if the next buffer is in use because of an XShmPutImage call. fn next_buffer_in_use(&self) -> bool { // Buffers that have not yet been made are not in use, hence unwrap_or(false). self.buffers .get(self.buffer_next) .and_then(|b| Some(b.as_ref()?.in_use)) .unwrap_or(false) } /// Puts the next buffer onto the screen and sets the next buffer in the swap chain. fn flip(&mut self) { let current_buffer_index = self.buffer_next; self.buffer_next = (self.buffer_next + 1) % self.buffers.len(); self.draw_buffer(current_buffer_index); } } impl Drop for Surface { fn drop(&mut self) { // Safe given it should always be of the correct type. unsafe { xlib::XFreeGC(self.display.as_ptr(), self.gc); xlib::XDestroyWindow(self.display.as_ptr(), self.window); } } } #[derive(PollToken)] enum DisplayXPollToken { Display, EventDevice { event_device_id: u32 }, } pub struct DisplayX { poll_ctx: PollContext, display: XDisplay, screen: XScreen, visual: *mut xlib::Visual, next_id: ObjectId, surfaces: BTreeMap, event_devices: BTreeMap, } impl DisplayX { pub fn open_display(display: Option<&str>) -> Result { let poll_ctx = PollContext::new().map_err(|_| GpuDisplayError::Allocate)?; let display_cstr = match display.map(CString::new) { Some(Ok(s)) => Some(s), Some(Err(_)) => return Err(GpuDisplayError::InvalidPath), None => None, }; unsafe { // Open the display let display = match NonNull::new(xlib::XOpenDisplay( display_cstr .as_ref() .map(|s| CStr::as_ptr(s)) .unwrap_or(null()), )) { Some(display_ptr) => XDisplay(Rc::new(display_ptr)), None => return Err(GpuDisplayError::Connect), }; poll_ctx .add(&display, DisplayXPollToken::Display) .map_err(|_| GpuDisplayError::Allocate)?; // Check for required extension. if !display.supports_shm() { return Err(GpuDisplayError::RequiredFeature("xshm extension")); } let screen = display .default_screen() .ok_or(GpuDisplayError::Connect) .unwrap(); let screen_number = screen.get_number(); // Check for and save required visual (24-bit BGR for the default screen). let mut visual_info_template = xlib::XVisualInfo { visual: null_mut(), visualid: 0, screen: screen_number, depth: 24, class: 0, red_mask: 0x00ff0000, green_mask: 0x0000ff00, blue_mask: 0x000000ff, colormap_size: 0, bits_per_rgb: 0, }; let visual_info = xlib::XGetVisualInfo( display.as_ptr(), (xlib::VisualScreenMask | xlib::VisualDepthMask | xlib::VisualRedMaskMask | xlib::VisualGreenMaskMask | xlib::VisualBlueMaskMask) as i64, &mut visual_info_template, &mut 0, ); if visual_info.is_null() { return Err(GpuDisplayError::RequiredFeature("no matching visual")); } let visual = (*visual_info).visual; x_free(visual_info); Ok(DisplayX { poll_ctx, display, screen, visual, next_id: ObjectId::new(1).unwrap(), surfaces: Default::default(), event_devices: Default::default(), }) } } fn surface_ref(&self, surface_id: u32) -> Option<&Surface> { ObjectId::new(surface_id).and_then(move |id| self.surfaces.get(&id)) } fn surface_mut(&mut self, surface_id: u32) -> Option<&mut Surface> { ObjectId::new(surface_id).and_then(move |id| self.surfaces.get_mut(&id)) } fn event_device(&self, event_device_id: u32) -> Option<&EventDevice> { ObjectId::new(event_device_id).and_then(move |id| self.event_devices.get(&id)) } fn event_device_mut(&mut self, event_device_id: u32) -> Option<&mut EventDevice> { ObjectId::new(event_device_id).and_then(move |id| self.event_devices.get_mut(&id)) } fn handle_event(&mut self, ev: XEvent) { let window = ev.window(); for surface in self.surfaces.values_mut() { if surface.window != window { continue; } surface.handle_event(ev); return; } } fn dispatch_display_events(&mut self) { loop { self.display.flush(); if !self.display.pending_events() { break; } let ev = self.display.next_event(); self.handle_event(ev); } } fn handle_event_device(&mut self, event_device_id: u32) { if let Some(event_device) = self.event_device(event_device_id) { // TODO(zachr): decode the event and forward to the device. let _ = event_device.recv_event_encoded(); } } fn handle_poll_ctx(&mut self) -> sys_util::Result<()> { let poll_events = self.poll_ctx.wait_timeout(Duration::default())?.to_owned(); for poll_event in poll_events.as_ref().iter_writable() { if let DisplayXPollToken::EventDevice { event_device_id } = poll_event.token() { if let Some(event_device) = self.event_device_mut(event_device_id) { if !event_device.flush_buffered_events()? { continue; } } // Although this looks exactly like the previous if-block, we need to reborrow self // as immutable in order to make use of self.poll_ctx. if let Some(event_device) = self.event_device(event_device_id) { self.poll_ctx.modify( event_device, WatchingEvents::empty().set_read(), DisplayXPollToken::EventDevice { event_device_id }, )?; } } } for poll_event in poll_events.as_ref().iter_readable() { match poll_event.token() { DisplayXPollToken::Display => self.dispatch_display_events(), DisplayXPollToken::EventDevice { event_device_id } => { self.handle_event_device(event_device_id) } } } Ok(()) } } impl DisplayT for DisplayX { fn dispatch_events(&mut self) { if let Err(e) = self.handle_poll_ctx() { error!("failed to dispatch events: {}", e); } } fn create_surface( &mut self, parent_surface_id: Option, width: u32, height: u32, ) -> Result { if parent_surface_id.is_some() { return Err(GpuDisplayError::Unsupported); } let new_surface = Surface::create( self.display.clone(), &self.screen, self.visual, width, height, )?; let new_surface_id = self.next_id; self.surfaces.insert(new_surface_id, new_surface); self.next_id = ObjectId::new(self.next_id.get() + 1).unwrap(); Ok(new_surface_id.get()) } fn release_surface(&mut self, surface_id: u32) { if let Some(mut surface) = ObjectId::new(surface_id).and_then(|id| self.surfaces.remove(&id)) { self.event_devices.append(&mut surface.event_devices); } } fn framebuffer(&mut self, surface_id: u32) -> Option { self.surface_mut(surface_id).and_then(|s| s.framebuffer()) } fn next_buffer_in_use(&self, surface_id: u32) -> bool { self.surface_ref(surface_id) .map(|s| s.next_buffer_in_use()) .unwrap_or(false) } fn flip(&mut self, surface_id: u32) { if let Some(surface) = self.surface_mut(surface_id) { surface.flip() } } fn close_requested(&self, surface_id: u32) -> bool { self.surface_ref(surface_id) .map(|s| s.close_requested) .unwrap_or(true) } #[allow(unused_variables)] fn import_dmabuf( &mut self, fd: RawFd, offset: u32, stride: u32, modifiers: u64, width: u32, height: u32, fourcc: u32, ) -> Result { Err(GpuDisplayError::Unsupported) } #[allow(unused_variables)] fn release_import(&mut self, import_id: u32) { // unsupported } #[allow(unused_variables)] fn commit(&mut self, surface_id: u32) { // unsupported } #[allow(unused_variables)] fn flip_to(&mut self, surface_id: u32, import_id: u32) { // unsupported } #[allow(unused_variables)] fn set_position(&mut self, surface_id: u32, x: u32, y: u32) { // unsupported } fn import_event_device(&mut self, event_device: EventDevice) -> Result { let new_event_device_id = self.next_id; self.poll_ctx .add( &event_device, DisplayXPollToken::EventDevice { event_device_id: new_event_device_id.get(), }, ) .map_err(|_| GpuDisplayError::Allocate)?; self.event_devices.insert(new_event_device_id, event_device); self.next_id = ObjectId::new(self.next_id.get() + 1).unwrap(); Ok(new_event_device_id.get()) } fn release_event_device(&mut self, event_device_id: u32) { ObjectId::new(event_device_id).and_then(|id| self.event_devices.remove(&id)); } fn attach_event_device(&mut self, surface_id: u32, event_device_id: u32) { let event_device_id = match ObjectId::new(event_device_id) { Some(id) => id, None => return, }; let surface_id = match ObjectId::new(surface_id) { Some(id) => id, None => return, }; let surface = self.surfaces.get_mut(&surface_id).unwrap(); let event_device = self.event_devices.remove(&event_device_id).unwrap(); surface.event_devices.insert(event_device_id, event_device); } } impl AsRawFd for DisplayX { fn as_raw_fd(&self) -> RawFd { self.poll_ctx.as_raw_fd() } }