implement xdg_popup support
This commit is contained in:
@@ -93,6 +93,7 @@ xkbcommon = { version = "0.9.0" }
|
|||||||
################################
|
################################
|
||||||
smithay = { version = "0.7.0", default-features = false, features = [
|
smithay = { version = "0.7.0", default-features = false, features = [
|
||||||
"backend_vulkan",
|
"backend_vulkan",
|
||||||
|
"desktop",
|
||||||
"xwayland",
|
"xwayland",
|
||||||
"wayland_frontend",
|
"wayland_frontend",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
use anyhow::Context;
|
||||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||||
use smithay::backend::renderer::{BufferType, buffer_type};
|
use smithay::backend::renderer::{BufferType, buffer_type};
|
||||||
|
use smithay::desktop::{PopupKind, PopupManager};
|
||||||
use smithay::input::{Seat, SeatHandler, SeatState};
|
use smithay::input::{Seat, SeatHandler, SeatState};
|
||||||
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||||
use smithay::reexports::wayland_server;
|
use smithay::reexports::wayland_server;
|
||||||
@@ -9,6 +11,7 @@ use smithay::wayland::buffer::BufferHandler;
|
|||||||
use smithay::wayland::dmabuf::{
|
use smithay::wayland::dmabuf::{
|
||||||
DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf,
|
DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf,
|
||||||
};
|
};
|
||||||
|
use smithay::wayland::fractional_scale::with_fractional_scale;
|
||||||
use smithay::wayland::output::OutputHandler;
|
use smithay::wayland::output::OutputHandler;
|
||||||
use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents};
|
use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents};
|
||||||
use smithay::wayland::single_pixel_buffer::get_single_pixel_buffer;
|
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 std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use smithay::utils::Serial;
|
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::SelectionHandler;
|
||||||
use smithay::wayland::selection::data_device::{
|
use smithay::wayland::selection::data_device::{
|
||||||
@@ -50,6 +53,7 @@ pub struct Application {
|
|||||||
pub data_device: DataDeviceState,
|
pub data_device: DataDeviceState,
|
||||||
pub wayvr_tasks: SyncEventQueue<WayVRTask>,
|
pub wayvr_tasks: SyncEventQueue<WayVRTask>,
|
||||||
pub redraw_requests: HashSet<wayland_server::backend::ObjectId>,
|
pub redraw_requests: HashSet<wayland_server::backend::ObjectId>,
|
||||||
|
pub popup_manager: PopupManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
@@ -60,6 +64,34 @@ impl Application {
|
|||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
self.image_importer.cleanup();
|
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 {
|
impl compositor::CompositorHandler for Application {
|
||||||
@@ -76,6 +108,8 @@ impl compositor::CompositorHandler for Application {
|
|||||||
|
|
||||||
#[allow(clippy::significant_drop_tightening)]
|
#[allow(clippy::significant_drop_tightening)]
|
||||||
fn commit(&mut self, surface: &WlSurface) {
|
fn commit(&mut self, surface: &WlSurface) {
|
||||||
|
self.popups_commit(surface);
|
||||||
|
|
||||||
smithay::wayland::compositor::with_states(surface, |states| {
|
smithay::wayland::compositor::with_states(surface, |states| {
|
||||||
let mut guard = states.cached_state.get::<SurfaceAttributes>();
|
let mut guard = states.cached_state.get::<SurfaceAttributes>();
|
||||||
let attrs = guard.current();
|
let attrs = guard.current();
|
||||||
@@ -247,8 +281,16 @@ impl XdgShellHandler for Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) {
|
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
|
||||||
// Handle popup creation here
|
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) {
|
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
|
||||||
|
|||||||
@@ -11,16 +11,13 @@ use process::ProcessVec;
|
|||||||
use slotmap::SecondaryMap;
|
use slotmap::SecondaryMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
input::{SeatState, keyboard::XkbConfig},
|
desktop::PopupManager, input::{keyboard::XkbConfig, SeatState}, output::{Mode, Output}, reexports::wayland_server::{self, backend::ClientId}, wayland::{
|
||||||
output::{Mode, Output},
|
compositor::{self, with_states, SurfaceData},
|
||||||
reexports::wayland_server::{self, backend::ClientId},
|
|
||||||
wayland::{
|
|
||||||
compositor::{self, SurfaceData, with_states},
|
|
||||||
dmabuf::{DmabufFeedbackBuilder, DmabufState},
|
dmabuf::{DmabufFeedbackBuilder, DmabufState},
|
||||||
selection::data_device::DataDeviceState,
|
selection::data_device::DataDeviceState,
|
||||||
shell::xdg::{ToplevelSurface, XdgShellState, XdgToplevelSurfaceData},
|
shell::xdg::{ToplevelSurface, XdgShellState, XdgToplevelSurfaceData},
|
||||||
shm::ShmState,
|
shm::ShmState,
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
@@ -226,6 +223,7 @@ impl WvrServerState {
|
|||||||
wayvr_tasks: tasks.clone(),
|
wayvr_tasks: tasks.clone(),
|
||||||
redraw_requests: HashSet::new(),
|
redraw_requests: HashSet::new(),
|
||||||
dmabuf_state,
|
dmabuf_state,
|
||||||
|
popup_manager: PopupManager::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let time_start = get_millis();
|
let time_start = get_millis();
|
||||||
@@ -319,11 +317,11 @@ impl WvrServerState {
|
|||||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create(
|
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create(
|
||||||
OverlaySelector::Nothing,
|
OverlaySelector::Nothing,
|
||||||
Box::new(move |app: &mut AppState| {
|
Box::new(move |app: &mut AppState| {
|
||||||
Some(create_wl_window_overlay(
|
create_wl_window_overlay(
|
||||||
title,
|
title,
|
||||||
app.xr_backend,
|
app,
|
||||||
window_handle,
|
window_handle,
|
||||||
))
|
).context("Could not create WvrWindow overlay").inspect_err(|e| log::warn!("{e:?}")).ok()
|
||||||
}),
|
}),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
@@ -549,7 +547,6 @@ pub struct SurfaceBufWithImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceBufWithImage {
|
impl SurfaceBufWithImage {
|
||||||
#[allow(invalid_value)]
|
|
||||||
fn apply_to_surface(self, surface_data: &SurfaceData) {
|
fn apply_to_surface(self, surface_data: &SurfaceData) {
|
||||||
if let Some(container) = surface_data.data_map.get::<SurfaceBufWithImageContainer>() {
|
if let Some(container) = surface_data.data_map.get::<SurfaceBufWithImageContainer>() {
|
||||||
container.inner.replace(self);
|
container.inner.replace(self);
|
||||||
|
|||||||
@@ -233,6 +233,10 @@ impl ScreenPipeline {
|
|||||||
|
|
||||||
Ok(())
|
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] {
|
fn stereo_mode_to_verts(stereo: StereoMode, array_index: usize) -> [Vert2Uv; 4] {
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
use glam::{Affine2, Affine3A, Quat, Vec3, vec3};
|
use glam::{Affine2, Affine3A, Quat, Vec2, Vec3, vec2, vec3};
|
||||||
use smithay::wayland::compositor::with_states;
|
use smithay::{
|
||||||
|
desktop::PopupManager,
|
||||||
|
wayland::{compositor::with_states, shell::xdg::XdgPopupSurfaceData},
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
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_capture::frame::MouseMeta;
|
||||||
use wlx_common::{
|
use wlx_common::{
|
||||||
overlays::{BackendAttrib, BackendAttribValue, StereoMode},
|
overlays::{BackendAttrib, BackendAttribValue, StereoMode},
|
||||||
@@ -14,7 +20,7 @@ use crate::{
|
|||||||
input::{self, HoverResult},
|
input::{self, HoverResult},
|
||||||
wayvr::{self, SurfaceBufWithImage},
|
wayvr::{self, SurfaceBufWithImage},
|
||||||
},
|
},
|
||||||
graphics::ExtentExt,
|
graphics::{ExtentExt, Vert2Uv, upload_quad_vertices},
|
||||||
overlays::screen::capture::ScreenPipeline,
|
overlays::screen::capture::ScreenPipeline,
|
||||||
state::{self, AppState},
|
state::{self, AppState},
|
||||||
subsystem::{hid::WheelDelta, input::KeyboardFocus},
|
subsystem::{hid::WheelDelta, input::KeyboardFocus},
|
||||||
@@ -29,10 +35,10 @@ use crate::{
|
|||||||
|
|
||||||
pub fn create_wl_window_overlay(
|
pub fn create_wl_window_overlay(
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
xr_backend: XrBackend,
|
app: &AppState,
|
||||||
window: wayvr::window::WindowHandle,
|
window: wayvr::window::WindowHandle,
|
||||||
) -> OverlayWindowConfig {
|
) -> anyhow::Result<OverlayWindowConfig> {
|
||||||
OverlayWindowConfig {
|
Ok(OverlayWindowConfig {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
default_state: OverlayWindowState {
|
default_state: OverlayWindowState {
|
||||||
grabbable: true,
|
grabbable: true,
|
||||||
@@ -49,17 +55,17 @@ pub fn create_wl_window_overlay(
|
|||||||
keyboard_focus: Some(KeyboardFocus::WayVR),
|
keyboard_focus: Some(KeyboardFocus::WayVR),
|
||||||
category: OverlayCategory::WayVR,
|
category: OverlayCategory::WayVR,
|
||||||
show_on_spawn: true,
|
show_on_spawn: true,
|
||||||
..OverlayWindowConfig::from_backend(Box::new(WvrWindowBackend::new(
|
..OverlayWindowConfig::from_backend(Box::new(WvrWindowBackend::new(name, app, window)?))
|
||||||
name, xr_backend, window,
|
})
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WvrWindowBackend {
|
pub struct WvrWindowBackend {
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
pipeline: Option<ScreenPipeline>,
|
pipeline: Option<ScreenPipeline>,
|
||||||
|
popups_pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
||||||
interaction_transform: Option<Affine2>,
|
interaction_transform: Option<Affine2>,
|
||||||
window: wayvr::window::WindowHandle,
|
window: wayvr::window::WindowHandle,
|
||||||
|
popups: Vec<(Arc<ImageView>, Vec2)>,
|
||||||
just_resumed: bool,
|
just_resumed: bool,
|
||||||
meta: Option<FrameMeta>,
|
meta: Option<FrameMeta>,
|
||||||
mouse: Option<MouseMeta>,
|
mouse: Option<MouseMeta>,
|
||||||
@@ -68,26 +74,34 @@ pub struct WvrWindowBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WvrWindowBackend {
|
impl WvrWindowBackend {
|
||||||
const fn new(
|
fn new(
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
xr_backend: XrBackend,
|
app: &AppState,
|
||||||
window: wayvr::window::WindowHandle,
|
window: wayvr::window::WindowHandle,
|
||||||
) -> Self {
|
) -> anyhow::Result<Self> {
|
||||||
Self {
|
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,
|
name,
|
||||||
pipeline: None,
|
pipeline: None,
|
||||||
window,
|
window,
|
||||||
|
popups: vec![],
|
||||||
|
popups_pipeline,
|
||||||
interaction_transform: None,
|
interaction_transform: None,
|
||||||
just_resumed: false,
|
just_resumed: false,
|
||||||
meta: None,
|
meta: None,
|
||||||
mouse: None,
|
mouse: None,
|
||||||
stereo: if matches!(xr_backend, XrBackend::OpenXR) {
|
stereo: if matches!(app.xr_backend, XrBackend::OpenXR) {
|
||||||
Some(StereoMode::None)
|
Some(StereoMode::None)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
cur_image: None,
|
cur_image: None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +133,30 @@ impl OverlayBackend for WvrWindowBackend {
|
|||||||
return Ok(ShouldRender::Unable);
|
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::<XdgPopupSurfaceData>()
|
||||||
|
.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::<Vec<_>>();
|
||||||
|
|
||||||
with_states(toplevel.wl_surface(), |states| {
|
with_states(toplevel.wl_surface(), |states| {
|
||||||
if let Some(surf) = SurfaceBufWithImage::get_from_surface(states) {
|
if let Some(surf) = SurfaceBufWithImage::get_from_surface(states) {
|
||||||
let mut meta = FrameMeta {
|
let mut meta = FrameMeta {
|
||||||
@@ -158,8 +196,9 @@ impl OverlayBackend for WvrWindowBackend {
|
|||||||
y: (m.y as f32) / (meta.extent[1] as f32),
|
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.mouse = mouse;
|
||||||
|
self.popups = popups;
|
||||||
self.meta = Some(meta);
|
self.meta = Some(meta);
|
||||||
if self
|
if self
|
||||||
.cur_image
|
.cur_image
|
||||||
@@ -173,7 +212,7 @@ impl OverlayBackend for WvrWindowBackend {
|
|||||||
);
|
);
|
||||||
self.cur_image = Some(surf.image);
|
self.cur_image = Some(surf.image);
|
||||||
Ok(ShouldRender::Should)
|
Ok(ShouldRender::Should)
|
||||||
} else if mouse_dirty {
|
} else if dirty {
|
||||||
Ok(ShouldRender::Should)
|
Ok(ShouldRender::Should)
|
||||||
} else {
|
} else {
|
||||||
Ok(ShouldRender::Can)
|
Ok(ShouldRender::Can)
|
||||||
@@ -197,6 +236,44 @@ impl OverlayBackend for WvrWindowBackend {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.render(image, self.mouse.as_ref(), app, rdr)?;
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user