diff --git a/wlx-overlay-s/Cargo.toml b/wlx-overlay-s/Cargo.toml index eb70858..362cd3f 100644 --- a/wlx-overlay-s/Cargo.toml +++ b/wlx-overlay-s/Cargo.toml @@ -93,6 +93,7 @@ xkbcommon = { version = "0.9.0" } ################################ smithay = { version = "0.7.0", default-features = false, features = [ "backend_vulkan", + "desktop", "xwayland", "wayland_frontend", ], optional = true } diff --git a/wlx-overlay-s/src/backend/wayvr/comp.rs b/wlx-overlay-s/src/backend/wayvr/comp.rs index 773cb9d..3d533f2 100644 --- a/wlx-overlay-s/src/backend/wayvr/comp.rs +++ b/wlx-overlay-s/src/backend/wayvr/comp.rs @@ -1,5 +1,7 @@ +use anyhow::Context; use smithay::backend::allocator::dmabuf::Dmabuf; use smithay::backend::renderer::{BufferType, buffer_type}; +use smithay::desktop::{PopupKind, PopupManager}; use smithay::input::{Seat, SeatHandler, SeatState}; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; use smithay::reexports::wayland_server; @@ -9,6 +11,7 @@ use smithay::wayland::buffer::BufferHandler; use smithay::wayland::dmabuf::{ DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf, }; +use smithay::wayland::fractional_scale::with_fractional_scale; use smithay::wayland::output::OutputHandler; use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents}; use smithay::wayland::single_pixel_buffer::get_single_pixel_buffer; @@ -21,7 +24,7 @@ use std::os::fd::OwnedFd; use std::sync::{Arc, Mutex}; use smithay::utils::Serial; -use smithay::wayland::compositor::{self, BufferAssignment, SurfaceAttributes}; +use smithay::wayland::compositor::{self, BufferAssignment, SurfaceAttributes, send_surface_state}; use smithay::wayland::selection::SelectionHandler; use smithay::wayland::selection::data_device::{ @@ -50,6 +53,7 @@ pub struct Application { pub data_device: DataDeviceState, pub wayvr_tasks: SyncEventQueue, pub redraw_requests: HashSet, + pub popup_manager: PopupManager, } impl Application { @@ -60,6 +64,34 @@ impl Application { pub fn cleanup(&mut self) { self.image_importer.cleanup(); } + + fn popups_commit(&mut self, surface: &WlSurface) { + self.popup_manager.commit(surface); + + if let Some(popup) = self.popup_manager.find_popup(surface) { + match popup { + PopupKind::Xdg(ref popup) => { + if !popup.is_initial_configure_sent() { + smithay::wayland::compositor::with_states(surface, |states| { + send_surface_state( + surface, + states, + 1, + smithay::utils::Transform::Normal, + ); + with_fractional_scale(states, |fractional| { + fractional.set_preferred_scale(1.0); + }); + }); + popup.send_configure().expect("initial configure failed"); + } + } + PopupKind::InputMethod(_) => { + // TODO? + } + } + } + } } impl compositor::CompositorHandler for Application { @@ -76,6 +108,8 @@ impl compositor::CompositorHandler for Application { #[allow(clippy::significant_drop_tightening)] fn commit(&mut self, surface: &WlSurface) { + self.popups_commit(surface); + smithay::wayland::compositor::with_states(surface, |states| { let mut guard = states.cached_state.get::(); let attrs = guard.current(); @@ -247,8 +281,16 @@ impl XdgShellHandler for Application { } } - fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) { - // Handle popup creation here + fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) { + let _ = self + .popup_manager + .track_popup(PopupKind::Xdg(surface)) + .context("Could not track xdg_popup") + .inspect_err(|e| log::warn!("{e:?}")); + } + + fn popup_destroyed(&mut self, _surface: PopupSurface) { + self.popup_manager.cleanup(); } fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) { diff --git a/wlx-overlay-s/src/backend/wayvr/mod.rs b/wlx-overlay-s/src/backend/wayvr/mod.rs index b7c1236..21f80b8 100644 --- a/wlx-overlay-s/src/backend/wayvr/mod.rs +++ b/wlx-overlay-s/src/backend/wayvr/mod.rs @@ -11,16 +11,13 @@ use process::ProcessVec; use slotmap::SecondaryMap; use smallvec::SmallVec; use smithay::{ - input::{SeatState, keyboard::XkbConfig}, - output::{Mode, Output}, - reexports::wayland_server::{self, backend::ClientId}, - wayland::{ - compositor::{self, SurfaceData, with_states}, + desktop::PopupManager, input::{keyboard::XkbConfig, SeatState}, output::{Mode, Output}, reexports::wayland_server::{self, backend::ClientId}, wayland::{ + compositor::{self, with_states, SurfaceData}, dmabuf::{DmabufFeedbackBuilder, DmabufState}, selection::data_device::DataDeviceState, shell::xdg::{ToplevelSurface, XdgShellState, XdgToplevelSurfaceData}, shm::ShmState, - }, + } }; use std::{ cell::RefCell, @@ -226,6 +223,7 @@ impl WvrServerState { wayvr_tasks: tasks.clone(), redraw_requests: HashSet::new(), dmabuf_state, + popup_manager: PopupManager::default(), }; let time_start = get_millis(); @@ -319,11 +317,11 @@ impl WvrServerState { app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create( OverlaySelector::Nothing, Box::new(move |app: &mut AppState| { - Some(create_wl_window_overlay( + create_wl_window_overlay( title, - app.xr_backend, + app, window_handle, - )) + ).context("Could not create WvrWindow overlay").inspect_err(|e| log::warn!("{e:?}")).ok() }), ))); @@ -549,7 +547,6 @@ pub struct SurfaceBufWithImage { } impl SurfaceBufWithImage { - #[allow(invalid_value)] fn apply_to_surface(self, surface_data: &SurfaceData) { if let Some(container) = surface_data.data_map.get::() { container.inner.replace(self); diff --git a/wlx-overlay-s/src/overlays/screen/capture.rs b/wlx-overlay-s/src/overlays/screen/capture.rs index b56eb90..7c9f105 100644 --- a/wlx-overlay-s/src/overlays/screen/capture.rs +++ b/wlx-overlay-s/src/overlays/screen/capture.rs @@ -233,6 +233,10 @@ impl ScreenPipeline { Ok(()) } + + pub fn get_alpha_buf(&self) -> Subbuffer<[f32]> { + self.buf_alpha.clone() + } } fn stereo_mode_to_verts(stereo: StereoMode, array_index: usize) -> [Vert2Uv; 4] { diff --git a/wlx-overlay-s/src/overlays/wayvr.rs b/wlx-overlay-s/src/overlays/wayvr.rs index 881f8be..96d6eaa 100644 --- a/wlx-overlay-s/src/overlays/wayvr.rs +++ b/wlx-overlay-s/src/overlays/wayvr.rs @@ -1,7 +1,13 @@ -use glam::{Affine2, Affine3A, Quat, Vec3, vec3}; -use smithay::wayland::compositor::with_states; +use glam::{Affine2, Affine3A, Quat, Vec2, Vec3, vec2, vec3}; +use smithay::{ + desktop::PopupManager, + wayland::{compositor::with_states, shell::xdg::XdgPopupSurfaceData}, +}; use std::sync::Arc; -use vulkano::image::view::ImageView; +use vulkano::{ + buffer::BufferUsage, image::view::ImageView, pipeline::graphics::color_blend::AttachmentBlend, +}; +use wgui::gfx::pipeline::{WGfxPipeline, WPipelineCreateInfo}; use wlx_capture::frame::MouseMeta; use wlx_common::{ overlays::{BackendAttrib, BackendAttribValue, StereoMode}, @@ -14,7 +20,7 @@ use crate::{ input::{self, HoverResult}, wayvr::{self, SurfaceBufWithImage}, }, - graphics::ExtentExt, + graphics::{ExtentExt, Vert2Uv, upload_quad_vertices}, overlays::screen::capture::ScreenPipeline, state::{self, AppState}, subsystem::{hid::WheelDelta, input::KeyboardFocus}, @@ -29,10 +35,10 @@ use crate::{ pub fn create_wl_window_overlay( name: Arc, - xr_backend: XrBackend, + app: &AppState, window: wayvr::window::WindowHandle, -) -> OverlayWindowConfig { - OverlayWindowConfig { +) -> anyhow::Result { + Ok(OverlayWindowConfig { name: name.clone(), default_state: OverlayWindowState { grabbable: true, @@ -49,17 +55,17 @@ pub fn create_wl_window_overlay( keyboard_focus: Some(KeyboardFocus::WayVR), category: OverlayCategory::WayVR, show_on_spawn: true, - ..OverlayWindowConfig::from_backend(Box::new(WvrWindowBackend::new( - name, xr_backend, window, - ))) - } + ..OverlayWindowConfig::from_backend(Box::new(WvrWindowBackend::new(name, app, window)?)) + }) } pub struct WvrWindowBackend { name: Arc, pipeline: Option, + popups_pipeline: Arc>, interaction_transform: Option, window: wayvr::window::WindowHandle, + popups: Vec<(Arc, Vec2)>, just_resumed: bool, meta: Option, mouse: Option, @@ -68,26 +74,34 @@ pub struct WvrWindowBackend { } impl WvrWindowBackend { - const fn new( + fn new( name: Arc, - xr_backend: XrBackend, + app: &AppState, window: wayvr::window::WindowHandle, - ) -> Self { - Self { + ) -> anyhow::Result { + let popups_pipeline = app.gfx.create_pipeline( + app.gfx_extras.shaders.get("vert_quad").unwrap(), // want panic + app.gfx_extras.shaders.get("frag_screen").unwrap(), // want panic + WPipelineCreateInfo::new(app.gfx.surface_format).use_blend(AttachmentBlend::default()), + )?; + + Ok(Self { name, pipeline: None, window, + popups: vec![], + popups_pipeline, interaction_transform: None, just_resumed: false, meta: None, mouse: None, - stereo: if matches!(xr_backend, XrBackend::OpenXR) { + stereo: if matches!(app.xr_backend, XrBackend::OpenXR) { Some(StereoMode::None) } else { None }, cur_image: None, - } + }) } } @@ -119,6 +133,30 @@ impl OverlayBackend for WvrWindowBackend { return Ok(ShouldRender::Unable); }; + let popups = PopupManager::popups_for_surface(toplevel.wl_surface()) + .filter_map(|(popup, point)| { + with_states(popup.wl_surface(), |states| { + if !states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .configured + { + // not yet configured + return None; + } + + if let Some(surf) = SurfaceBufWithImage::get_from_surface(states) { + Some((surf.image, vec2(point.x as _, point.y as _))) + } else { + None + } + }) + }) + .collect::>(); + with_states(toplevel.wl_surface(), |states| { if let Some(surf) = SurfaceBufWithImage::get_from_surface(states) { let mut meta = FrameMeta { @@ -158,8 +196,9 @@ impl OverlayBackend for WvrWindowBackend { y: (m.y as f32) / (meta.extent[1] as f32), }); - let mouse_dirty = self.mouse != mouse; + let dirty = self.mouse != mouse || self.popups != popups; self.mouse = mouse; + self.popups = popups; self.meta = Some(meta); if self .cur_image @@ -173,7 +212,7 @@ impl OverlayBackend for WvrWindowBackend { ); self.cur_image = Some(surf.image); Ok(ShouldRender::Should) - } else if mouse_dirty { + } else if dirty { Ok(ShouldRender::Should) } else { Ok(ShouldRender::Can) @@ -197,6 +236,44 @@ impl OverlayBackend for WvrWindowBackend { .unwrap() .render(image, self.mouse.as_ref(), app, rdr)?; + for (popup_img, point) in &self.popups { + let extentf = self.meta.as_ref().unwrap().extent.extent_f32(); + let popup_extentf = popup_img.extent_f32(); + let mut buf_vert = app + .gfx + .empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::VERTEX_BUFFER, 4)?; + + upload_quad_vertices( + &mut buf_vert, + extentf[0], + extentf[1], + point.x, + point.y, + popup_extentf[0], + popup_extentf[1], + )?; + + let set0 = self.popups_pipeline.uniform_sampler( + 0, + popup_img.clone(), + app.gfx.texture_filter, + )?; + let set1 = self + .popups_pipeline + .buffer(1, self.pipeline.as_ref().unwrap().get_alpha_buf())?; + + let pass = self.popups_pipeline.create_pass( + extentf, + buf_vert, + 0..4, + 0..1, + vec![set0, set1], + &Default::default(), + )?; + + rdr.cmd_buf_single().run_ref(&pass)?; + } + Ok(()) }