diff --git a/wgui/src/drawing.rs b/wgui/src/drawing.rs index df83fb8..2a6aad9 100644 --- a/wgui/src/drawing.rs +++ b/wgui/src/drawing.rs @@ -10,8 +10,8 @@ use crate::{ globals::Globals, layout::Widget, renderer_vk::text::{ - TextShadow, custom_glyph::{CustomGlyph, CustomGlyphData}, + TextShadow, }, stack::{self, ScissorBoundary, ScissorStack, TransformStack}, widget::{self, ScrollbarInfo, WidgetState}, @@ -147,6 +147,11 @@ impl Color { let a = (self.a.clamp(0.0, 1.0) * 255.0).round() as u8; format!("#{r:02X}{g:02X}{b:02X}{a:02X}") } + + #[must_use] + pub fn as_arr(&self) -> [f32; 4] { + [self.r, self.b, self.g, self.a] + } } impl Default for Color { diff --git a/wgui/src/gfx/pass.rs b/wgui/src/gfx/pass.rs index 2dc3c0c..358774a 100644 --- a/wgui/src/gfx/pass.rs +++ b/wgui/src/gfx/pass.rs @@ -13,12 +13,12 @@ use vulkano::{ view::ImageView, }, pipeline::{ - Pipeline, PipelineBindPoint, graphics::{self, vertex_input::Vertex, viewport::Viewport}, + Pipeline, PipelineBindPoint, }, }; -use super::{WGfx, pipeline::WGfxPipeline}; +use super::{pipeline::WGfxPipeline, WGfx}; pub struct WGfxPass { pub command_buffer: Arc, @@ -34,6 +34,7 @@ where pub(super) fn new( pipeline: &Arc>, dimensions: [f32; 2], + offset: [f32; 2], vertex_buffer: Subbuffer<[V]>, vertices: Range, instances: Range, @@ -41,7 +42,7 @@ where vk_scissor: &graphics::viewport::Scissor, ) -> anyhow::Result { let viewport = Viewport { - offset: [0.0, 0.0], + offset, extent: dimensions, depth_range: 0.0..=1.0, }; diff --git a/wgui/src/gfx/pipeline.rs b/wgui/src/gfx/pipeline.rs index c22b4c0..2a1e9c9 100644 --- a/wgui/src/gfx/pipeline.rs +++ b/wgui/src/gfx/pipeline.rs @@ -1,14 +1,14 @@ use std::{marker::PhantomData, ops::Range, sync::Arc}; -use smallvec::{SmallVec, smallvec}; +use smallvec::{smallvec, SmallVec}; use vulkano::{ buffer::{ - BufferContents, BufferUsage, Subbuffer, allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, + BufferContents, BufferUsage, Subbuffer, }, descriptor_set::{ - DescriptorSet, WriteDescriptorSet, layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags}, + DescriptorSet, WriteDescriptorSet, }, format::Format, image::{ @@ -17,9 +17,8 @@ use vulkano::{ }, memory::allocator::MemoryTypeFilter, pipeline::{ - DynamicState, GraphicsPipeline, Pipeline, PipelineLayout, graphics::{ - self, GraphicsPipelineCreateInfo, + self, color_blend::{AttachmentBlend, ColorBlendAttachmentState, ColorBlendState}, input_assembly::{InputAssemblyState, PrimitiveTopology}, multisample::MultisampleState, @@ -27,13 +26,15 @@ use vulkano::{ subpass::PipelineRenderingCreateInfo, vertex_input::{Vertex, VertexDefinition, VertexInputState}, viewport::ViewportState, + GraphicsPipelineCreateInfo, }, layout::PipelineDescriptorSetLayoutCreateInfo, + DynamicState, GraphicsPipeline, Pipeline, PipelineLayout, }, shader::{EntryPoint, ShaderModule}, }; -use super::{WGfx, pass::WGfxPass}; +use super::{pass::WGfxPass, WGfx}; pub struct WGfxPipeline { pub graphics: Arc, @@ -266,6 +267,7 @@ where pub fn create_pass( self: &Arc, dimensions: [f32; 2], + offset: [f32; 2], vertex_buffer: Subbuffer<[V]>, vertices: Range, instances: Range, @@ -275,6 +277,7 @@ where WGfxPass::new( &self.clone(), dimensions, + offset, vertex_buffer, vertices, instances, diff --git a/wgui/src/renderer_vk/image.rs b/wgui/src/renderer_vk/image.rs index 0950dda..66bcf78 100644 --- a/wgui/src/renderer_vk/image.rs +++ b/wgui/src/renderer_vk/image.rs @@ -200,6 +200,7 @@ impl ImageRenderer { let pass = self.pipeline.inner.create_pass( [res[0] as _, res[1] as _], + [0.0, 0.0], vert_buffer.clone(), 0..4, 0..1, diff --git a/wgui/src/renderer_vk/rect.rs b/wgui/src/renderer_vk/rect.rs index 54eb908..900c672 100644 --- a/wgui/src/renderer_vk/rect.rs +++ b/wgui/src/renderer_vk/rect.rs @@ -10,10 +10,10 @@ use vulkano::{ use crate::{ drawing::{Boundary, Rectangle}, gfx::{ - BLEND_ALPHA, WGfx, cmd::GfxCommandBuffer, pass::WGfxPass, pipeline::{WGfxPipeline, WPipelineCreateInfo}, + WGfx, BLEND_ALPHA, }, renderer_vk::model_buffer::ModelBuffer, }; @@ -151,6 +151,7 @@ impl RectRenderer { let set1 = self.model_buffer.get_rect_descriptor(&self.pipeline); let pass = self.pipeline.color_rect.create_pass( [res[0] as _, res[1] as _], + [0.0, 0.0], self.vert_buffer.clone(), 0..4, 0..self.rect_vertices.len() as _, diff --git a/wgui/src/renderer_vk/text/text_renderer.rs b/wgui/src/renderer_vk/text/text_renderer.rs index 20a2d3d..3aafe54 100644 --- a/wgui/src/renderer_vk/text/text_renderer.rs +++ b/wgui/src/renderer_vk/text/text_renderer.rs @@ -4,9 +4,9 @@ use crate::{ }; use super::{ - ContentType, FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, TextArea, custom_glyph::{CustomGlyphCacheKey, RasterizeCustomGlyphRequest, RasterizedCustomGlyph}, text_atlas::{GlyphVertex, TextAtlas, TextPipeline}, + ContentType, FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, TextArea, }; use cosmic_text::{Color, SubpixelBin, SwashContent}; use etagere::size2; @@ -272,6 +272,7 @@ impl TextRenderer { let pass = self.pipeline.inner.create_pass( [res[0] as _, res[1] as _], + [0.0, 0.0], self.vertex_buffer.clone(), 0..4, 0..self.glyph_vertices.len() as u32, diff --git a/wlx-overlay-s/src/assets/gui/decor.xml b/wlx-overlay-s/src/assets/gui/decor.xml new file mode 100644 index 0000000..da5b44f --- /dev/null +++ b/wlx-overlay-s/src/assets/gui/decor.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/wlx-overlay-s/src/backend/openxr/lines.rs b/wlx-overlay-s/src/backend/openxr/lines.rs index 40f1420..d73b717 100644 --- a/wlx-overlay-s/src/backend/openxr/lines.rs +++ b/wlx-overlay-s/src/backend/openxr/lines.rs @@ -75,6 +75,7 @@ impl LinePool { let pass = self.pipeline.create_pass( [1.0, 1.0], + [0.0, 0.0], app.gfx_extras.quad_verts.clone(), 0..4, 0..1, diff --git a/wlx-overlay-s/src/backend/openxr/skybox.rs b/wlx-overlay-s/src/backend/openxr/skybox.rs index 273602a..9e14eaa 100644 --- a/wlx-overlay-s/src/backend/openxr/skybox.rs +++ b/wlx-overlay-s/src/backend/openxr/skybox.rs @@ -110,6 +110,7 @@ impl Skybox { let set1 = pipeline.uniform_buffer_upload(1, vec![1f32])?; let pass = pipeline.create_pass( tgt.extent_f32(), + [0.0, 0.0], app.gfx_extras.quad_verts.clone(), 0..4, 0..1, @@ -161,6 +162,7 @@ impl Skybox { .unwrap(); let pass = pipeline.create_pass( tgt.extent_f32(), + [0.0, 0.0], app.gfx_extras.quad_verts.clone(), 0..4, 0..1, diff --git a/wlx-overlay-s/src/overlays/screen/backend.rs b/wlx-overlay-s/src/overlays/screen/backend.rs index 37f4e22..8c7bc6c 100644 --- a/wlx-overlay-s/src/overlays/screen/backend.rs +++ b/wlx-overlay-s/src/overlays/screen/backend.rs @@ -192,12 +192,20 @@ impl OverlayBackend for ScreenBackend { .meta .is_some_and(|old| old.extent[..2] != meta.extent[..2]) { - pipeline.set_extent(app, [meta.extent[0] as _, meta.extent[1] as _])?; + pipeline.set_extent( + app, + [meta.extent[0] as _, meta.extent[1] as _], + [0., 0.], + )?; self.interaction_transform = Some(ui_transform(meta.extent.extent_u32arr())); } } else { - let pipeline = - ScreenPipeline::new(&meta, app, self.stereo.unwrap_or(StereoMode::None))?; + let pipeline = ScreenPipeline::new( + &meta, + app, + self.stereo.unwrap_or(StereoMode::None), + [0., 0.], + )?; meta.extent[2] = pipeline.get_depth(); self.pipeline = Some(pipeline); self.interaction_transform = Some(ui_transform(meta.extent.extent_u32arr())); diff --git a/wlx-overlay-s/src/overlays/screen/capture.rs b/wlx-overlay-s/src/overlays/screen/capture.rs index 7c9f105..555d443 100644 --- a/wlx-overlay-s/src/overlays/screen/capture.rs +++ b/wlx-overlay-s/src/overlays/screen/capture.rs @@ -46,11 +46,17 @@ pub struct ScreenPipeline { pipeline: Arc>, buf_alpha: Subbuffer<[f32]>, extentf: [f32; 2], + offsetf: [f32; 2], stereo: StereoMode, } impl ScreenPipeline { - pub fn new(meta: &FrameMeta, app: &mut AppState, stereo: StereoMode) -> anyhow::Result { + pub fn new( + meta: &FrameMeta, + app: &mut AppState, + stereo: StereoMode, + offsetf: [f32; 2], + ) -> anyhow::Result { let extentf = [meta.extent[0] as f32, meta.extent[1] as f32]; let pipeline = app.gfx.create_pipeline( @@ -70,11 +76,19 @@ impl ScreenPipeline { app, pipeline.clone(), extentf, + offsetf, buf_alpha.clone() )?], - mouse: Self::create_mouse_pass(app, pipeline.clone(), extentf, buf_alpha.clone())?, + mouse: Self::create_mouse_pass( + app, + pipeline.clone(), + extentf, + offsetf, + buf_alpha.clone(), + )?, pipeline, extentf, + offsetf, buf_alpha, stereo, }; @@ -94,6 +108,7 @@ impl ScreenPipeline { app, self.pipeline.clone(), self.extentf, + self.offsetf, self.buf_alpha.clone(), )?); } @@ -113,15 +128,34 @@ impl ScreenPipeline { self.pass.len() as _ } - pub fn set_extent(&mut self, app: &mut AppState, extentf: [f32; 2]) -> anyhow::Result<()> { + pub fn set_extent( + &mut self, + app: &mut AppState, + extentf: [f32; 2], + offsetf: [f32; 2], + ) -> anyhow::Result<()> { + self.extentf = extentf; + self.offsetf = offsetf; + for (eye, pass) in self.pass.iter_mut().enumerate() { - *pass = Self::create_pass(app, self.pipeline.clone(), extentf, self.buf_alpha.clone())?; + *pass = Self::create_pass( + app, + self.pipeline.clone(), + extentf, + offsetf, + self.buf_alpha.clone(), + )?; let verts = stereo_mode_to_verts(self.stereo, eye); pass.buf_vert.write()?.copy_from_slice(&verts); } - self.mouse = - Self::create_mouse_pass(app, self.pipeline.clone(), extentf, self.buf_alpha.clone())?; + self.mouse = Self::create_mouse_pass( + app, + self.pipeline.clone(), + extentf, + offsetf, + self.buf_alpha.clone(), + )?; Ok(()) } @@ -129,6 +163,7 @@ impl ScreenPipeline { app: &mut AppState, pipeline: Arc>, extentf: [f32; 2], + offsetf: [f32; 2], buf_alpha: Subbuffer<[f32]>, ) -> anyhow::Result { let set0 = pipeline.uniform_sampler( @@ -143,6 +178,7 @@ impl ScreenPipeline { let pass = pipeline.create_pass( extentf, + offsetf, buf_vert.clone(), 0..4, 0..1, @@ -157,6 +193,7 @@ impl ScreenPipeline { app: &mut AppState, pipeline: Arc>, extentf: [f32; 2], + offsetf: [f32; 2], buf_alpha: Subbuffer<[f32]>, ) -> anyhow::Result { #[rustfmt::skip] @@ -184,6 +221,7 @@ impl ScreenPipeline { let set1 = pipeline.buffer(1, buf_alpha)?; let pass = pipeline.create_pass( extentf, + offsetf, buf_vert.clone(), 0..4, 0..1, diff --git a/wlx-overlay-s/src/overlays/wayvr.rs b/wlx-overlay-s/src/overlays/wayvr.rs index 96d6eaa..2efef8f 100644 --- a/wlx-overlay-s/src/overlays/wayvr.rs +++ b/wlx-overlay-s/src/overlays/wayvr.rs @@ -7,7 +7,15 @@ use std::sync::Arc; use vulkano::{ buffer::BufferUsage, image::view::ImageView, pipeline::graphics::color_blend::AttachmentBlend, }; -use wgui::gfx::pipeline::{WGfxPipeline, WPipelineCreateInfo}; +use wgui::{ + gfx::{ + cmd::WGfxClearMode, + pipeline::{WGfxPipeline, WPipelineCreateInfo}, + }, + i18n::Translation, + parser::Fetchable, + widget::label::WidgetLabel, +}; use wlx_capture::frame::MouseMeta; use wlx_common::{ overlays::{BackendAttrib, BackendAttribValue, StereoMode}, @@ -21,6 +29,7 @@ use crate::{ wayvr::{self, SurfaceBufWithImage}, }, graphics::{ExtentExt, Vert2Uv, upload_quad_vertices}, + gui::panel::{GuiPanel, NewGuiPanelParams}, overlays::screen::capture::ScreenPipeline, state::{self, AppState}, subsystem::{hid::WheelDelta, input::KeyboardFocus}, @@ -33,9 +42,12 @@ use crate::{ }, }; +const BORDER_SIZE: u32 = 5; +const BAR_SIZE: u32 = 24; + pub fn create_wl_window_overlay( name: Arc, - app: &AppState, + app: &mut AppState, window: wayvr::window::WindowHandle, ) -> anyhow::Result { Ok(OverlayWindowConfig { @@ -71,12 +83,14 @@ pub struct WvrWindowBackend { mouse: Option, stereo: Option, cur_image: Option>, + panel: GuiPanel<()>, + mouse_transform: Affine2, } impl WvrWindowBackend { fn new( name: Arc, - app: &AppState, + app: &mut AppState, window: wayvr::window::WindowHandle, ) -> anyhow::Result { let popups_pipeline = app.gfx.create_pipeline( @@ -85,6 +99,28 @@ impl WvrWindowBackend { WPipelineCreateInfo::new(app.gfx.surface_format).use_blend(AttachmentBlend::default()), )?; + let mut panel = GuiPanel::new_from_template( + app, + "gui/decor.xml", + (), + NewGuiPanelParams { + resize_to_parent: true, + ..Default::default() + }, + )?; + + { + let mut title = panel + .parser_state + .fetch_widget_as::(&panel.layout.state, "label_title")?; + title.set_text_simple( + &mut app.wgui_globals.get(), + Translation::from_raw_text(&*name), + ); + } + + panel.update_layout()?; + Ok(Self { name, pipeline: None, @@ -101,25 +137,29 @@ impl WvrWindowBackend { None }, cur_image: None, + panel, + mouse_transform: Affine2::ZERO, }) } } impl OverlayBackend for WvrWindowBackend { - fn init(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> { - Ok(()) + fn init(&mut self, app: &mut state::AppState) -> anyhow::Result<()> { + self.panel.init(app) } - fn pause(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> { - Ok(()) + fn pause(&mut self, app: &mut state::AppState) -> anyhow::Result<()> { + self.panel.pause(app) } - fn resume(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> { + fn resume(&mut self, app: &mut state::AppState) -> anyhow::Result<()> { self.just_resumed = true; - Ok(()) + self.panel.resume(app) } fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { + let should_render_panel = self.panel.should_render(app)?; + let Some(toplevel) = app .wvr_server .as_ref() @@ -162,22 +202,37 @@ impl OverlayBackend for WvrWindowBackend { let mut meta = FrameMeta { extent: surf.image.image().extent(), format: surf.image.format(), + clear: WGfxClearMode::Clear([0.0, 0.0, 0.0, 0.0]), ..Default::default() }; if let Some(pipeline) = self.pipeline.as_mut() { + let inner_extent = meta.extent; + + meta.extent[0] += BORDER_SIZE * 2; + meta.extent[1] += BORDER_SIZE * 2 + BAR_SIZE; meta.extent[2] = pipeline.get_depth(); if self .meta .is_some_and(|old| old.extent[..2] != meta.extent[..2]) { - pipeline.set_extent(app, [meta.extent[0] as _, meta.extent[1] as _])?; + pipeline.set_extent( + app, + [inner_extent[0] as _, inner_extent[1] as _], + [BORDER_SIZE as _, (BAR_SIZE + BORDER_SIZE) as _], + )?; self.interaction_transform = Some(ui_transform(meta.extent.extent_u32arr())); } } else { - let pipeline = - ScreenPipeline::new(&meta, app, self.stereo.unwrap_or(StereoMode::None))?; + let pipeline = ScreenPipeline::new( + &meta, + app, + self.stereo.unwrap_or(StereoMode::None), + [BORDER_SIZE as _, (BAR_SIZE + BORDER_SIZE) as _], + )?; + meta.extent[0] += BORDER_SIZE * 2; + meta.extent[1] += BORDER_SIZE * 2 + BAR_SIZE; meta.extent[2] = pipeline.get_depth(); self.pipeline = Some(pipeline); self.interaction_transform = Some(ui_transform(meta.extent.extent_u32arr())); @@ -215,7 +270,7 @@ impl OverlayBackend for WvrWindowBackend { } else if dirty { Ok(ShouldRender::Should) } else { - Ok(ShouldRender::Can) + Ok(should_render_panel) } } else { log::trace!("{}: no buffer for wl_surface", self.name); @@ -229,6 +284,8 @@ impl OverlayBackend for WvrWindowBackend { app: &mut state::AppState, rdr: &mut RenderResources, ) -> anyhow::Result<()> { + self.panel.render(app, rdr)?; + let image = self.cur_image.as_ref().unwrap().clone(); self.pipeline @@ -264,6 +321,7 @@ impl OverlayBackend for WvrWindowBackend { let pass = self.popups_pipeline.create_pass( extentf, + [BORDER_SIZE as _, (BAR_SIZE + BORDER_SIZE) as _], buf_vert, 0..4, 0..1, @@ -308,8 +366,8 @@ impl OverlayBackend for WvrWindowBackend { } } - fn on_left(&mut self, _app: &mut state::AppState, _pointer: usize) { - // Ignore event + fn on_left(&mut self, app: &mut state::AppState, pointer: usize) { + self.panel.on_left(app, pointer); } fn on_pointer(&mut self, app: &mut state::AppState, hit: &input::PointerHit, pressed: bool) {