summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--devices/src/virtio/gpu/backend.rs47
-rw-r--r--devices/src/virtio/gpu/mod.rs27
-rw-r--r--src/crosvm.rs4
-rw-r--r--src/linux.rs71
-rw-r--r--src/main.rs8
5 files changed, 132 insertions, 25 deletions
diff --git a/devices/src/virtio/gpu/backend.rs b/devices/src/virtio/gpu/backend.rs
index f13904c..8845612 100644
--- a/devices/src/virtio/gpu/backend.rs
+++ b/devices/src/virtio/gpu/backend.rs
@@ -11,6 +11,8 @@ use std::os::unix::io::AsRawFd;
 use std::rc::Rc;
 use std::usize;
 
+use libc::EINVAL;
+
 use data_model::*;
 
 use msg_socket::{MsgReceiver, MsgSender};
@@ -19,7 +21,8 @@ use sys_util::{error, GuestAddress, GuestMemory};
 
 use gpu_display::*;
 use gpu_renderer::{
-    Box3, Context as RendererContext, Renderer, Resource as GpuRendererResource, ResourceCreateArgs,
+    Box3, Context as RendererContext, Error as GpuRendererError, Renderer,
+    Resource as GpuRendererResource, ResourceCreateArgs,
 };
 
 use super::protocol::{
@@ -73,6 +76,7 @@ impl VirtioGpuResource {
 
         let (query, dmabuf) = match self.gpu_resource.export() {
             Ok(export) => (export.0, export.1),
+            Err(GpuRendererError::Virglrenderer(e)) if e == -EINVAL => return None,
             Err(e) => {
                 error!("failed to query resource: {}", e);
                 return None;
@@ -160,6 +164,8 @@ pub struct Backend {
     renderer: Renderer,
     resources: Map<u32, VirtioGpuResource>,
     contexts: Map<u32, RendererContext>,
+    // Maps event devices to scanout number.
+    event_devices: Map<u32, u32>,
     gpu_device_socket: VmMemoryControlRequestSocket,
     scanout_surface: Option<u32>,
     cursor_surface: Option<u32>,
@@ -187,9 +193,10 @@ impl Backend {
             display_width,
             display_height,
             renderer,
-            gpu_device_socket,
             resources: Default::default(),
             contexts: Default::default(),
+            event_devices: Default::default(),
+            gpu_device_socket,
             scanout_surface: None,
             cursor_surface: None,
             scanout_resource: 0,
@@ -203,6 +210,25 @@ impl Backend {
         &self.display
     }
 
+    pub fn import_event_device(&mut self, event_device: EventDevice, scanout: u32) {
+        // TODO(zachr): support more than one scanout.
+        if scanout != 0 {
+            return;
+        }
+
+        let mut display = self.display.borrow_mut();
+        let event_device_id = match display.import_event_device(event_device) {
+            Ok(id) => id,
+            Err(e) => {
+                error!("error importing event device: {}", e);
+                return;
+            }
+        };
+        self.scanout_surface
+            .map(|s| display.attach_event_device(s, event_device_id));
+        self.event_devices.insert(event_device_id, scanout);
+    }
+
     /// Processes the internal `display` events and returns `true` if the main display was closed.
     pub fn process_display(&mut self) -> bool {
         let mut display = self.display.borrow_mut();
@@ -281,9 +307,9 @@ impl Backend {
     }
 
     /// Sets the given resource id as the source of scanout to the display.
-    pub fn set_scanout(&mut self, id: u32) -> GpuResponse {
+    pub fn set_scanout(&mut self, _scanout_id: u32, resource_id: u32) -> GpuResponse {
         let mut display = self.display.borrow_mut();
-        if id == 0 {
+        if resource_id == 0 {
             if let Some(surface) = self.scanout_surface.take() {
                 display.release_surface(surface);
             }
@@ -293,12 +319,19 @@ impl Backend {
             }
             self.cursor_resource = 0;
             GpuResponse::OkNoData
-        } else if self.resources.get_mut(&id).is_some() {
-            self.scanout_resource = id;
+        } else if self.resources.get_mut(&resource_id).is_some() {
+            self.scanout_resource = resource_id;
 
             if self.scanout_surface.is_none() {
                 match display.create_surface(None, self.display_width, self.display_height) {
-                    Ok(surface) => self.scanout_surface = Some(surface),
+                    Ok(surface) => {
+                        // TODO(zachr): do not assume every event device belongs to this scanout_id;
+                        // in other words, support multiple display outputs.
+                        for &event_device in self.event_devices.keys() {
+                            display.attach_event_device(surface, event_device);
+                        }
+                        self.scanout_surface = Some(surface);
+                    }
                     Err(e) => error!("failed to create display surface: {}", e),
                 }
             }
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index 896e2ca..637f22b 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -23,6 +23,7 @@ use sys_util::{
     debug, error, warn, Error, EventFd, GuestAddress, GuestMemory, PollContext, PollToken,
 };
 
+pub use gpu_display::EventDevice;
 use gpu_display::*;
 use gpu_renderer::{Renderer, RendererFlags};
 
@@ -119,7 +120,9 @@ impl Frontend {
             GpuCommand::ResourceUnref(info) => {
                 self.backend.unref_resource(info.resource_id.to_native())
             }
-            GpuCommand::SetScanout(info) => self.backend.set_scanout(info.resource_id.to_native()),
+            GpuCommand::SetScanout(info) => self
+                .backend
+                .set_scanout(info.scanout_id.to_native(), info.resource_id.to_native()),
             GpuCommand::ResourceFlush(info) => self.backend.flush_resource(
                 info.resource_id.to_native(),
                 info.r.x.to_native(),
@@ -672,6 +675,7 @@ fn build_backend(
     possible_displays: &[DisplayBackend],
     display_width: u32,
     display_height: u32,
+    event_devices: Vec<EventDevice>,
     gpu_device_socket: VmMemoryControlRequestSocket,
     pci_bar: Alloc,
 ) -> Option<Backend> {
@@ -717,22 +721,29 @@ fn build_backend(
         }
     };
 
-    Some(Backend::new(
+    let mut backend = Backend::new(
         display,
         display_width,
         display_height,
         renderer,
         gpu_device_socket,
         pci_bar,
-    ))
+    );
+
+    for event_device in event_devices {
+        backend.import_event_device(event_device, 0);
+    }
+
+    Some(backend)
 }
 
 pub struct Gpu {
-    config_event: bool,
     exit_evt: EventFd,
     gpu_device_socket: Option<VmMemoryControlRequestSocket>,
     resource_bridges: Vec<ResourceResponseSocket>,
+    event_devices: Vec<EventDevice>,
     kill_evt: Option<EventFd>,
+    config_event: bool,
     worker_thread: Option<thread::JoinHandle<()>>,
     num_scanouts: NonZeroU8,
     display_backends: Vec<DisplayBackend>,
@@ -749,15 +760,17 @@ impl Gpu {
         resource_bridges: Vec<ResourceResponseSocket>,
         display_backends: Vec<DisplayBackend>,
         gpu_parameters: &GpuParameters,
+        event_devices: Vec<EventDevice>,
     ) -> Gpu {
         Gpu {
-            config_event: false,
             exit_evt,
             gpu_device_socket,
+            num_scanouts,
             resource_bridges,
+            event_devices,
+            config_event: false,
             kill_evt: None,
             worker_thread: None,
-            num_scanouts,
             display_backends,
             display_width: gpu_parameters.display_width,
             display_height: gpu_parameters.display_height,
@@ -881,6 +894,7 @@ impl VirtioDevice for Gpu {
         let display_backends = self.display_backends.clone();
         let display_width = self.display_width;
         let display_height = self.display_height;
+        let event_devices = self.event_devices.split_off(0);
         if let (Some(gpu_device_socket), Some(pci_bar)) =
             (self.gpu_device_socket.take(), self.pci_bar.take())
         {
@@ -892,6 +906,7 @@ impl VirtioDevice for Gpu {
                             &display_backends,
                             display_width,
                             display_height,
+                            event_devices,
                             gpu_device_socket,
                             pci_bar,
                         ) {
diff --git a/src/crosvm.rs b/src/crosvm.rs
index 0e0e218..8a15c6b 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -155,6 +155,8 @@ pub struct Config {
     pub cras_audio: bool,
     pub cras_capture: bool,
     pub null_audio: bool,
+    pub display_window_keyboard: bool,
+    pub display_window_mouse: bool,
     pub serial_parameters: BTreeMap<u8, SerialParameters>,
     pub syslog_tag: Option<String>,
     pub virtio_single_touch: Option<TouchDeviceOption>,
@@ -194,6 +196,8 @@ impl Default for Config {
             wayland_socket_path: None,
             wayland_dmabuf: false,
             x_display: None,
+            display_window_keyboard: false,
+            display_window_mouse: false,
             shared_dirs: Vec::new(),
             sandbox: !cfg!(feature = "default-no-sandbox"),
             seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
diff --git a/src/linux.rs b/src/linux.rs
index a9a3445..57b3ef5 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -28,6 +28,8 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
 use libc::{self, c_int, gid_t, uid_t};
 
 use audio_streams::DummyStreamSource;
+#[cfg(feature = "gpu")]
+use devices::virtio::EventDevice;
 use devices::virtio::{self, VirtioDevice};
 use devices::{
     self, HostBackendDeviceProvider, PciDevice, VfioDevice, VfioPciDevice, VirtioPciDevice,
@@ -456,7 +458,7 @@ fn create_tpm_device(cfg: &Config) -> DeviceResult {
 }
 
 fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOption) -> DeviceResult {
-    let socket = create_input_socket(&single_touch_spec.path).map_err(|e| {
+    let socket = single_touch_spec.path.into_unix_stream().map_err(|e| {
         error!("failed configuring virtio single touch: {:?}", e);
         e
     })?;
@@ -470,7 +472,7 @@ fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOptio
 }
 
 fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> DeviceResult {
-    let socket = create_input_socket(&trackpad_spec.path).map_err(|e| {
+    let socket = trackpad_spec.path.into_unix_stream().map_err(|e| {
         error!("failed configuring virtio trackpad: {}", e);
         e
     })?;
@@ -484,8 +486,8 @@ fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> De
     })
 }
 
-fn create_mouse_device(cfg: &Config, mouse_socket: &Path) -> DeviceResult {
-    let socket = create_input_socket(&mouse_socket).map_err(|e| {
+fn create_mouse_device<T: IntoUnixStream>(cfg: &Config, mouse_socket: T) -> DeviceResult {
+    let socket = mouse_socket.into_unix_stream().map_err(|e| {
         error!("failed configuring virtio mouse: {}", e);
         e
     })?;
@@ -498,8 +500,8 @@ fn create_mouse_device(cfg: &Config, mouse_socket: &Path) -> DeviceResult {
     })
 }
 
-fn create_keyboard_device(cfg: &Config, keyboard_socket: &Path) -> DeviceResult {
-    let socket = create_input_socket(&keyboard_socket).map_err(|e| {
+fn create_keyboard_device<T: IntoUnixStream>(cfg: &Config, keyboard_socket: T) -> DeviceResult {
+    let socket = keyboard_socket.into_unix_stream().map_err(|e| {
         error!("failed configuring virtio keyboard: {}", e);
         e
     })?;
@@ -589,6 +591,7 @@ fn create_gpu_device(
     gpu_sockets: Vec<virtio::resource_bridge::ResourceResponseSocket>,
     wayland_socket_path: Option<PathBuf>,
     x_display: Option<String>,
+    event_devices: Vec<EventDevice>,
 ) -> DeviceResult {
     let jailed_wayland_path = Path::new("/wayland-0");
 
@@ -615,6 +618,7 @@ fn create_gpu_device(
         gpu_sockets,
         display_backends,
         cfg.gpu_parameters.as_ref().unwrap(),
+        event_devices,
     );
 
     let jail = match simple_jail(&cfg, "gpu_device.policy")? {
@@ -986,6 +990,31 @@ fn create_virtio_devices(
     #[cfg(feature = "gpu")]
     {
         if cfg.gpu_parameters.is_some() {
+            let mut event_devices = Vec::new();
+            if cfg.display_window_mouse {
+                let (event_device_socket, virtio_dev_socket) =
+                    UnixStream::pair().map_err(Error::CreateSocket)?;
+                // TODO(nkgold): the width/height here should match the display's height/width. When
+                // those settings are available as CLI options, we should use the CLI options here
+                // as well.
+                let dev = virtio::new_single_touch(virtio_dev_socket, 1280, 1024)
+                    .map_err(Error::InputDeviceNew)?;
+                devs.push(VirtioDeviceStub {
+                    dev: Box::new(dev),
+                    jail: simple_jail(&cfg, "input_device.policy")?,
+                });
+                event_devices.push(EventDevice::touchscreen(event_device_socket));
+            }
+            if cfg.display_window_keyboard {
+                let (event_device_socket, virtio_dev_socket) =
+                    UnixStream::pair().map_err(Error::CreateSocket)?;
+                let dev = virtio::new_keyboard(virtio_dev_socket).map_err(Error::InputDeviceNew)?;
+                devs.push(VirtioDeviceStub {
+                    dev: Box::new(dev),
+                    jail: simple_jail(&cfg, "input_device.policy")?,
+                });
+                event_devices.push(EventDevice::keyboard(event_device_socket));
+            }
             devs.push(create_gpu_device(
                 cfg,
                 _exit_evt,
@@ -993,6 +1022,7 @@ fn create_virtio_devices(
                 resource_bridges,
                 cfg.wayland_socket_path.clone(),
                 cfg.x_display.clone(),
+                event_devices,
             )?);
         }
     }
@@ -1160,12 +1190,29 @@ fn raw_fd_from_path(path: &Path) -> Result<RawFd> {
     validate_raw_fd(raw_fd).map_err(Error::ValidateRawFd)
 }
 
-fn create_input_socket(path: &Path) -> Result<UnixStream> {
-    if path.parent() == Some(Path::new("/proc/self/fd")) {
-        // Safe because we will validate |raw_fd|.
-        unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(path)?)) }
-    } else {
-        UnixStream::connect(path).map_err(Error::InputEventsOpen)
+trait IntoUnixStream {
+    fn into_unix_stream(self) -> Result<UnixStream>;
+}
+
+impl<'a> IntoUnixStream for &'a Path {
+    fn into_unix_stream(self) -> Result<UnixStream> {
+        if self.parent() == Some(Path::new("/proc/self/fd")) {
+            // Safe because we will validate |raw_fd|.
+            unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(self)?)) }
+        } else {
+            UnixStream::connect(self).map_err(Error::InputEventsOpen)
+        }
+    }
+}
+impl<'a> IntoUnixStream for &'a PathBuf {
+    fn into_unix_stream(self) -> Result<UnixStream> {
+        self.as_path().into_unix_stream()
+    }
+}
+
+impl IntoUnixStream for UnixStream {
+    fn into_unix_stream(self) -> Result<UnixStream> {
+        Ok(self)
     }
 }
 
diff --git a/src/main.rs b/src/main.rs
index 9c97c4d..7f12841 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -511,6 +511,12 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             }
             cfg.x_display = Some(value.unwrap().to_owned());
         }
+        "display-window-keyboard" => {
+            cfg.display_window_keyboard = true;
+        }
+        "display-window-mouse" => {
+            cfg.display_window_mouse = true;
+        }
         "socket" => {
             if cfg.socket_path.is_some() {
                 return Err(argument::Error::TooManyArguments(
@@ -931,6 +937,8 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
                           "),
           Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
           Argument::value("x-display", "DISPLAY", "X11 display name to use."),
+          Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
+          Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
           Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
           #[cfg(feature = "wl-dmabuf")]
           Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),