| // 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; |
| 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>(t: *mut T) { |
| xlib::XFree(t as *mut c_void); |
| } |
| |
| #[derive(Clone)] |
| struct XDisplay(Rc<NonNull<xlib::Display>>); |
| 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<XScreen> { |
| 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<xlib::XEvent> 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<xlib::Screen>); |
| |
| 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<xlib::XShmSegmentInfo>, |
| 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<ObjectId, EventDevice>, |
| keycode_translator: KeycodeTranslator, |
| |
| // Fields for handling the buffer swap chain. |
| buffers: [Option<Buffer>; 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<Surface, GpuDisplayError> { |
| 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<xlib::XShmSegmentInfo> = 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<GpuDisplayFramebuffer> { |
| // 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<DisplayXPollToken>, |
| display: XDisplay, |
| screen: XScreen, |
| visual: *mut xlib::Visual, |
| next_id: ObjectId, |
| surfaces: BTreeMap<ObjectId, Surface>, |
| event_devices: BTreeMap<ObjectId, EventDevice>, |
| } |
| |
| impl DisplayX { |
| pub fn open_display(display: Option<&str>) -> Result<DisplayX, GpuDisplayError> { |
| 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<u32>, |
| width: u32, |
| height: u32, |
| ) -> Result<u32, GpuDisplayError> { |
| 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<GpuDisplayFramebuffer> { |
| 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<u32, GpuDisplayError> { |
| 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<u32, GpuDisplayError> { |
| 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() |
| } |
| } |