7 Commits

Author SHA1 Message Date
Thomas Colliers
fbf31e1df7 Code style 2025-08-18 18:20:58 +02:00
Thomas Colliers
d1043f7b8c fix: typo 2025-08-17 22:17:41 +02:00
Thomas Colliers
a9e4d91f9a fix: don't lower framerate when window is out of focus 2025-08-17 22:17:41 +02:00
Nova
877a32ab09 fix: presentation feedback send unwrap 2025-08-13 12:01:42 -07:00
Nova
4ccee1bf89 fix(lines): properly break line points with same position 2025-08-13 11:57:51 -07:00
Nova
6b78684650 fix: remove out 2025-08-13 11:22:31 -07:00
Nova
4f75aa5edf fix: window opaqueness 2025-08-13 11:21:18 -07:00
6 changed files with 106 additions and 232 deletions

View File

@@ -17,7 +17,6 @@ use bevy::app::{App, ScheduleRunnerPlugin, TerminalCtrlCHandlerPlugin};
use bevy::asset::{AssetMetaCheck, UnapprovedPathMode};
use bevy::audio::AudioPlugin;
use bevy::core_pipeline::CorePipelinePlugin;
use bevy::core_pipeline::oit::OrderIndependentTransparencySettings;
use bevy::diagnostic::DiagnosticsPlugin;
use bevy::ecs::schedule::{ExecutorKind, ScheduleLabel};
use bevy::gizmos::GizmoPlugin;
@@ -28,7 +27,7 @@ use bevy::render::settings::{Backends, RenderCreation, WgpuSettings};
use bevy::render::{RenderDebugFlags, RenderPlugin};
use bevy::scene::ScenePlugin;
use bevy::window::{CompositeAlphaMode, PresentMode};
use bevy::winit::{WakeUp, WinitPlugin};
use bevy::winit::{WakeUp, WinitPlugin, WinitSettings, UpdateMode};
use bevy_dmabuf::import::DmabufImportPlugin;
use bevy_mod_openxr::action_set_attaching::OxrActionAttachingPlugin;
use bevy_mod_openxr::action_set_syncing::OxrActionSyncingPlugin;
@@ -316,10 +315,10 @@ fn bevy_loop(
.async_compute
.on_thread_spawn = Some(enter_runtime_context.clone());
plugins = plugins.set(task_pool_plugin);
if args.flatscreen
let flatscreenmode = args.flatscreen
|| std::env::var_os("DISPLAY").is_some_and(|s| !s.is_empty())
|| std::env::var_os("WAYLAND_DISPLAY").is_some_and(|s| !s.is_empty())
{
|| std::env::var_os("WAYLAND_DISPLAY").is_some_and(|s| !s.is_empty());
if flatscreenmode {
let mut plugin = WinitPlugin::<WakeUp>::default();
plugin.run_on_any_thread = true;
plugins = plugins
@@ -370,7 +369,7 @@ fn bevy_loop(
composite_alpha_mode: if args.transparent_flatscreen {
CompositeAlphaMode::PreMultiplied
} else {
CompositeAlphaMode::Inherit
CompositeAlphaMode::Auto
},
title: "StardustXR server flatscreen mode".to_string(),
..default()
@@ -379,6 +378,13 @@ fn bevy_loop(
}),
);
if flatscreenmode {
app.insert_resource(WinitSettings {
focused_mode: UpdateMode::Continuous,
unfocused_mode: UpdateMode::Continuous,
});
}
app.add_plugins(bevy_sk::hand::HandPlugin);
// app.add_plugins(HandGizmosPlugin);
app.world_mut().resource_mut::<AmbientLight>().brightness = 1000.0;
@@ -457,26 +463,6 @@ fn cam_observer(
*msaa = Msaa::Off;
}
fn add_oit(
mut commands: Commands,
cameras: Query<
Entity,
(
With<Camera3d>,
Without<OrderIndependentTransparencySettings>,
),
>,
) {
for entity in &cameras {
commands
.entity(entity)
.insert(OrderIndependentTransparencySettings {
layer_count: 4,
alpha_threshold: 0.00,
});
}
}
fn xr_step(world: &mut World) {
// update things like the Xr input methods
world.run_schedule(PreFrameWait);

View File

@@ -77,6 +77,14 @@ fn build_line_mesh(
let mut last = line.cyclic.then(|| line.points.last()).flatten();
let mut peekable = line.points.iter().peekable();
while let Some(curr) = peekable.next() {
// Skip this point if it has the same position as the previous point
if let Some(prev) = last {
if Vec3::from(prev.point) == Vec3::from(curr.point) {
last = Some(curr);
continue;
}
}
let mut end = false;
// Determine the next point - either the next in sequence or
// for cyclic lines, wrap back to first point at the end
@@ -93,6 +101,10 @@ fn build_line_mesh(
}
out
};
// if we can't make a full line, don't bother trying
if point_windows.len() < 2 {
continue;
}
for (last, curr, next, last_point) in point_windows {
let last_quat = last.map(|v| {
Quat::from_rotation_arc(

View File

@@ -230,13 +230,11 @@ impl Surface {
display_timestamp: MonotonicTimestamp,
refresh_cycle: u64,
) {
self.message_sink
.send(Message::SendPresentationFeedback {
surface: self.clone(),
display_timestamp,
refresh_cycle,
})
.unwrap();
let _ = self.message_sink.send(Message::SendPresentationFeedback {
surface: self.clone(),
display_timestamp,
refresh_cycle,
});
}
#[tracing::instrument(level = "debug", skip_all)]

View File

@@ -1,30 +1,28 @@
use super::toplevel::Toplevel;
use crate::{
core::error::Result,
nodes::{
drawable::model::ModelPart,
items::panel::{Backend, Geometry, PanelItemInitData, SurfaceId, ToplevelInfo},
},
wayland::{Message, core::surface::Surface},
core::error::Result,
nodes::{
drawable::model::ModelPart,
items::panel::{Backend, Geometry, PanelItemInitData, SurfaceId, ToplevelInfo},
},
wayland::{Message, core::surface::Surface},
};
use mint::Vector2;
use parking_lot::Mutex;
use std::{collections::HashMap, sync::{Arc, Weak}};
use std::sync::Arc;
use std::sync::Weak;
use tracing;
#[derive(Debug)]
pub struct XdgBackend {
toplevel: Weak<Toplevel>,
children: Mutex<HashMap<u64, Weak<Surface>>>,
toplevel: Weak<Toplevel>,
}
impl XdgBackend {
pub fn new(toplevel: Arc<Toplevel>) -> Self {
Self {
toplevel: Arc::downgrade(&toplevel),
children: Mutex::new(HashMap::new()),
}
}
pub fn new(toplevel: Arc<Toplevel>) -> Self {
Self {
toplevel: Arc::downgrade(&toplevel),
}
}
// Since XdgBackend is created and owned by Mapped which is owned by Toplevel,
// we can safely assume the Toplevel reference will always be valid
@@ -34,20 +32,12 @@ impl XdgBackend {
.expect("Toplevel should always be valid while XdgBackend exists")
}
fn surface_from_id(&self, id: SurfaceId) -> Option<Arc<Surface>> {
match id {
SurfaceId::Toplevel(_) => Some(self.toplevel().surface()),
SurfaceId::Child(uid) => self.children.lock().get(&uid).and_then(Weak::upgrade),
}
}
pub fn register_child(&self, id: u64, surface: &Arc<Surface>) {
self.children.lock().insert(id, Arc::downgrade(surface));
}
pub fn unregister_child(&self, id: u64) {
self.children.lock().remove(&id);
}
fn surface_from_id(&self, id: SurfaceId) -> Option<Arc<Surface>> {
match id {
SurfaceId::Toplevel(_) => Some(self.toplevel().surface()),
SurfaceId::Child(_) => None,
}
}
}
impl Backend for XdgBackend {

View File

@@ -4,8 +4,8 @@ use super::{
surface::Surface,
};
use crate::{
nodes::items::panel::{ChildInfo, Geometry, PanelItem, SurfaceId},
wayland::util::DoubleBuffer,
nodes::items::panel::{Geometry, PanelItem, SurfaceId},
wayland::util::DoubleBuffer,
};
use parking_lot::Mutex;
use rand::Rng;
@@ -25,10 +25,10 @@ pub struct Popup {
surface_id: SurfaceId,
parent: Arc<Surface>,
surface: Weak<Surface>,
pub panel_item: Weak<PanelItem<XdgBackend>>,
positioner_data: Mutex<PositionerData>,
geometry: Mutex<DoubleBuffer<Geometry>>,
mapped: AtomicBool,
pub panel_item: Weak<PanelItem<XdgBackend>>,
positioner_data: Mutex<PositionerData>,
geometry: DoubleBuffer<Geometry>,
mapped: AtomicBool,
}
impl Popup {
pub fn new(
@@ -46,83 +46,12 @@ impl Popup {
surface_id: SurfaceId::Child(rand::thread_rng().gen_range(0..u64::MAX)),
parent,
surface: Arc::downgrade(xdg_surface),
panel_item: Arc::downgrade(panel_item),
positioner_data: Mutex::new(positioner_data),
geometry: Mutex::new(DoubleBuffer::new(positioner_data.infinite_geometry())),
mapped: AtomicBool::new(false),
}
}
}
impl Popup {
fn id(&self) -> u64 {
match self.surface_id {
SurfaceId::Child(id) => id,
SurfaceId::Toplevel(_) => 0,
}
}
pub fn surface_id(&self) -> SurfaceId { self.surface_id.clone() }
pub fn current_geometry(&self) -> Geometry {
*self.geometry.lock().current()
}
pub fn is_mapped(&self) -> bool {
self.mapped.load(std::sync::atomic::Ordering::SeqCst)
}
pub fn map(&self) {
if self.is_mapped() {
return;
}
let Some(panel_item) = self.panel_item.upgrade() else { return; };
let xdg_surface = match self.surface.upgrade() { Some(s) => s, None => return };
let core_surface = xdg_surface.wl_surface();
// Determine parent surface id
let parent_wl_surface = self.parent.wl_surface();
let parent_role = parent_wl_surface.role.lock();
let parent_id = match parent_role.as_ref().unwrap() {
crate::wayland::core::surface::SurfaceRole::XdgToplevel(_) => {
SurfaceId::Toplevel(())
}
crate::wayland::core::surface::SurfaceRole::XDGPopup(p) => p.surface_id(),
};
let geometry = *self.geometry.lock().current();
let info = ChildInfo {
id: self.id(),
parent: parent_id,
geometry,
z_order: 0,
receives_input: true,
};
panel_item.create_child(self.id(), &info);
panel_item.backend.register_child(self.id(), &core_surface);
self.mapped.store(true, std::sync::atomic::Ordering::SeqCst);
}
pub fn unmap(&self) {
if !self.mapped.swap(false, std::sync::atomic::Ordering::SeqCst) {
return;
}
if let Some(panel_item) = self.panel_item.upgrade() {
panel_item.destroy_child(self.id());
panel_item.backend.unregister_child(self.id());
}
}
fn reposition_child(&self) {
if self.is_mapped() {
if let Some(panel_item) = self.panel_item.upgrade() {
let geometry = *self.geometry.lock().current();
panel_item.reposition_child(self.id(), &geometry);
}
}
}
panel_item: Arc::downgrade(panel_item),
positioner_data: Mutex::new(positioner_data),
geometry: DoubleBuffer::new(positioner_data.infinite_geometry()),
mapped: AtomicBool::new(false),
}
}
}
impl XdgPopup for Popup {
/// https://wayland.app/protocols/xdg-shell#xdg_popup:request:grab
@@ -144,35 +73,28 @@ impl XdgPopup for Popup {
positioner: ObjectId,
token: u32,
) -> Result<()> {
let positioner = client.get::<Positioner>(positioner).unwrap();
let positioner_data = positioner.data();
*self.positioner_data.lock() = positioner_data;
if self.version >= 5 {
self.repositioned(client, sender_id, token).await?;
}
let geometry = positioner_data.infinite_geometry();
{
let mut geo = self.geometry.lock();
geo.pending = geometry;
geo.apply();
}
self.configure(
client,
sender_id,
geometry.origin.x,
geometry.origin.y,
geometry.size.x as i32,
geometry.size.y as i32,
)
.await?;
self.surface.upgrade().unwrap().reconfigure(client).await?;
self.reposition_child();
Ok(())
}
let positioner = client.get::<Positioner>(positioner).unwrap();
let positioner_data = positioner.data();
*self.positioner_data.lock() = positioner_data;
if self.version >= 5 {
self.repositioned(client, sender_id, token).await?;
}
let geometry = positioner_data.infinite_geometry();
self.configure(
client,
sender_id,
geometry.origin.x,
geometry.origin.y,
geometry.size.x as i32,
geometry.size.y as i32,
)
.await?;
self.surface.upgrade().unwrap().reconfigure(client).await?;
Ok(())
}
/// https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy
async fn destroy(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
self.unmap();
Ok(())
}
async fn destroy(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
Ok(())
}
}

View File

@@ -1,6 +1,5 @@
use super::{popup::Popup, positioner::Positioner, toplevel::Mapped};
use crate::wayland::{core::surface::SurfaceRole, display::Display, xdg::toplevel::Toplevel};
use waynest::server::protocol::stable::xdg_shell::xdg_popup::XdgPopup;
use std::sync::Arc;
use std::sync::Weak;
pub use waynest::server::protocol::stable::xdg_shell::xdg_surface::*;
@@ -120,43 +119,29 @@ impl XdgSurface for Surface {
parent: Option<ObjectId>,
positioner: ObjectId,
) -> Result<()> {
let parent = client.get::<Surface>(parent.unwrap()).unwrap();
let panel_item = match parent.wl_surface().role.lock().as_ref().unwrap() {
SurfaceRole::XdgToplevel(toplevel) => {
let toplevel_lock = toplevel.mapped.lock();
toplevel_lock.as_ref().unwrap()._panel_item.clone()
}
SurfaceRole::XDGPopup(popup) => popup.panel_item.upgrade().unwrap(),
};
let positioner = client.get::<Positioner>(positioner).unwrap();
let parent = client.get::<Surface>(parent.unwrap()).unwrap();
let panel_item = match parent.wl_surface().role.lock().as_ref().unwrap() {
SurfaceRole::XdgToplevel(toplevel) => {
let toplevel_lock = toplevel.mapped.lock();
toplevel_lock.as_ref().unwrap()._panel_item.clone()
}
SurfaceRole::XDGPopup(popup) => popup.panel_item.upgrade().unwrap(),
};
let positioner = client.get::<Positioner>(positioner).unwrap();
let surface = client.get::<Surface>(self.id).unwrap();
let wl_surface = surface.wl_surface();
let surface = client.get::<Surface>(self.id).unwrap();
let popup = client.insert(
popup_id,
Popup::new(
popup_id,
self.version,
parent,
&panel_item,
&surface,
&positioner,
),
);
// Initial popup configure
let geometry = popup.current_geometry();
popup
.configure(
client,
popup_id,
geometry.origin.x,
geometry.origin.y,
geometry.size.x as i32,
geometry.size.y as i32,
)
.await?;
let popup = client.insert(
popup_id,
Popup::new(
popup_id,
self.version,
parent,
&panel_item,
&surface,
&positioner,
),
);
{
let wl_surface = self.wl_surface();
@@ -170,27 +155,8 @@ impl XdgSurface for Surface {
}
}
let serial = client.next_event_serial();
self.configure(client, sender_id, serial).await?;
let configured = self.configured.clone();
wl_surface.add_commit_handler(move |surface, state| {
let Some(SurfaceRole::XDGPopup(popup)) = &*surface.role.lock() else {
return true;
};
let has_valid_buffer = state
.buffer
.as_ref()
.is_some_and(|b| b.buffer.size().x > 0 && b.buffer.size().y > 0);
if !popup.is_mapped()
&& configured.load(std::sync::atomic::Ordering::SeqCst)
&& has_valid_buffer
{
popup.map();
return false;
}
true
});
let serial = client.next_event_serial();
self.configure(client, sender_id, serial).await?;
// let pid = client.get::<Display>(ObjectId::DISPLAY).unwrap().pid;
// let configured = self.configured.clone();