Merge branch 'StardustXR:main' into main

This commit is contained in:
Astavie
2023-05-01 17:51:49 +02:00
committed by GitHub
25 changed files with 1452 additions and 1260 deletions

830
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ homepage = "https://stardustxr.org"
[dependencies]
color-eyre = { version = "0.6.2", default-features = false }
clap = { version = "4.1.6", features = ["derive"] }
clap = { version = "4.2.4", features = ["derive"] }
dashmap = "5.4.0"
glam = {version = "0.23.0", features = ["mint"]}
lazy_static = "1.4.0"
@@ -18,29 +18,28 @@ mint = "0.5.9"
nanoid = "0.4.0"
once_cell = "1.17.1"
parking_lot = "0.12.1"
portable-atomic = {version = "1.0.1", features = ["float", "std"]}
portable-atomic = {version = "1.2.0", features = ["float", "std"]}
rustc-hash = "1.1.0"
tokio = { version = "1.25.0", features = ["rt-multi-thread", "signal"] }
tokio = { version = "1.27.0", features = ["rt-multi-thread", "signal"] }
send_wrapper = "0.6.0"
prisma = "0.1.1"
slog = "2.7.0"
xkbcommon = { version = "0.5.0", default-features = false, optional = true }
stardust-xr = "0.11.0"
stardust-xr = "0.11.3"
directories = "5.0.0"
serde = { version = "1.0.152", features = ["derive"] }
serde = { version = "1.0.160", features = ["derive"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
tracing-slog = "0.2.0"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
global_counter = "0.2.2"
rand = "0.8.5"
[dependencies.stereokit]
default-features = false
features = ["linux-egl", "color_named", "prisma"]
version = "0.15.2"
features = ["linux-egl"]
version = "0.16.7"
[dependencies.smithay]
git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
# git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
git = "https://github.com/smithay/smithay.git" # Until we get stereokit to understand OES samplers and external textures
default-features = false
features = ["desktop", "renderer_gl", "wayland_frontend"]
version = "*"

View File

@@ -80,7 +80,7 @@ impl Client {
let (mut messenger_tx, mut messenger_rx) = messenger::create(connection);
let scenegraph = Arc::new(Scenegraph::default());
let startup_settings = env.as_ref().and_then(|env| startup_settings(env));
let startup_settings = env.as_ref().and_then(startup_settings);
let client = CLIENTS.add(Client {
pid,

View File

@@ -14,20 +14,17 @@ use self::core::eventloop::EventLoop;
use clap::Parser;
use color_eyre::eyre::Result;
use directories::ProjectDirs;
use once_cell::sync::OnceCell;
use stardust_xr::server;
use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;
use std::time::Duration;
use stereokit::input::Handed;
use stereokit::lifecycle::DisplayMode;
use stereokit::lifecycle::{DepthMode, LogFilter};
use stereokit::material::Material;
use stereokit::render::SphericalHarmonics;
use stereokit::render::StereoKitRender;
use stereokit::shader::Shader;
use stereokit::texture::Texture;
use stereokit::time::StereoKitTime;
use stereokit::DisplayBlend;
use stereokit::{
named_colors::BLACK, DepthMode, DisplayMode, Handed, LogLevel, StereoKitMultiThread,
TextureFormat, TextureType,
};
use tokio::{runtime::Handle, sync::oneshot};
use tracing::{debug_span, error, info};
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
@@ -46,8 +43,14 @@ struct CliArgs {
/// Don't create a tip input for controller because SOME RUNTIMES will lie
#[clap(long, action)]
disable_controller: bool,
/// Run a script when ready for clients to connect. If this is not set the script at $HOME/.config/stardust/startup will be ran if it exists.
#[clap(id = "PATH", short = 'e', long = "execute-startup-script", action)]
startup_script: Option<PathBuf>,
}
static STARDUST_INSTANCE: OnceCell<String> = OnceCell::new();
struct EventLoopInfo {
tokio_handle: Handle,
socket_path: PathBuf,
@@ -70,6 +73,7 @@ fn main() -> Result<()> {
let log_layer = fmt::Layer::new()
.with_thread_names(true)
.with_ansi(true)
.with_line_number(true)
.with_filter(EnvFilter::from_default_env());
registry.with(log_layer).init();
@@ -79,47 +83,52 @@ fn main() -> Result<()> {
}
let cli_args = Arc::new(CliArgs::parse());
let stereokit = stereokit::Settings::default()
.app_name("Stardust XR")
.log_filter(LogFilter::None)
.overlay_app(cli_args.overlay_priority.is_some())
.overlay_priority(cli_args.overlay_priority.unwrap_or(u32::MAX))
.disable_desktop_input_window(true)
.display_preference(if cli_args.flatscreen {
let sk = stereokit::Settings {
app_name: "Stardust XR".to_string(),
display_preference: if cli_args.flatscreen {
DisplayMode::Flatscreen
} else {
DisplayMode::MixedReality
})
.depth_mode(DepthMode::D32)
.init()
.expect("StereoKit failed to initialize");
},
blend_preference: DisplayBlend::AnyTransparent,
depth_mode: DepthMode::D32,
log_filter: LogLevel::None,
overlay_app: cli_args.overlay_priority.is_some(),
overlay_priority: cli_args.overlay_priority.unwrap_or(u32::MAX),
disable_desktop_input_window: true,
..Default::default()
}
.init()
.expect("StereoKit failed to initialize");
info!("Init StereoKit");
Material::find(&stereokit, "default/material_pbr")?
.set_shader(Shader::from_name(&stereokit, "default/shader_pbr_clip")?);
sk.material_set_shader(
sk.material_find("default/material_pbr")?,
sk.shader_find("default/shader_pbr_clip")?,
);
// Skytex/light stuff
{
if let Some((tex, light)) = project_dirs
if let Some((light, tex)) = project_dirs
.as_ref()
.and_then(|dirs| {
let skytex_path = dirs.config_dir().join("skytex.hdr");
skytex_path.exists().then(|| {
Texture::from_cubemap_equirectangular(&stereokit, &skytex_path, true, 100)
})
skytex_path
.exists()
.then(|| sk.tex_create_cubemap_file(&skytex_path, true, 100).ok())
})
.flatten()
{
stereokit.set_skytex(&tex);
stereokit.set_skylight(&light);
} else if let Some(tex) = Texture::cubemap_from_spherical_harmonics(
&stereokit,
&SphericalHarmonics::default(),
16,
0.0,
0.0,
) {
stereokit.set_skytex(&tex);
sk.render_set_skytex(&tex);
sk.render_set_skylight(light);
} else {
sk.render_set_skytex(sk.tex_gen_color(
BLACK,
1,
1,
TextureType::CUBEMAP,
TextureFormat::RGBA32,
));
}
}
@@ -140,10 +149,8 @@ fn main() -> Result<()> {
.flatten();
if hands.is_none() {
unsafe {
stereokit::sys::input_hand_visible(stereokit::sys::handed__handed_left, false as i32);
stereokit::sys::input_hand_visible(stereokit::sys::handed__handed_right, false as i32);
}
sk.input_hand_visible(Handed::Left, false);
sk.input_hand_visible(Handed::Right, false);
}
let (event_stop_tx, event_stop_rx) = oneshot::channel::<()>();
@@ -159,7 +166,12 @@ fn main() -> Result<()> {
info!("Stardust ready!");
if let Some(project_dirs) = project_dirs.as_ref() {
let _startup = Command::new(project_dirs.config_dir().join("startup"))
let startup_script_path = cli_args
.startup_script
.clone()
.and_then(|p| p.canonicalize().ok())
.unwrap_or_else(|| project_dirs.config_dir().join("startup"));
let _startup = Command::new(startup_script_path)
.env("WAYLAND_DISPLAY", &wayland.socket_name)
.env(
"STARDUST_INSTANCE",
@@ -168,13 +180,18 @@ fn main() -> Result<()> {
.file_name()
.expect("Stardust socket path not found"),
)
.env("GDK_BACKEND", "wayland")
.env("QT_QPA_PLATFORM", "wayland")
.env("MOZ_ENABLE_WAYLAND", "1")
.env("CLUTTER_BACKEND", "wayland")
.env("SDL_VIDEODRIVER", "wayland")
.spawn();
}
let mut last_frame_delta = Duration::ZERO;
let mut sleep_duration = Duration::ZERO;
debug_span!("StereoKit").in_scope(|| {
stereokit.run(
sk.run(
|sk| {
let _span = debug_span!("StereoKit step");
let _span = _span.enter();
@@ -196,7 +213,7 @@ fn main() -> Result<()> {
right_controller.update(sk);
}
input::process_input();
nodes::root::Root::send_frame_events(sk.time_elapsed());
nodes::root::Root::send_frame_events(sk.time_elapsed_unscaled());
{
let frame_delta = Duration::from_secs_f64(sk.time_elapsed_unscaled());
if last_frame_delta < frame_delta {
@@ -220,7 +237,7 @@ fn main() -> Result<()> {
#[cfg(feature = "wayland")]
wayland.update(sk);
drawable::draw(sk);
audio::update();
audio::update(sk);
#[cfg(feature = "wayland")]
wayland.make_context_current();
},
@@ -241,14 +258,15 @@ fn main() -> Result<()> {
Ok(())
}
// #[tokio::main]
#[tokio::main]
// #[tokio::main(flavor = "current_thread")]
async fn event_loop(
info_sender: oneshot::Sender<EventLoopInfo>,
stop_rx: oneshot::Receiver<()>,
) -> color_eyre::eyre::Result<()> {
let socket_path =
server::get_free_socket_path().expect("Unable to find a free stardust socket path");
STARDUST_INSTANCE.set(socket_path.file_name().unwrap().to_string_lossy().into_owned()).expect("Someone hasn't done their job, yell at Nova because how is this set multiple times what the hell");
let _event_loop = EventLoop::new(socket_path.clone()).expect("Couldn't create server socket");
info!("Init event loop");
info!(

View File

@@ -5,7 +5,7 @@ use crate::core::registry::Registry;
use crate::core::resource::ResourceID;
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
use color_eyre::eyre::{ensure, eyre, Result};
use glam::{vec3a, Vec4Swizzles};
use glam::{vec3, Vec4Swizzles};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use send_wrapper::SendWrapper;
@@ -13,19 +13,20 @@ use serde::Deserialize;
use stardust_xr::schemas::flex::deserialize;
use stardust_xr::values::Transform;
use std::ops::DerefMut;
use std::{ffi::OsStr, fmt::Error, path::PathBuf, sync::Arc};
use stereokit::sound::SoundInstance;
use stereokit::sound::{Sound as SKSound, SoundT};
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
use stereokit::{Sound as SkSound, SoundInstance, StereoKitDraw};
static SOUND_REGISTRY: Registry<Sound> = Registry::new();
pub struct Sound {
space: Arc<Spatial>,
resource_id: ResourceID,
pending_audio_path: OnceCell<PathBuf>,
instance: Mutex<Option<SoundInstance>>,
volume: f32,
sk_sound: OnceCell<SendWrapper<SKSound>>,
pending_audio_path: PathBuf,
sk_sound: OnceCell<SendWrapper<SkSound>>,
instance: Mutex<Option<SoundInstance>>,
stop: Mutex<Option<()>>,
play: Mutex<Option<()>>,
}
impl Sound {
@@ -34,78 +35,70 @@ impl Sound {
node.spatial.get().is_some(),
"Internal: Node does not have a spatial attached!"
);
let pending_audio_path = resource_id
.get_file(
&node
.get_client()
.ok_or_else(|| eyre!("Client not found"))?
.base_resource_prefixes
.lock()
.clone(),
&[OsStr::new("wav"), OsStr::new("mp3")],
)
.ok_or_else(|| eyre!("Resource not found"))?;
let sound = Sound {
space: node.spatial.get().unwrap().clone(),
resource_id,
volume: 1.0,
instance: Mutex::new(None),
pending_audio_path: OnceCell::new(),
pending_audio_path,
sk_sound: OnceCell::new(),
instance: Mutex::new(None),
stop: Mutex::new(None),
play: Mutex::new(None),
};
let sound_arc = SOUND_REGISTRY.add(sound);
node.add_local_signal("play", Sound::play_flex);
node.add_local_signal("stop", Sound::stop_flex);
let _ = sound_arc.pending_audio_path.set(
sound_arc
.resource_id
.get_file(
&node
.get_client()
.ok_or_else(|| eyre!("Client not found"))?
.base_resource_prefixes
.lock()
.clone(),
&[OsStr::new("wav"), OsStr::new("mp3")],
)
.ok_or_else(|| eyre!("Resource not found"))?,
);
let _ = node.sound.set(sound_arc.clone());
Ok(sound_arc)
}
fn update(&self) {
fn update(&self, sk: &impl StereoKitDraw) {
let sound = self.sk_sound.get_or_init(|| {
SendWrapper::new(sk.sound_create(self.pending_audio_path.clone()).unwrap())
});
if self.stop.lock().take().is_some() {
if let Some(instance) = self.instance.lock().take() {
sk.sound_inst_stop(instance);
}
}
if self.play.lock().is_some() && self.instance.lock().is_none() {
self.instance.lock().replace(sk.sound_play(
sound.as_ref(),
vec3(0.0, 0.0, 0.0),
self.volume,
));
}
if let Some(instance) = self.instance.lock().deref_mut() {
instance.set_position(self.space.global_transform().w_axis.xyz())
sk.sound_inst_set_pos(*instance, self.space.global_transform().w_axis.xyz());
}
}
fn play_flex(node: &Node, _calling_client: Arc<Client>, _data: &[u8]) -> Result<()> {
let sound = node.sound.get().unwrap();
let sk_sound = sound
.sk_sound
.get_or_try_init(|| -> color_eyre::eyre::Result<SendWrapper<SKSound>> {
let pending_audio_path = sound.pending_audio_path.get().ok_or(Error)?;
let sound = SKSound::from_file(pending_audio_path.as_path()).ok_or(Error)?;
Ok(SendWrapper::new(sound))
})
.ok();
if let Some(sk_sound) = sk_sound {
let position = sound
.space
.global_transform()
.transform_point3a(vec3a(0.0, 0.0, 0.0));
sound
.instance
.lock()
.replace(sk_sound.play_sound(position, sound.volume));
}
sound.play.lock().replace(());
Ok(())
}
pub fn stop_flex(node: &Node, _calling_client: Arc<Client>, _data: &[u8]) -> Result<()> {
let sound = node.sound.get().unwrap();
if let Some(instance) = sound.instance.lock().take() {
instance.stop();
}
sound.stop.lock().replace(());
Ok(())
}
}
pub fn update() {
pub fn update(sk: &impl StereoKitDraw) {
for sound in SOUND_REGISTRY.get_valid_contents() {
sound.update()
sound.update(sk)
}
}

View File

@@ -125,8 +125,8 @@ impl PulseSender {
}
let (_, rotation, position) = Spatial::space_to_space_matrix(
rx_node.spatial.get().map(|s| &**s),
tx_node.spatial.get().map(|s| &**s),
rx_node.spatial.get().map(|s| s.as_ref()),
tx_node.spatial.get().map(|s| s.as_ref()),
)
.to_scale_rotation_translation();

View File

@@ -14,7 +14,7 @@ use prisma::{Flatten, Lerp, Rgba};
use serde::Deserialize;
use stardust_xr::{schemas::flex::deserialize, values::Transform};
use std::{collections::VecDeque, sync::Arc};
use stereokit::{lifecycle::StereoKitDraw, lines::LinePoint as SkLinePoint, values::Color128};
use stereokit::{Color128, LinePoint as SkLinePoint, StereoKitDraw};
use super::Drawable;
@@ -57,14 +57,14 @@ impl Lines {
Ok(lines)
}
fn draw(&self, draw_ctx: &StereoKitDraw) {
fn draw(&self, draw_ctx: &impl StereoKitDraw) {
let transform_mat = self.space.global_transform();
let data = self.data.lock().clone();
let mut points: VecDeque<SkLinePoint> = data
.points
.iter()
.map(|p| SkLinePoint {
point: transform_mat.transform_point3a(Vec3A::from(p.point)).into(),
pt: transform_mat.transform_point3a(Vec3A::from(p.point)).into(),
thickness: p.thickness,
color: p.color.map(|c| (c * 255.0) as u8).into(),
})
@@ -72,20 +72,19 @@ impl Lines {
if data.cyclic && !points.is_empty() {
let first = data.points.first().unwrap();
let last = data.points.last().unwrap();
let color = Rgba::from_slice(&first.color).lerp(&Rgba::from_slice(&last.color), 0.5);
let connect_point = SkLinePoint {
point: transform_mat
pt: transform_mat
.transform_point3a(Vec3A::from(first.point).lerp(Vec3A::from(last.point), 0.5))
.into(),
thickness: (first.thickness + last.thickness) * 0.5,
color: Color128::from(
Rgba::from_slice(&first.color).lerp(&Rgba::from_slice(&last.color), 0.5),
)
.into(),
color: Color128::from([color.red(), color.green(), color.blue(), color.alpha()])
.into(),
};
points.push_front(connect_point.clone());
points.push_back(connect_point);
}
stereokit::lines::line_add_listv(draw_ctx, points.make_contiguous());
draw_ctx.line_add_listv(points.make_contiguous());
}
pub fn set_points_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
@@ -113,7 +112,7 @@ impl Drop for Lines {
}
}
pub fn draw_all(draw_ctx: &StereoKitDraw) {
pub fn draw_all(draw_ctx: &impl StereoKitDraw) {
for lines in LINES_REGISTRY.get_valid_contents() {
if lines.enabled.load(Ordering::Relaxed) {
lines.draw(draw_ctx);

View File

@@ -11,7 +11,7 @@ use parking_lot::Mutex;
use serde::Deserialize;
use stardust_xr::schemas::flex::deserialize;
use std::{path::PathBuf, sync::Arc};
use stereokit::{lifecycle::StereoKitDraw, render::StereoKitRender, texture::Texture};
use stereokit::StereoKitDraw;
use tracing::instrument;
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
@@ -30,31 +30,19 @@ pub enum Drawable {
}
#[instrument(level = "debug", skip(sk))]
pub fn draw(sk: &StereoKitDraw) {
pub fn draw(sk: &impl StereoKitDraw) {
lines::draw_all(sk);
model::draw_all(sk);
text::draw_all(sk);
let new_skytex = QUEUED_SKYTEX.lock().take();
let mut new_skylight = QUEUED_SKYLIGHT.lock().take();
let same_file = new_skytex == new_skylight;
if let Some(skytex) = new_skytex {
if let Some((skytex, skylight)) =
Texture::from_cubemap_equirectangular(sk, &skytex, true, i32::MAX)
{
sk.set_skytex(&skytex);
if same_file {
sk.set_skylight(&skylight);
new_skylight = None;
}
if let Some(skytex) = QUEUED_SKYTEX.lock().take() {
if let Ok((_skylight, skytex)) = sk.tex_create_cubemap_file(&skytex, true, i32::MAX) {
sk.render_set_skytex(&skytex);
}
}
if let Some(skylight) = new_skylight {
if let Some((_, skylight)) =
Texture::from_cubemap_equirectangular(sk, &skylight, true, i32::MAX)
{
sk.set_skylight(&skylight);
if let Some(skylight) = QUEUED_SKYLIGHT.lock().take() {
if let Ok((skylight, _)) = sk.tex_create_cubemap_file(&skylight, true, i32::MAX) {
sk.render_set_skylight(skylight);
}
}
}

View File

@@ -6,6 +6,7 @@ use crate::core::resource::ResourceID;
use crate::nodes::drawable::Drawable;
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
use color_eyre::eyre::{bail, ensure, eyre, Result};
use glam::Mat4;
use mint::{ColumnMatrix4, Vector2, Vector3, Vector4};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
@@ -19,13 +20,10 @@ use std::ffi::OsStr;
use std::fmt::Error;
use std::path::PathBuf;
use std::sync::Arc;
use stereokit::color_named::WHITE;
use stereokit::lifecycle::{StereoKitContext, StereoKitDraw};
use stereokit::material::Material;
use stereokit::model::Model as SKModel;
use stereokit::render::RenderLayer;
use stereokit::texture::Texture;
use stereokit::values::Color128;
use stereokit::named_colors::WHITE;
use stereokit::{
Color128, Material, Model as SKModel, RenderLayer, Shader, StereoKitDraw, StereoKitMultiThread,
};
static MODEL_REGISTRY: Registry<Model> = Registry::new();
@@ -53,63 +51,63 @@ impl MaterialParameter {
fn apply_to_material(
&self,
client: &Client,
sk: &impl StereoKitContext,
sk: &impl StereoKitMultiThread,
material: &Material,
parameter_name: &str,
) {
match self {
MaterialParameter::Float(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_float(material, parameter_name, *val);
}
MaterialParameter::Vector2(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_vector2(material, parameter_name, *val);
}
MaterialParameter::Vector3(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_vector3(material, parameter_name, *val);
}
MaterialParameter::Vector4(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_vector4(material, parameter_name, *val);
}
MaterialParameter::Color(val) => {
material.set_parameter(sk, parameter_name, &Color128::from(val.clone()));
sk.material_set_color(material, parameter_name, Color128::from(val.clone()));
}
MaterialParameter::Int(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_int(material, parameter_name, *val);
}
MaterialParameter::Int2(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_int2(material, parameter_name, val.x, val.y);
}
MaterialParameter::Int3(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_int3(material, parameter_name, val.x, val.y, val.z);
}
MaterialParameter::Int4(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_int4(material, parameter_name, val.w, val.x, val.y, val.z);
}
MaterialParameter::Bool(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_bool(material, parameter_name, *val);
}
MaterialParameter::UInt(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_uint(material, parameter_name, *val);
}
MaterialParameter::UInt2(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_uint2(material, parameter_name, val.x, val.y);
}
MaterialParameter::UInt3(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_uint3(material, parameter_name, val.x, val.y, val.z);
}
MaterialParameter::UInt4(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_uint4(material, parameter_name, val.w, val.x, val.y, val.z);
}
MaterialParameter::Matrix(val) => {
material.set_parameter(sk, parameter_name, val);
sk.material_set_matrix(material, parameter_name, Mat4::from(*val));
}
MaterialParameter::Texture(resource) => {
let Some(texture_path) = resource.get_file(
&client.base_resource_prefixes.lock().clone(),
&[OsStr::new("png"), OsStr::new("jpg")],
) else { return; };
if let Some(tex) = Texture::from_file(sk, texture_path, true, 0) {
material.set_parameter(sk, parameter_name, &tex);
) else {return};
if let Ok(tex) = sk.tex_create_file(texture_path, true, 0) {
sk.material_set_texture(material, parameter_name, &tex);
}
}
}
@@ -188,14 +186,15 @@ impl Model {
Ok(())
}
fn draw(&self, sk: &StereoKitDraw) {
fn draw(&self, sk: &impl StereoKitDraw) {
let sk_model = self
.sk_model
.get_or_try_init(|| -> color_eyre::eyre::Result<SendWrapper<SKModel>> {
let pending_model_path = self.pending_model_path.get().ok_or(Error)?;
let model = SKModel::from_file(sk, pending_model_path.as_path(), None)?;
let model =
sk.model_create_file(pending_model_path.to_str().unwrap(), None::<Shader>)?;
Ok(SendWrapper::new(model.clone()))
Ok(SendWrapper::new(sk.model_copy(model)))
})
.ok();
@@ -203,8 +202,15 @@ impl Model {
{
let mut material_replacements = self.pending_material_replacements.lock();
for (material_idx, replacement_material) in material_replacements.iter() {
if sk_model.get_material(sk, *material_idx as i32).is_some() {
sk_model.set_material(sk, *material_idx as i32, replacement_material);
if sk
.model_get_material(sk_model.as_ref(), *material_idx as i32)
.is_some()
{
sk.model_set_material(
sk_model.as_ref(),
*material_idx as i32,
replacement_material.as_ref().as_ref(),
);
}
}
material_replacements.clear();
@@ -214,20 +220,24 @@ impl Model {
let mut material_parameters = self.pending_material_parameters.lock();
for ((material_idx, parameter_name), parameter_value) in material_parameters.drain()
{
let Some(material) = sk_model.get_material(sk, material_idx) else {continue};
let new_material = material.clone();
let Some(material) = sk.model_get_material(sk_model.as_ref(), material_idx) else {continue};
let new_material = sk.material_copy(material);
parameter_value.apply_to_material(
&client,
sk,
&new_material,
parameter_name.as_str(),
);
sk_model.set_material(sk, material_idx, &new_material);
sk.model_set_material(sk_model.as_ref(), material_idx, &new_material);
}
}
let global_transform = self.space.global_transform().into();
sk_model.draw(sk, global_transform, WHITE, RenderLayer::Layer0);
sk.model_draw(
sk_model.as_ref(),
self.space.global_transform(),
WHITE,
RenderLayer::LAYER0,
);
}
}
}
@@ -240,7 +250,7 @@ impl Drop for Model {
}
}
pub fn draw_all(sk: &StereoKitDraw) {
pub fn draw_all(sk: &impl StereoKitDraw) {
for model in MODEL_REGISTRY.get_valid_contents() {
if model.enabled.load(Ordering::Relaxed) {
model.draw(sk);

View File

@@ -17,13 +17,7 @@ use send_wrapper::SendWrapper;
use serde::Deserialize;
use stardust_xr::{schemas::flex::deserialize, values::Transform};
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
use stereokit::{
color_named::WHITE,
font::Font,
lifecycle::StereoKitDraw,
text::{self, TextAlign, TextFit, TextStyle},
values::Color128,
};
use stereokit::{named_colors::WHITE, Color128, StereoKitDraw, TextAlign, TextFit, TextStyle};
static TEXT_REGISTRY: Registry<Text> = Registry::new();
@@ -96,15 +90,17 @@ impl Text {
Ok(text)
}
fn draw(&self, sk: &StereoKitDraw) {
fn draw(&self, sk: &impl StereoKitDraw) {
let style = self.style.get_or_try_init(
|| -> Result<SendWrapper<TextStyle>, color_eyre::eyre::Error> {
let font = self
.font_path
.as_deref()
.and_then(|path| Font::from_file(sk, path))
.unwrap_or_else(|| Font::default(sk));
Ok(SendWrapper::new(TextStyle::new(sk, font, 1.0, WHITE)))
.and_then(|path| sk.font_create(path).ok())
.unwrap_or_else(|| sk.font_find("default/font").unwrap());
Ok(SendWrapper::new(unsafe {
sk.text_make_style(font, 1.0, WHITE)
}))
},
);
@@ -117,28 +113,36 @@ impl Text {
data.character_height,
));
if let Some(bounds) = data.bounds {
text::draw_in(
sk,
sk.text_add_in(
&data.text,
transform,
bounds / data.character_height,
data.fit,
style,
**style,
data.bounds_align,
data.text_align,
vec3(0.0, 0.0, 0.0),
Color128::from(data.color),
Color128::from([
data.color.red(),
data.color.green(),
data.color.blue(),
data.color.alpha(),
]),
);
} else {
text::draw_at(
sk,
sk.text_add_at(
&data.text,
transform,
style,
**style,
data.bounds_align,
data.text_align,
vec3(0.0, 0.0, 0.0),
data.color,
Color128::from([
data.color.red(),
data.color.green(),
data.color.blue(),
data.color.alpha(),
]),
);
}
}
@@ -171,7 +175,7 @@ impl Drop for Text {
}
}
pub fn draw_all(sk: &StereoKitDraw) {
pub fn draw_all(sk: &impl StereoKitDraw) {
for text in TEXT_REGISTRY.get_valid_contents() {
if text.enabled.load(Ordering::Relaxed) {
text.draw(sk);

View File

@@ -6,7 +6,7 @@ use crate::{
use color_eyre::eyre::Result;
use glam::{vec3, Mat4};
use std::sync::Arc;
use stereokit::input::StereoKitInput;
use stereokit::StereoKitMultiThread;
use tracing::instrument;
lazy_static::lazy_static! {
@@ -21,7 +21,7 @@ fn create() -> Arc<Node> {
}
#[instrument(level = "debug", name = "Update HMD Pose", skip(sk))]
pub fn frame(sk: &impl StereoKitInput) {
pub fn frame(sk: &impl StereoKitMultiThread) {
let spatial = HMD
.spatial
.get()

View File

@@ -12,6 +12,7 @@ use crate::{
};
use color_eyre::eyre::{eyre, Result};
use lazy_static::lazy_static;
use nanoid::nanoid;
use serde::Deserialize;
use stardust_xr::{
schemas::flex::{deserialize, flexbuffers, serialize},
@@ -38,6 +39,7 @@ impl EnvironmentItem {
pub fn add_to(node: &Arc<Node>, path: String) {
Item::add_to(
node,
nanoid!(),
&ITEM_TYPE_INFO_ENVIRONMENT,
ItemType::Environment(EnvironmentItem { path }),
);

View File

@@ -88,12 +88,13 @@ pub struct Item {
impl Item {
pub fn add_to(
node: &Arc<Node>,
uid: String,
type_info: &'static TypeInfo,
specialization: ItemType,
) -> Arc<Self> {
let item = Item {
node: Arc::downgrade(node),
uid: node.uid.clone(),
uid,
type_info,
captured_acceptor: Default::default(),
specialization,
@@ -185,7 +186,7 @@ impl Deref for ItemType {
match self {
ItemType::Environment(item) => item,
#[cfg(feature = "wayland")]
ItemType::Panel(item) => &**item,
ItemType::Panel(item) => item.as_ref(),
}
}
}

View File

@@ -272,3 +272,8 @@ impl Debug for Node {
.finish()
}
}
impl Drop for Node {
fn drop(&mut self) {
// Debug breakpoint
}
}

View File

@@ -1,4 +1,4 @@
use crate::core::client::Client;
use crate::{core::client::Client, wayland::WAYLAND_DISPLAY, STARDUST_INSTANCE};
use super::{
items::{ItemAcceptor, TypeInfo},
@@ -9,7 +9,7 @@ use color_eyre::eyre::Result;
use glam::Mat4;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use stardust_xr::schemas::flex::{deserialize, flexbuffers, serialize};
use stardust_xr::schemas::flex::{deserialize, serialize};
use std::{
fmt::Debug,
sync::{Arc, Weak},
@@ -86,6 +86,10 @@ impl Debug for StartupSettings {
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
let node = Node::create(client, "", "startup", false);
node.add_local_signal("create_startup_settings", create_startup_settings_flex);
node.add_local_method(
"get_connection_environment",
get_connection_environment_flex,
);
node.add_to_scenegraph().map(|_| ())
}
@@ -94,9 +98,13 @@ pub fn create_startup_settings_flex(
calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
let name = flexbuffers::Reader::get_root(data)?.get_str()?;
let node =
Node::create(&calling_client, "/startup/settings", name, true).add_to_scenegraph()?;
let node = Node::create(
&calling_client,
"/startup/settings",
deserialize(data)?,
true,
)
.add_to_scenegraph()?;
StartupSettings::add_to(&node);
node.add_local_signal("set_root", StartupSettings::set_root_flex);
@@ -111,3 +119,28 @@ pub fn create_startup_settings_flex(
Ok(())
}
macro_rules! var_env_insert {
($env:ident, $name:ident) => {
$env.insert(stringify!($name).to_string(), $name.get().unwrap().clone());
};
}
pub fn get_connection_environment_flex(
_node: &Node,
_calling_client: Arc<Client>,
_data: &[u8],
) -> Result<Vec<u8>> {
let mut env: FxHashMap<String, String> = FxHashMap::default();
var_env_insert!(env, STARDUST_INSTANCE);
#[cfg(feature = "wayland")]
{
var_env_insert!(env, WAYLAND_DISPLAY);
env.insert("GDK_BACKEND".to_string(), "wayland".to_string());
env.insert("QT_QPA_PLATFORM".to_string(), "wayland".to_string());
env.insert("MOZ_ENABLE_WAYLAND".to_string(), "1".to_string());
env.insert("CLUTTER_BACKEND".to_string(), "wayland".to_string());
env.insert("SDL_VIDEODRIVER".to_string(), "wayland".to_string());
}
Ok(serialize(env)?)
}

View File

@@ -14,7 +14,7 @@ use nanoid::nanoid;
use serde::Serialize;
use stardust_xr::schemas::{flat::Datamap, flex::flexbuffers};
use std::{convert::TryFrom, sync::Arc};
use stereokit::input::{ButtonState, Key, Ray as SkRay, StereoKitInput};
use stereokit::{ray_from_mouse, ButtonState, Key, StereoKitMultiThread};
use tracing::instrument;
const SK_KEYMAP: &str = include_str!("sk.kmp");
@@ -57,22 +57,20 @@ impl MousePointer {
})
}
#[instrument(level = "debug", name = "Update Flatscreen Pointer Ray", skip_all)]
pub fn update(&self, sk: &impl StereoKitInput) {
pub fn update(&self, sk: &impl StereoKitMultiThread) {
let mouse = sk.input_mouse();
if let Some(ray) = SkRay::from_mouse(&mouse) {
self.spatial.set_local_transform(
Mat4::look_to_rh(ray.pos.into(), -Vec3::from(ray.dir), vec3(0.0, 1.0, 0.0))
.inverse(),
)
}
let ray = ray_from_mouse(mouse.pos).unwrap();
self.spatial.set_local_transform(
Mat4::look_to_rh(ray.pos.into(), -Vec3::from(ray.dir), vec3(0.0, 1.0, 0.0)).inverse(),
);
{
// Set pointer input datamap
let mut fbb = flexbuffers::Builder::default();
let mut map = fbb.start_map();
map.push(
"select",
if sk.input_key(Key::MouseLeft).contains(ButtonState::Active) {
if sk.input_key(Key::MouseLeft).contains(ButtonState::ACTIVE) {
1.0f32
} else {
0.0f32
@@ -80,7 +78,7 @@ impl MousePointer {
);
map.push(
"grab",
if sk.input_key(Key::MouseRight).contains(ButtonState::Active) {
if sk.input_key(Key::MouseRight).contains(ButtonState::ACTIVE) {
1.0f32
} else {
0.0f32
@@ -96,7 +94,7 @@ impl MousePointer {
self.send_keyboard_input(sk);
}
fn send_keyboard_input(&self, sk: &impl StereoKitInput) {
fn send_keyboard_input(&self, sk: &impl StereoKitMultiThread) {
let rx = PULSE_RECEIVER_REGISTRY
.get_valid_contents()
.into_iter()
@@ -128,9 +126,9 @@ impl MousePointer {
.filter_map(|i| Some((i, Key::try_from(i).ok()?)))
.map(|(i, k)| (i - 8, sk.input_key(k)));
for (key, state) in keys {
if state.contains(ButtonState::JustActive) {
if state.contains(ButtonState::JUST_ACTIVE) {
keys_down.push(key);
} else if state.contains(ButtonState::JustInactive) {
} else if state.contains(ButtonState::JUST_INACTIVE) {
keys_up.push(key);
}
}

View File

@@ -14,7 +14,7 @@ use stardust_xr::{
values::Transform,
};
use std::sync::Arc;
use stereokit::input::{ButtonState, Handed, StereoKitInput};
use stereokit::{ButtonState, Handed, StereoKitMultiThread};
use tracing::instrument;
pub struct SkController {
@@ -35,9 +35,9 @@ impl SkController {
})
}
#[instrument(level = "debug", name = "Update StereoKit Tip Input Method", skip_all)]
pub fn update(&mut self, sk: &impl StereoKitInput) {
pub fn update(&mut self, sk: &impl StereoKitMultiThread) {
let controller = sk.input_controller(self.handed);
*self.input.enabled.lock() = controller.tracked.contains(ButtonState::Active);
*self.input.enabled.lock() = controller.tracked.contains(ButtonState::ACTIVE);
if *self.input.enabled.lock() {
self.input.spatial.set_local_transform_components(
None,

View File

@@ -14,16 +14,13 @@ use stardust_xr::schemas::{
flex::flexbuffers,
};
use std::sync::Arc;
use stereokit::{
input::{ButtonState, Handed, Joint as SkJoint, StereoKitInput},
lifecycle::StereoKitDraw,
};
use stereokit::{ButtonState, HandJoint, Handed, StereoKitMultiThread};
use tracing::instrument;
fn convert_joint(joint: SkJoint) -> Joint {
fn convert_joint(joint: HandJoint) -> Joint {
Joint {
position: joint.position,
rotation: joint.orientation,
position: joint.position.into(),
rotation: joint.orientation.into(),
radius: joint.radius,
}
}
@@ -51,12 +48,12 @@ impl SkHand {
})
}
#[instrument(level = "debug", name = "Update Hand Input Method", skip_all)]
pub fn update(&mut self, sk: &StereoKitDraw) {
pub fn update(&mut self, sk: &impl StereoKitMultiThread) {
let sk_hand = sk.input_hand(self.handed);
if let InputType::Hand(hand) = &mut *self.input.specialization.lock() {
let controller = sk.input_controller(self.handed);
*self.input.enabled.lock() = controller.tracked.contains(ButtonState::Inactive)
&& sk_hand.tracked_state.contains(ButtonState::Active);
*self.input.enabled.lock() = controller.tracked.contains(ButtonState::INACTIVE)
&& sk_hand.tracked_state.contains(ButtonState::ACTIVE);
if *self.input.enabled.lock() {
hand.base.thumb.tip = convert_joint(sk_hand.fingers[0][4]);
hand.base.thumb.distal = convert_joint(sk_hand.fingers[0][3]);
@@ -76,13 +73,13 @@ impl SkHand {
finger.metacarpal = convert_joint(sk_finger[0]);
}
hand.base.palm.position = sk_hand.palm.position;
hand.base.palm.rotation = sk_hand.palm.orientation;
hand.base.palm.position = sk_hand.palm.position.into();
hand.base.palm.rotation = sk_hand.palm.orientation.into();
hand.base.palm.radius =
(sk_hand.fingers[2][0].radius + sk_hand.fingers[2][1].radius) * 0.5;
hand.base.wrist.position = sk_hand.wrist.position;
hand.base.wrist.rotation = sk_hand.wrist.orientation;
hand.base.wrist.position = sk_hand.wrist.position.into();
hand.base.wrist.rotation = sk_hand.wrist.orientation.into();
hand.base.wrist.radius =
(sk_hand.fingers[0][0].radius + sk_hand.fingers[4][0].radius) * 0.5;

View File

@@ -1,4 +1,7 @@
use super::{panel_item::PanelItem, state::WaylandState, surface::CoreSurface};
use crate::wayland::surface::CoreSurface;
use super::state::WaylandState;
use portable_atomic::{AtomicU32, Ordering};
use smithay::{
delegate_compositor,
reexports::wayland_server::protocol::wl_surface::WlSurface,
@@ -14,12 +17,24 @@ impl CompositorHandler for WaylandState {
fn commit(&mut self, surface: &WlSurface) {
debug!(?surface, "Surface commit");
CoreSurface::add_to(&self.display, self.display_handle.clone(), surface);
if let Some(panel_item) = compositor::with_states(surface, |data| {
data.data_map.get::<Arc<PanelItem>>().cloned()
}) {
panel_item.commit_toplevel();
};
let mut count = 0;
let core_surface = compositor::with_states(surface, |data| {
let count_new = data
.data_map
.insert_if_missing_threadsafe(|| AtomicU32::new(0));
if !count_new {
count = data
.data_map
.get::<AtomicU32>()
.unwrap()
.fetch_add(1, Ordering::Relaxed);
}
data.data_map.get::<Arc<CoreSurface>>().cloned()
});
if let Some(core_surface) = core_surface {
core_surface.commit(count);
}
}
}

View File

@@ -15,12 +15,10 @@ use color_eyre::eyre::{ensure, Result};
use global_counter::primitive::exact::CounterU32;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use sk::lifecycle::StereoKitDraw;
use slog::Drain;
use smithay::{
backend::{egl::EGLContext, renderer::gles2::Gles2Renderer},
reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket},
};
use sk::StereoKitDraw;
use smithay::backend::egl::EGLContext;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket};
use std::os::unix::prelude::AsRawFd;
use std::{
ffi::c_void,
@@ -33,6 +31,8 @@ use tokio::{
};
use tracing::{debug, debug_span, info, instrument};
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0);
struct EGLRawHandles {
@@ -59,49 +59,43 @@ fn get_sk_egl() -> Result<EGLRawHandles> {
static GLOBAL_DESTROY_QUEUE: OnceCell<mpsc::Sender<GlobalId>> = OnceCell::new();
pub struct Wayland {
log: slog::Logger,
display: Arc<Mutex<Display<WaylandState>>>,
pub socket_name: String,
join_handle: JoinHandle<Result<()>>,
renderer: Gles2Renderer,
renderer: GlesRenderer,
state: Arc<Mutex<WaylandState>>,
}
impl Wayland {
pub fn new() -> Result<Self> {
let log = ::slog::Logger::root(::tracing_slog::TracingSlogDrain.fuse(), slog::o!());
let egl_raw_handles = get_sk_egl()?;
let renderer = unsafe {
Gles2Renderer::new(
EGLContext::from_raw(
egl_raw_handles.display,
egl_raw_handles.config,
egl_raw_handles.context,
log.clone(),
)?,
log.clone(),
)?
GlesRenderer::new(EGLContext::from_raw(
egl_raw_handles.display,
egl_raw_handles.config,
egl_raw_handles.context,
)?)?
};
let display: Display<WaylandState> = Display::new()?;
let display_handle = display.handle();
let display = Arc::new(Mutex::new(display));
let state = WaylandState::new(log.clone(), display.clone(), display_handle, &renderer);
let state = WaylandState::new(display.clone(), display_handle, &renderer);
let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8);
GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap();
let socket = ListeningSocket::bind_auto("wayland", 0..33)?;
let socket_name = socket.socket_name().unwrap().to_str().unwrap().to_string();
WAYLAND_DISPLAY
.set(socket_name.clone())
.expect("seriously message nova this time they screwed up big time");
info!(socket_name, "Wayland active");
let join_handle =
Wayland::start_loop(display.clone(), socket, state.clone(), global_destroy_queue)?;
Ok(Wayland {
log,
display,
socket_name,
join_handle,
@@ -155,15 +149,15 @@ impl Wayland {
}
#[instrument(level = "debug", name = "Wayland frame", skip(self, sk))]
pub fn update(&mut self, sk: &StereoKitDraw) {
pub fn update(&mut self, sk: &impl StereoKitDraw) {
for core_surface in CORE_SURFACES.get_valid_contents() {
core_surface.process(sk, &mut self.renderer, &self.log);
core_surface.process(sk, &mut self.renderer);
}
self.display.lock().flush_clients().unwrap();
}
pub fn frame_event(&self, sk: &StereoKitDraw) {
pub fn frame_event(&self, sk: &impl StereoKitDraw) {
let state = self.state.lock();
for core_surface in CORE_SURFACES.get_valid_contents() {

View File

@@ -1,7 +1,7 @@
use super::{
seat::{Cursor, SeatData},
surface::CoreSurface,
xdg_shell::{XdgSurfaceData, XdgToplevelData},
xdg_shell::{PopupData, ToplevelData, XdgSurfaceData},
SERIAL_COUNTER,
};
use crate::{
@@ -23,11 +23,18 @@ use lazy_static::lazy_static;
use mint::Vector2;
use nanoid::nanoid;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use rustc_hash::FxHashMap;
use serde::{
de::{Deserializer, Error, SeqAccess, Visitor},
ser::Serializer,
Deserialize, Serialize,
};
use smithay::{
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel::{
XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE,
wayland_protocols::xdg::shell::server::{
xdg_popup::XdgPopup,
xdg_surface::XdgSurface,
xdg_toplevel::{XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE},
},
wayland_server::{
backend::Credentials, protocol::wl_surface::WlSurface, Resource, Weak as WlWeak,
@@ -44,55 +51,92 @@ lazy_static! {
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
type_name: "panel",
aliased_local_signals: vec![
"apply_cursor_material",
"apply_toplevel_material",
"apply_surface_material",
"configure_toplevel",
"set_toplevel_capabilities",
"pointer_set_active",
"pointer_scroll",
"pointer_button",
"pointer_motion",
"keyboard_set_active",
"keyboard_key",
"keyboard_set_keymap_names",
"keyboard_set_keymap_string",
"close",
],
aliased_local_methods: vec![],
aliased_remote_signals: vec!["commit_toplevel", "recommend_toplevel_state", "set_cursor"],
aliased_remote_signals: vec![
"commit_toplevel",
"recommend_toplevel_state",
"set_cursor",
"new_popup",
"reposition_popup",
"drop_popup",
],
ui: Default::default(),
items: Registry::new(),
acceptors: Registry::new(),
};
}
#[derive(Debug, Clone, Serialize)]
pub struct ToplevelState {
#[serde(skip_serializing)]
pub mapped: bool,
#[serde(skip_serializing)]
pub parent: Option<WlWeak<XdgToplevel>>,
pub title: Option<String>,
pub app_id: Option<String>,
pub size: Vector2<u32>,
pub max_size: Option<Vector2<u32>>,
pub min_size: Option<Vector2<u32>>,
pub states: Vec<u32>,
#[serde(skip_serializing)]
pub queued_state: Option<Box<ToplevelState>>,
/// An ID for a surface inside this panel item
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum SurfaceID {
Cursor,
Toplevel,
Popup(String),
}
impl Default for ToplevelState {
impl Default for SurfaceID {
fn default() -> Self {
Self {
mapped: false,
parent: None,
title: None,
app_id: None,
size: Vector2::from([0; 2]),
max_size: None,
min_size: None,
states: Vec::new(),
queued_state: None,
Self::Toplevel
}
}
impl<'de> serde::Deserialize<'de> for SurfaceID {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_seq(SurfaceIDVisitor)
}
}
struct SurfaceIDVisitor;
impl<'de> Visitor<'de> for SurfaceIDVisitor {
type Value = SurfaceID;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("idk")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let Some(discrim) = seq.next_element()? else {
return Err(A::Error::missing_field("discrim"));
};
// idk if you wanna check for extraneous elements
// I didn't bother
match discrim {
"Cursor" => Ok(SurfaceID::Cursor),
"Toplevel" => Ok(SurfaceID::Toplevel),
"Popup" => {
let Some(text) = seq.next_element()? else {
return Err(A::Error::missing_field("popup_text"));
};
Ok(SurfaceID::Popup(text))
}
_ => Err(A::Error::unknown_variant(
discrim,
&["Cursor", "Toplevel", "Popup"],
)),
}
}
}
impl serde::Serialize for SurfaceID {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Cursor => ["Cursor"].serialize(serializer),
Self::Toplevel => ["Toplevel"].serialize(serializer),
Self::Popup(text) => ["Popup", text].serialize(serializer),
}
}
}
@@ -108,11 +152,15 @@ pub enum RecommendedState {
}
pub struct PanelItem {
pub uid: String,
node: Weak<Node>,
client_credentials: Option<Credentials>,
cursor: Mutex<Option<WlWeak<WlSurface>>>,
pub seat_data: Arc<SeatData>,
toplevel: WlWeak<XdgToplevel>,
pub cursor: Mutex<Option<WlWeak<WlSurface>>>,
seat_data: Arc<SeatData>,
popups: Mutex<FxHashMap<String, WlWeak<XdgPopup>>>,
pointer_grab: Mutex<Option<SurfaceID>>,
keyboard_grab: Mutex<Option<SurfaceID>>,
}
impl PanelItem {
pub fn create(
@@ -122,51 +170,49 @@ impl PanelItem {
seat_data: Arc<SeatData>,
) -> (Arc<Node>, Arc<PanelItem>) {
debug!(?toplevel, ?client_credentials, "Create panel item");
let uid = nanoid!();
let node = Arc::new(Node::create(
&INTERNAL_CLIENT,
"/item/panel/item",
&nanoid!(),
&uid,
true,
));
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
let panel_item = Arc::new(PanelItem {
uid: uid.clone(),
node: Arc::downgrade(&node),
client_credentials,
toplevel: toplevel.downgrade(),
cursor: Mutex::new(None),
seat_data,
toplevel: toplevel.downgrade(),
popups: Mutex::new(FxHashMap::default()),
pointer_grab: Mutex::new(None),
keyboard_grab: Mutex::new(None),
});
panel_item
.seat_data
.new_panel_item(&panel_item, &toplevel, &wl_surface);
.new_surface(&wl_surface, Arc::downgrade(&panel_item));
let item = Item::add_to(
&node,
uid,
&ITEM_TYPE_INFO_PANEL,
ItemType::Panel(panel_item.clone()),
);
node.add_local_signal(
"apply_toplevel_material",
PanelItem::apply_toplevel_material_flex,
"apply_surface_material",
PanelItem::apply_surface_material_flex,
);
node.add_local_signal("configure_toplevel", PanelItem::configure_toplevel_flex);
if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE {
node.add_local_signal(
"set_toplevel_capabilities",
PanelItem::set_toplevel_capabilities_flex,
);
}
node.add_local_signal(
"apply_cursor_material",
PanelItem::apply_cursor_material_flex,
"set_toplevel_capabilities",
PanelItem::set_toplevel_capabilities_flex,
);
node.add_local_signal("pointer_set_active", PanelItem::pointer_set_active_flex);
node.add_local_signal("pointer_scroll", PanelItem::pointer_scroll_flex);
node.add_local_signal("pointer_button", PanelItem::pointer_button_flex);
node.add_local_signal("pointer_motion", PanelItem::pointer_motion_flex);
node.add_local_signal("keyboard_set_active", PanelItem::keyboard_set_active_flex);
node.add_local_signal(
"keyboard_set_keymap_string",
PanelItem::keyboard_set_keymap_string_flex,
@@ -195,38 +241,26 @@ impl PanelItem {
(node, panel_item)
}
pub fn from_node(node: &Node) -> Option<&PanelItem> {
node.item.get().and_then(|item| match &item.specialization {
ItemType::Panel(panel_item) => Some(&**panel_item),
_ => None,
})
pub fn from_node(node: &Node) -> Option<Arc<PanelItem>> {
let ItemType::Panel(panel_item) = &node.item.get()?.specialization else {return None};
Some(panel_item.clone())
}
fn toplevel_surface_data(&self) -> Option<XdgSurfaceData> {
Some(
self.toplevel
.upgrade()
.ok()?
.data::<XdgToplevelData>()?
.xdg_surface_data
.clone(),
)
fn toplevel(&self) -> XdgToplevel {
self.toplevel.upgrade().unwrap()
}
fn toplevel_state(&self) -> Option<Arc<Mutex<ToplevelState>>> {
Some(
self.toplevel
.upgrade()
.ok()?
.data::<XdgToplevelData>()?
.state
.clone(),
)
fn toplevel_xdg_surface(&self) -> XdgSurface {
let toplevel = self.toplevel();
let data = ToplevelData::get(&toplevel).lock();
data.xdg_surface()
}
pub fn toplevel_wl_surface(&self) -> Option<WlSurface> {
self.toplevel_surface_data()?.wl_surface.upgrade().ok()
fn toplevel_wl_surface(&self) -> WlSurface {
XdgSurfaceData::get(&self.toplevel_xdg_surface())
.lock()
.wl_surface()
}
fn core_surface(&self) -> Option<Arc<CoreSurface>> {
compositor::with_states(&self.toplevel_wl_surface()?, |data| {
compositor::with_states(&self.toplevel_wl_surface(), |data| {
data.data_map.get::<Arc<CoreSurface>>().cloned()
})
}
@@ -235,55 +269,49 @@ impl PanelItem {
core_surface.flush_clients();
}
}
fn apply_toplevel_material_flex(
node: &Node,
calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
#[derive(Debug, Deserialize)]
struct SurfaceMaterialInfo<'a> {
model_path: &'a str,
idx: u32,
}
let info: SurfaceMaterialInfo = deserialize(data)?;
let model_node = calling_client
.scenegraph
.get_node(info.model_path)
.ok_or_else(|| eyre!("Model node not found"))?;
let Some(Drawable::Model(model)) = model_node.drawable.get() else {bail!("Node is not a model")};
debug!(?info, "Apply toplevel material");
if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization {
if let Some(core_surface) = panel_item.core_surface() {
core_surface.apply_material(model.clone(), info.idx);
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
match id {
SurfaceID::Cursor => self.cursor.lock().clone()?.upgrade().ok(),
SurfaceID::Toplevel => Some(self.toplevel_wl_surface()),
SurfaceID::Popup(popup) => {
let popups = self.popups.lock();
let popup = popups.get(popup)?.upgrade().ok()?;
let surf = PopupData::get(&popup).lock().wl_surface();
Some(surf)
}
}
Ok(())
}
fn wl_surface_from_id_result(&self, id: &SurfaceID) -> Result<WlSurface> {
self.wl_surface_from_id(id)
.ok_or(eyre!("Surface with ID not found"))
}
fn apply_cursor_material_flex(
fn apply_surface_material_flex(
node: &Node,
calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Some(cursor) = panel_item.cursor.lock().as_ref().and_then(|c| c.upgrade().ok()) else { return Ok(())};
let Some(core_surface) = CoreSurface::from_wl_surface(&cursor) else { return Ok(()) };
#[derive(Debug, Deserialize)]
struct SurfaceMaterialInfo<'a> {
surface: SurfaceID,
model_path: &'a str,
idx: u32,
}
let info: SurfaceMaterialInfo = deserialize(data)?;
debug!(?cursor, ?info, "Apply cursor material");
let Some(wl_surface) = panel_item.wl_surface_from_id(&info.surface) else { return Ok(()) };
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else { return Ok(()) };
let model_node = calling_client
.scenegraph
.get_node(info.model_path)
.ok_or_else(|| eyre!("Model node not found"))?;
let Some(Drawable::Model(model)) = model_node.drawable.get() else {bail!("Node is not a model")};
debug!(?info, "Apply surface material");
core_surface.apply_material(model.clone(), info.idx);
Ok(())
@@ -291,69 +319,57 @@ impl PanelItem {
fn pointer_motion_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
debug!(?toplevel, "Pointer deactivate");
let (surface_id, position): (SurfaceID, Vector2<f64>) = deserialize(data)?;
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
debug!(?surface_id, ?position, "Pointer deactivate");
panel_item
.seat_data
.pointer_event(&toplevel, PointerEvent::Motion(deserialize(data)?));
.pointer_event(&wl_surface, PointerEvent::Motion(position));
panel_item.flush_clients();
Ok(())
}
fn pointer_button_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let (button, state): (u32, u32) = deserialize(data)?;
debug!(button, state, "Pointer button");
let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(data)?;
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
debug!(?surface_id, button, state, "Pointer button");
panel_item
.seat_data
.pointer_event(&toplevel, PointerEvent::Button { button, state });
.pointer_event(&wl_surface, PointerEvent::Button { button, state });
panel_item.flush_clients();
Ok(())
}
fn pointer_scroll_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
#[derive(Debug, Deserialize)]
struct PointerScrollArgs {
struct PointerScrollInfo {
surface_id: SurfaceID,
axis_continuous: Option<Vector2<f32>>,
axis_discrete: Option<Vector2<f32>>,
}
let args: PointerScrollArgs = deserialize(data)?;
let info: PointerScrollInfo = deserialize(data)?;
let wl_surface = panel_item.wl_surface_from_id_result(&info.surface_id)?;
debug!(?args, "Pointer scroll");
debug!(?info, "Pointer scroll");
panel_item.seat_data.pointer_event(
&toplevel,
&wl_surface,
PointerEvent::Scroll {
axis_continuous: args.axis_continuous,
axis_discrete: args.axis_discrete,
axis_continuous: info.axis_continuous,
axis_discrete: info.axis_discrete,
},
);
panel_item.flush_clients();
Ok(())
}
fn pointer_set_active_flex(
node: &Node,
_calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let active: bool = deserialize(data)?;
debug!(?toplevel, active, "Pointer set active");
panel_item.seat_data.set_pointer_active(&toplevel, active);
panel_item.flush_clients();
Ok(())
}
fn keyboard_set_keymap_string_flex(
node: &Node,
@@ -397,38 +413,32 @@ impl PanelItem {
}
fn keyboard_set_keymap_flex(node: &Node, keymap: &Keymap) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let toplevel = panel_item.toplevel_wl_surface();
debug!(?toplevel, "Keyboard set keymap");
panel_item.seat_data.set_keymap(&toplevel, keymap);
let mut surfaces = vec![toplevel];
surfaces.extend(panel_item.popups.lock().values().filter_map(|p| {
let popup = p.upgrade().ok()?;
let popup_data = PopupData::get(&popup).lock();
let xdg_surface = popup_data.xdg_surface();
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface).lock();
Some(xdg_surface_data.wl_surface())
}));
Ok(())
}
fn keyboard_set_active_flex(
node: &Node,
_calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let active: bool = deserialize(data)?;
debug!(?toplevel, active, "Keyboard set active");
panel_item.seat_data.set_keyboard_active(&toplevel, active);
panel_item.seat_data.set_keymap(keymap, surfaces);
Ok(())
}
fn keyboard_key_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let (key, state): (u32, u32) = deserialize(data)?;
let (surface_id, key, state): (SurfaceID, u32, u32) = deserialize(data)?;
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
debug!(key, state, "Set keyboard key state");
panel_item
.seat_data
.keyboard_event(&toplevel, KeyboardEvent::Key { key, state });
.keyboard_event(&wl_surface, KeyboardEvent::Key { key, state });
Ok(())
}
@@ -441,7 +451,7 @@ impl PanelItem {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Some(core_surface) = panel_item.core_surface() else { return Ok(()) };
let Ok(xdg_toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let Some(xdg_surface) = panel_item.toplevel_surface_data().and_then(|d| d.xdg_surface.upgrade().ok()) else { return Ok(()) };
let xdg_surface = panel_item.toplevel_xdg_surface();
#[derive(Debug, Deserialize)]
struct ConfigureToplevelInfo {
@@ -452,9 +462,6 @@ impl PanelItem {
let info: ConfigureToplevelInfo = deserialize(data)?;
debug!(info = ?&info, "Configure toplevel info");
if let Some(xdg_state) = panel_item.toplevel_state() {
xdg_state.lock().queued_state.as_mut().unwrap().states = info.states.clone();
}
if let Some(bounds) = info.bounds {
if xdg_toplevel.version() > EVT_CONFIGURE_BOUNDS_SINCE {
xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32);
@@ -483,7 +490,10 @@ impl PanelItem {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Some(core_surface) = panel_item.core_surface() else { return Ok(()) };
let Ok(xdg_toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let Some(xdg_surface) = panel_item.toplevel_surface_data().and_then(|d| d.xdg_surface.upgrade().ok()) else { return Ok(()) };
if xdg_toplevel.version() < EVT_WM_CAPABILITIES_SINCE {
return Ok(());
}
let xdg_surface = panel_item.toplevel_xdg_surface();
let capabilities: Vec<u8> = deserialize(data)?;
debug!("Set toplevel capabilities");
@@ -495,23 +505,22 @@ impl PanelItem {
}
pub fn commit_toplevel(&self) {
let mapped_size = self.core_surface().and_then(|c| c.size());
let Some(state) = self.toplevel_state() else { return };
let mut state = state.lock();
let mut queued_state = state.queued_state.take().unwrap();
queued_state.mapped = mapped_size.is_some();
if let Some(size) = mapped_size {
queued_state.size = size;
}
*state = (*queued_state).clone();
state.queued_state = Some(queued_state);
// let mapped_size = self.core_surface().and_then(|c| c.size());
let toplevel = self.toplevel();
let state = ToplevelData::get(&toplevel);
let state = state.lock();
// let mut queued_state = state.queued_state.take().unwrap();
// queued_state.mapped = mapped_size.is_some();
// if let Some(size) = mapped_size {
// queued_state.size = size;
// queued_state.geometry.update_to_surface_size(size);
// }
// *state = (*queued_state).clone();
// state.queued_state = Some(queued_state);
debug!(state = ?&state.mapped.then_some(&*state), "Commit toplevel");
debug!(state = ?&*state, "Commit toplevel");
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal(
"commit_toplevel",
&serialize(&state.mapped.then_some(&*state)).unwrap(),
);
let _ = node.send_remote_signal("commit_toplevel", &serialize(&*state).unwrap());
}
pub fn recommend_toplevel_state(&self, state: RecommendedState) {
@@ -522,6 +531,56 @@ impl PanelItem {
let _ = node.send_remote_signal("recommend_toplevel_state", &data);
}
pub fn new_popup(&self, popup: &XdgPopup, data: &PopupData) {
let uid = data.uid.clone();
self.popups.lock().insert(uid.clone(), popup.downgrade());
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal("new_popup", &serialize(&(&uid, data)).unwrap());
}
// pub fn commit_popup(&self, data: &PopupData) {
// let xdg_surf = data.xdg_surface.upgrade().unwrap();
// let surf = xdg_surf
// .data::<XdgSurfaceData>()
// .unwrap()
// .wl_surface
// .upgrade()
// .unwrap();
// let core_surface =
// compositor::with_states(&surf, |s| s.data_map.get::<Arc<CoreSurface>>().cloned())
// .unwrap();
// let mut popup_state = data.state.lock();
// popup_state.mapped = core_surface.size().is_some();
// }
pub fn reposition_popup(&self, popup_state: &PopupData) {
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal(
"reposition_popup",
&serialize(popup_state.positioner_data().unwrap()).unwrap(),
);
}
pub fn drop_popup(&self, uid: &str) {
if let Some(popup) = self
.popups
.lock()
.remove(uid)
.and_then(|popup| popup.upgrade().ok()?.data::<Arc<PopupData>>().cloned())
{
self.seat_data.drop_surface(&popup.wl_surface());
}
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal("drop_popup", &serialize(uid).unwrap());
}
pub fn grab_keyboard(&self, sid: Option<SurfaceID>) {
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal("grab_keyboard", &serialize(sid).unwrap());
}
pub fn set_cursor(&self, surface: Option<&WlSurface>, hotspot_x: i32, hotspot_y: i32) {
let Some(node) = self.node.upgrade() else { return };
debug!(?surface, hotspot_x, hotspot_y, "Set cursor size");
@@ -540,8 +599,8 @@ impl PanelItem {
}
pub fn on_drop(&self) {
let Ok(toplevel) = self.toplevel.upgrade() else { return; };
self.seat_data.drop_panel_item(&toplevel);
let toplevel = self.toplevel_wl_surface();
self.seat_data.drop_surface(&toplevel);
debug!("Drop panel item");
}
@@ -559,12 +618,35 @@ impl ItemSpecialization for PanelItem {
})
.map(|cursor| cursor.hotspot);
let toplevel_state = self.toplevel_state();
let toplevel_state = toplevel_state.as_ref().map(|state| state.lock());
let toplevel_state = toplevel_state
.as_ref()
.and_then(|state| state.mapped.then_some(&**state));
let toplevel = self.toplevel();
let toplevel_state = ToplevelData::get(&toplevel);
let toplevel_state = toplevel_state.lock().clone();
serialize((id, (toplevel_state, cursor_size.zip(cursor_hotspot)))).unwrap()
let popups = self
.popups
.lock()
.values()
.filter_map(|v| Some(v.upgrade().ok()?.data::<Mutex<PopupData>>()?.lock().clone()))
.collect::<Vec<_>>();
let pointer_grab = self.pointer_grab.lock().clone();
let keyboard_grab = self.keyboard_grab.lock().clone();
serialize((
id,
(
cursor_size.zip(cursor_hotspot),
toplevel_state,
popups,
pointer_grab,
keyboard_grab,
),
))
.unwrap()
}
}
impl Drop for PanelItem {
fn drop(&mut self) {
// Dropped panel item, basically just a debug breakpoint place
}
}

View File

@@ -1,9 +1,8 @@
use crate::core::task;
use super::{
panel_item::PanelItem, state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE,
SERIAL_COUNTER,
};
use crate::core::task;
use color_eyre::eyre::Result;
use mint::Vector2;
use nanoid::nanoid;
@@ -13,20 +12,16 @@ use rand::{seq::IteratorRandom, thread_rng};
use rustc_hash::{FxHashMap, FxHashSet};
use smithay::{
input::keyboard::{KeymapFile, ModifiersState},
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel,
wayland_server::{
backend::{ClientId, GlobalId, ObjectId},
protocol::{
wl_keyboard::{self, KeyState, WlKeyboard},
wl_pointer::{self, Axis, ButtonState, WlPointer},
wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE},
wl_surface::WlSurface,
wl_touch::{self, WlTouch},
},
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
Weak as WlWeak,
reexports::wayland_server::{
backend::{ClientId, GlobalId, ObjectId},
protocol::{
wl_keyboard::{self, KeyState, WlKeyboard},
wl_pointer::{self, Axis, ButtonState, WlPointer},
wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE},
wl_surface::WlSurface,
wl_touch::{self, WlTouch},
},
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak as WlWeak,
},
wayland::compositor,
};
@@ -48,7 +43,7 @@ impl KeyboardInfo {
pub fn new(keymap: &Keymap) -> Self {
KeyboardInfo {
state: xkb::State::new(keymap),
keymap: KeymapFile::new(keymap, None),
keymap: KeymapFile::new(keymap),
mods: ModifiersState::default(),
keys: FxHashSet::default(),
}
@@ -109,51 +104,34 @@ pub enum KeyboardEvent {
}
const POINTER_EVENT_TIMEOUT: Duration = Duration::from_secs(1);
struct PanelInfo {
struct SurfaceInfo {
wl_surface: WlWeak<WlSurface>,
panel_item: Weak<PanelItem>,
toplevel: WlWeak<XdgToplevel>,
focus: WlWeak<WlSurface>,
pointer_queue: Option<VecDeque<PointerEvent>>,
pointer_queue: VecDeque<PointerEvent>,
pointer_latest_event: Instant,
keyboard_queue: Option<VecDeque<KeyboardEvent>>,
keyboard_queue: VecDeque<KeyboardEvent>,
keyboard_info: Option<KeyboardInfo>,
}
impl PanelInfo {
fn new(panel_item: &Arc<PanelItem>, toplevel: &XdgToplevel, focus: &WlSurface) -> Self {
PanelInfo {
toplevel: toplevel.downgrade(),
panel_item: Arc::downgrade(panel_item),
focus: focus.downgrade(),
pointer_queue: None,
impl SurfaceInfo {
fn new(wl_surface: &WlSurface, panel_item: Weak<PanelItem>) -> Self {
SurfaceInfo {
wl_surface: wl_surface.downgrade(),
panel_item,
pointer_queue: VecDeque::new(),
pointer_latest_event: Instant::now(),
keyboard_queue: None,
keyboard_queue: VecDeque::new(),
keyboard_info: None,
}
}
pub fn set_pointer_active(&mut self, seat_data: &SeatData, active: bool) {
if active && self.pointer_queue.is_none() {
self.pointer_queue.replace(Default::default());
}
if !active && self.pointer_queue.is_some() {
self.pointer_queue.take();
let Ok(focus) = self.focus.upgrade() else {return};
let Some((pointer, pointer_focus)) = seat_data.pointer.get() else {return};
if &*pointer_focus.lock() == &Some(self.toplevel.id()) {
pointer.leave(SERIAL_COUNTER.inc(), &focus);
}
}
}
fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool {
let Ok(focus) = self.focus.upgrade() else { return false; };
let Some(pointer_queue) = self.pointer_queue.as_mut() else { return false; };
let Ok(focus) = self.wl_surface.upgrade() else { return false; };
let Some(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; };
let Some(focus_size) = core_surface.size() else { return false; };
if !pointer_queue.is_empty() {
if !self.pointer_queue.is_empty() {
self.pointer_latest_event = Instant::now();
}
while let Some(event) = pointer_queue.pop_front() {
while let Some(event) = self.pointer_queue.pop_front() {
match (locked, event) {
(false, PointerEvent::Motion(pos)) => {
pointer.enter(
@@ -218,22 +196,8 @@ impl PanelInfo {
locked
}
pub fn set_keyboard_active(&mut self, seat_data: &SeatData, active: bool) {
if active && self.keyboard_queue.is_none() {
self.keyboard_queue.replace(Default::default());
}
if !active && self.keyboard_queue.is_some() {
self.keyboard_queue.take();
let Ok(focus) = self.focus.upgrade() else {return};
let Some((keyboard, keyboard_focus)) = seat_data.keyboard.get() else {return};
if &*keyboard_focus.lock() == &Some(self.toplevel.id()) {
keyboard.leave(SERIAL_COUNTER.inc(), &focus);
}
}
}
fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, mut locked: bool) -> bool {
let Ok(focus) = self.focus.upgrade() else { return false; };
let Some(keyboard_queue) = self.keyboard_queue.as_mut() else { return false; };
let Ok(focus) = self.wl_surface.upgrade() else { return false; };
let Some(info) = self.keyboard_info.as_mut() else { return true; };
if !locked {
@@ -241,7 +205,7 @@ impl PanelInfo {
keyboard.repeat_info(0, 0);
locked = info.keymap.send(keyboard).is_ok();
}
while let Some(event) = keyboard_queue.pop_front() {
while let Some(event) = self.keyboard_queue.pop_front() {
debug!(locked, ?event, "Process keyboard event");
match (locked, event) {
(true, KeyboardEvent::Keymap) => {
@@ -267,9 +231,9 @@ impl PanelInfo {
pub struct SeatData {
client: ClientId,
global_id: OnceCell<GlobalId>,
panels: Mutex<FxHashMap<ObjectId, PanelInfo>>,
pointer: OnceCell<(WlPointer, Mutex<Option<ObjectId>>)>,
keyboard: OnceCell<(WlKeyboard, Mutex<Option<ObjectId>>)>,
surfaces: Mutex<FxHashMap<ObjectId, SurfaceInfo>>,
pointer: OnceCell<(WlPointer, Mutex<ObjectId>)>,
keyboard: OnceCell<(WlKeyboard, Mutex<ObjectId>)>,
touch: OnceCell<WlTouch>,
}
impl SeatData {
@@ -277,7 +241,7 @@ impl SeatData {
let seat_data = Arc::new(SeatData {
client,
global_id: OnceCell::new(),
panels: Mutex::new(FxHashMap::default()),
surfaces: Mutex::new(FxHashMap::default()),
pointer: OnceCell::new(),
keyboard: OnceCell::new(),
touch: OnceCell::new(),
@@ -291,144 +255,111 @@ impl SeatData {
seat_data
}
// pub fn set_focus(&self, toplevel: &WlSurface, focus: &WlSurface) {
// if let Some(panel_info) = self.panels.lock().get_mut(&toplevel.id()) {
// panel_info.focus = focus.downgrade();
// match panel_info.pointer_queue.back() {
// None => (),
// Some(&PointerEvent::Leave) => (),
// _ => panel_info.pointer_queue.push_back(PointerEvent::Leave),
// };
// match panel_info.keyboard_queue.back() {
// None => (),
// Some(&KeyboardEvent::Leave) => (),
// _ => panel_info.keyboard_queue.push_back(KeyboardEvent::Leave),
// };
// }
// }
pub fn set_pointer_active(&self, toplevel: &XdgToplevel, active: bool) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
panel_info.set_pointer_active(self, active);
}
pub fn set_keyboard_active(&self, toplevel: &XdgToplevel, active: bool) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
panel_info.set_keyboard_active(self, active);
}
pub fn set_keymap(&self, toplevel: &XdgToplevel, keymap: &Keymap) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
panel_info.keyboard_info.replace(KeyboardInfo::new(keymap));
let Some(keyboard_queue) = panel_info.keyboard_queue.as_mut() else {return};
pub fn set_keymap(&self, keymap: &Keymap, surfaces: Vec<WlSurface>) {
let mut panels = self.surfaces.lock();
let Some((_, focus)) = self.keyboard.get() else {return};
let Some(id) = &*focus.lock() else {return};
if id == &toplevel.id() {
keyboard_queue.push_back(KeyboardEvent::Keymap);
for surface in surfaces {
let Some(surface_info) = panels.get_mut(&surface.id()) else {continue};
surface_info
.keyboard_info
.replace(KeyboardInfo::new(keymap));
if *focus.lock() == surface.id() {
surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap);
}
}
}
pub fn pointer_event(&self, toplevel: &XdgToplevel, event: PointerEvent) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
let Some(pointer_queue) = panel_info.pointer_queue.as_mut() else {return};
pointer_queue.push_back(event);
drop(panels);
pub fn pointer_event(&self, surface: &WlSurface, event: PointerEvent) {
let mut surfaces = self.surfaces.lock();
let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return};
surface_info.pointer_queue.push_back(event);
drop(surfaces);
self.handle_pointer_events();
}
pub fn keyboard_event(&self, toplevel: &XdgToplevel, event: KeyboardEvent) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
let Some(keyboard_queue) = panel_info.keyboard_queue.as_mut() else {return};
keyboard_queue.push_back(event);
drop(panels);
pub fn keyboard_event(&self, surface: &WlSurface, event: KeyboardEvent) {
let mut surfaces = self.surfaces.lock();
let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return};
surface_info.keyboard_queue.push_back(event);
drop(surfaces);
self.handle_keyboard_events();
}
fn handle_pointer_events(&self) {
let mut panels = self.panels.lock();
let mut surfaces = self.surfaces.lock();
let Some((pointer, pointer_focus)) = self.pointer.get() else {return};
let mut pointer_focus = pointer_focus.lock();
loop {
let locked = pointer_focus.is_some();
let locked = !pointer_focus.is_null();
// Pick a pointer to focus on if there is none
if pointer_focus.is_none() {
*pointer_focus = panels
if pointer_focus.is_null() {
*pointer_focus = surfaces
.iter()
.filter(|(_k, v)| v.pointer_queue.is_some())
.filter(|(_k, v)| !v.pointer_queue.as_ref().unwrap().is_empty())
.filter(|(_k, v)| !v.pointer_queue.is_empty())
.map(|(k, _v)| k)
.choose(&mut thread_rng())
.cloned();
.cloned()
.unwrap_or(ObjectId::null());
}
if pointer_focus.is_none() {
if pointer_focus.is_null() {
// If there's still none, guess we're done with pointer events for the time being
break;
}
let Some(panel_info) = panels.get_mut(pointer_focus.as_ref().unwrap()) else {break};
if panel_info.handle_pointer_events(pointer, locked) {
let Some(surface_info) = surfaces.get_mut(&pointer_focus) else {break};
if surface_info.handle_pointer_events(pointer, locked) {
// We haven't gotten to a point where we can switch the focus
break;
} else {
pointer_focus.take();
*pointer_focus = ObjectId::null();
}
}
}
fn handle_keyboard_events(&self) {
let mut panels = self.panels.lock();
let mut surfaces = self.surfaces.lock();
let Some((keyboard, keyboard_focus)) = self.keyboard.get() else {return};
let mut keyboard_focus = keyboard_focus.lock();
loop {
let locked = keyboard_focus.is_some();
let locked = !keyboard_focus.is_null();
// Pick a keyboard to focus on if there is none
if keyboard_focus.is_none() {
*keyboard_focus = panels
if keyboard_focus.is_null() {
*keyboard_focus = surfaces
.iter()
.filter(|(_k, v)| v.keyboard_info.is_some())
.filter(|(_k, v)| v.keyboard_queue.is_some())
.filter(|(_k, v)| !v.keyboard_queue.as_ref().unwrap().is_empty())
.filter(|(_k, v)| !v.keyboard_queue.is_empty())
.map(|(k, _v)| k)
.choose(&mut thread_rng())
.cloned();
.cloned()
.unwrap_or(ObjectId::null());
}
if keyboard_focus.is_none() {
// If there's still none, guess we're done with keyboard events for the time being
break;
}
let Some(panel_info) = panels.get_mut(keyboard_focus.as_ref().unwrap()) else {break};
if panel_info.handle_keyboard_events(keyboard, locked) {
// If there's still none, guess we're done with keyboard events for the time being
let Some(surface_info) = surfaces.get_mut(&keyboard_focus) else {break};
if surface_info.handle_keyboard_events(keyboard, locked) {
// We haven't gotten to a point where we can switch the focus
break;
} else {
keyboard_focus.take();
*keyboard_focus = ObjectId::null();
}
}
}
pub fn new_panel_item(
&self,
panel_item: &Arc<PanelItem>,
toplevel: &XdgToplevel,
focus: &WlSurface,
) {
self.panels
pub fn new_surface(&self, surface: &WlSurface, panel_item: Weak<PanelItem>) {
self.surfaces
.lock()
.insert(toplevel.id(), PanelInfo::new(panel_item, toplevel, focus));
.insert(surface.id(), SurfaceInfo::new(surface, panel_item));
}
pub fn drop_panel_item(&self, toplevel: &XdgToplevel) {
self.panels.lock().remove(&toplevel.id());
pub fn drop_surface(&self, surface: &WlSurface) {
self.surfaces.lock().remove(&surface.id());
if let Some((_, pointer_focus)) = self.pointer.get() {
let mut pointer_focus = pointer_focus.lock();
if *pointer_focus == Some(toplevel.id()) {
pointer_focus.take();
if *pointer_focus == surface.id() {
*pointer_focus = ObjectId::null();
}
}
if let Some((_, keyboard_focus)) = self.keyboard.get() {
let mut keyboard_focus = keyboard_focus.lock();
if *keyboard_focus == Some(toplevel.id()) {
keyboard_focus.take();
if *keyboard_focus == surface.id() {
*keyboard_focus = ObjectId::null();
}
}
}
@@ -478,12 +409,12 @@ impl Dispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
match request {
wl_seat::Request::GetPointer { id } => {
let pointer = data_init.init(id, data.clone());
let _ = data.pointer.set((pointer, Mutex::new(None)));
let _ = data.pointer.set((pointer, Mutex::new(ObjectId::null())));
}
wl_seat::Request::GetKeyboard { id } => {
let keyboard = data_init.init(id, data.clone());
keyboard.repeat_info(0, 0);
let _ = data.keyboard.set((keyboard, Mutex::new(None)));
let _ = data.keyboard.set((keyboard, Mutex::new(ObjectId::null())));
}
wl_seat::Request::GetTouch { id } => {
let _ = data.touch.set(data_init.init(id, data.clone()));
@@ -515,7 +446,7 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
hotspot_y,
} => {
if let Some(surface) = surface.as_ref() {
CoreSurface::add_to(&state.display, dh.clone(), surface);
CoreSurface::add_to(&state.display, dh.clone(), surface, |_| ());
compositor::with_states(surface, |data| {
data.data_map.insert_if_missing_threadsafe(|| {
Arc::new(Mutex::new(Cursor {
@@ -533,10 +464,9 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
let Some((_, focus)) = seat_data.pointer.get() else {return};
let focus = focus.lock();
let Some(id) = &*focus else {return};
let panels = seat_data.panels.lock();
let Some(panel_info) = panels.get(&id) else {return};
let Some(panel_item) = panel_info.panel_item.upgrade() else {return};
let surfaces = seat_data.surfaces.lock();
let Some(surface_info) = surfaces.get(&focus) else {return};
let Some(panel_item) = surface_info.panel_item.upgrade() else {return};
panel_item.set_cursor(surface.as_ref(), hotspot_x, hotspot_y);
}
wl_pointer::Request::Release => (),

View File

@@ -1,11 +1,10 @@
use crate::wayland::seat::SeatData;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use slog::Logger;
use smithay::{
backend::{
allocator::dmabuf::Dmabuf,
renderer::{gles2::Gles2Renderer, ImportDma},
renderer::{gles::GlesRenderer, ImportDma},
},
delegate_dmabuf, delegate_output, delegate_shm,
output::{Mode, Output, Scale, Subpixel},
@@ -49,7 +48,6 @@ impl ClientData for ClientState {
pub struct WaylandState {
pub weak_ref: Weak<Mutex<WaylandState>>,
pub log: Logger,
pub display: Arc<Mutex<Display<WaylandState>>>,
pub display_handle: DisplayHandle,
@@ -66,24 +64,19 @@ pub struct WaylandState {
impl WaylandState {
pub fn new(
log: Logger,
display: Arc<Mutex<Display<WaylandState>>>,
display_handle: DisplayHandle,
renderer: &Gles2Renderer,
renderer: &GlesRenderer,
) -> Arc<Mutex<Self>> {
let compositor_state = CompositorState::new::<Self, _>(&display_handle, log.clone());
// let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle, log.clone());
let kde_decoration_state = KdeDecorationState::new::<Self, _>(
&display_handle,
DecorationMode::Server,
log.clone(),
);
let shm_state = ShmState::new::<Self, _>(&display_handle, vec![], log.clone());
let compositor_state = CompositorState::new::<Self>(&display_handle);
// let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle);
let kde_decoration_state =
KdeDecorationState::new::<Self>(&display_handle, DecorationMode::Server);
let shm_state = ShmState::new::<Self>(&display_handle, vec![]);
let mut dmabuf_state = DmabufState::new();
let dmabuf_global = dmabuf_state.create_global::<Self, _>(
let dmabuf_global = dmabuf_state.create_global::<Self>(
&display_handle,
renderer.dmabuf_formats().cloned().collect::<Vec<_>>(),
log.clone(),
renderer.dmabuf_formats().collect::<Vec<_>>(),
);
let output = Output::new(
"1x".to_owned(),
@@ -93,18 +86,19 @@ impl WaylandState {
make: "Virtual XR Display".to_owned(),
model: "Your Headset Name Here".to_owned(),
},
log.clone(),
);
let _output_global = output.create_global::<Self>(&display_handle);
let mode = Mode {
size: (4096, 4096).into(),
refresh: 60000,
};
output.change_current_state(
Some(Mode {
size: (4096, 4096).into(),
refresh: 60000,
}),
Some(mode),
Some(Transform::Normal),
Some(Scale::Integer(2)),
None,
);
output.set_preferred(mode);
display_handle.create_global::<Self, WlDataDeviceManager, _>(3, ());
display_handle.create_global::<Self, XdgWmBase, _>(5, ());
display_handle.create_global::<Self, ZxdgDecorationManagerV1, _>(1, ());
@@ -114,7 +108,6 @@ impl WaylandState {
Arc::new_cyclic(|weak| {
Mutex::new(WaylandState {
weak_ref: weak.clone(),
log,
display,
display_handle,

View File

@@ -7,10 +7,9 @@ use mint::Vector2;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use send_wrapper::SendWrapper;
use slog::Logger;
use smithay::{
backend::renderer::{
gles2::{Gles2Renderer, Gles2Texture},
gles::{GlesRenderer, GlesTexture},
utils::{import_surface_tree, on_commit_buffer_handler, RendererSurfaceStateUserData},
Renderer, Texture,
},
@@ -22,35 +21,19 @@ use smithay::{
wayland::compositor::{self, SurfaceData},
};
use std::{
ffi::c_void,
sync::{Arc, Weak},
time::Duration,
};
use stereokit::{
lifecycle::StereoKitDraw,
material::{Material, Transparency},
shader::Shader,
texture::{Texture as SKTexture, TextureAddress, TextureFormat, TextureSample, TextureType},
time::StereoKitTime,
Material, StereoKitDraw, Tex, TextureAddress, TextureFormat, TextureSample, TextureType,
Transparency,
};
pub static CORE_SURFACES: Registry<CoreSurface> = Registry::new();
#[derive(Debug, Clone, Copy)]
pub struct SurfaceGeometry {
pub origin: Vector2<u32>,
pub size: Vector2<u32>,
}
impl Default for SurfaceGeometry {
fn default() -> Self {
Self {
origin: [0; 2].into(),
size: [0; 2].into(),
}
}
}
pub struct CoreSurfaceData {
wl_tex: Option<SendWrapper<Gles2Texture>>,
wl_tex: Option<SendWrapper<GlesTexture>>,
pub size: Vector2<u32>,
}
impl Drop for CoreSurfaceData {
@@ -64,10 +47,10 @@ pub struct CoreSurface {
pub dh: DisplayHandle,
pub weak_surface: wayland_server::Weak<WlSurface>,
mapped_data: Mutex<Option<CoreSurfaceData>>,
sk_tex: OnceCell<SendWrapper<SKTexture>>,
sk_tex: OnceCell<SendWrapper<Tex>>,
sk_mat: OnceCell<Arc<SendWrapper<Material>>>,
material_offset: Mutex<Delta<u32>>,
// geometry: Mutex<Delta<Option<SurfaceGeometry>>>,
on_commit: Box<dyn Fn(u32) + Send + Sync>,
pub pending_material_applications: Mutex<Vec<(Arc<Model>, u32)>>,
}
@@ -76,11 +59,9 @@ impl CoreSurface {
display: &Arc<Mutex<Display<WaylandState>>>,
dh: DisplayHandle,
surface: &WlSurface,
on_commit: impl Fn(u32) + Send + Sync + 'static,
) {
compositor::with_states(surface, |data| {
// let mut geometry: Delta<Option<SurfaceGeometry>> =
// Delta::new(data.data_map.get::<SurfaceGeometry>().cloned());
// geometry.mark_changed();
data.data_map.insert_if_missing_threadsafe(|| {
CORE_SURFACES.add(CoreSurface {
display: Arc::downgrade(display),
@@ -90,46 +71,48 @@ impl CoreSurface {
sk_tex: OnceCell::new(),
sk_mat: OnceCell::new(),
material_offset: Mutex::new(Delta::new(0)),
// geometry: Mutex::new(geometry),
on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
pending_material_applications: Mutex::new(Vec::new()),
})
});
});
}
pub fn commit(&self, count: u32) {
(self.on_commit)(count);
}
pub fn from_wl_surface(surf: &WlSurface) -> Option<Arc<CoreSurface>> {
compositor::with_states(surf, |data| {
data.data_map.get::<Arc<CoreSurface>>().cloned()
})
}
pub fn process(&self, sk: &StereoKitDraw, renderer: &mut Gles2Renderer, log: &Logger) {
pub fn process(&self, sk: &impl StereoKitDraw, renderer: &mut GlesRenderer) {
let Some(wl_surface) = self.wl_surface() else { return };
let sk_tex = self.sk_tex.get_or_init(|| {
SendWrapper::new(
SKTexture::create(sk, TextureType::ImageNoMips, TextureFormat::RGBA32).unwrap(),
)
SendWrapper::new(sk.tex_create(TextureType::IMAGE_NO_MIPS, TextureFormat::RGBA32))
});
self.sk_mat.get_or_init(|| {
let shader = Shader::from_mem(sk, PANEL_SHADER_BYTES).unwrap();
let mat = Material::create(sk, &shader).unwrap();
mat.set_parameter(sk, "diffuse", &**sk_tex);
mat.set_transparency(sk, Transparency::Blend);
let shader = sk.shader_create_mem(&PANEL_SHADER_BYTES).unwrap();
let mat = sk.material_create(&shader);
sk.material_set_texture(&mat, "diffuse", sk_tex.as_ref());
sk.material_set_transparency(&mat, Transparency::Blend);
Arc::new(SendWrapper::new(mat))
});
// Let smithay handle buffer management (has to be done here as RendererSurfaceStates is not thread safe)
on_commit_buffer_handler(&wl_surface);
// Import all surface buffers into textures
if import_surface_tree(renderer, &wl_surface, log).is_err() {
if import_surface_tree(renderer, &wl_surface).is_err() {
return;
}
let mapped = compositor::with_states(&wl_surface, |data| {
data.data_map
.get::<RendererSurfaceStateUserData>()
.map(|surface_states| surface_states.borrow().wl_buffer().is_some())
.map(|surface_states| surface_states.borrow().buffer().is_some())
.unwrap_or(false)
});
@@ -147,26 +130,28 @@ impl CoreSurface {
.unwrap()
.borrow();
let smithay_tex = renderer_surface_state
.texture::<Gles2Renderer>(renderer.id())
.texture::<GlesRenderer>(renderer.id())
.unwrap()
.clone();
let sk_tex = self.sk_tex.get().unwrap();
let sk_mat = self.sk_mat.get().unwrap();
unsafe {
sk_tex.set_native(
smithay_tex.tex_id() as usize,
smithay::backend::renderer::gles2::ffi::RGBA8.into(),
TextureType::ImageNoMips,
smithay_tex.width(),
smithay_tex.height(),
sk.tex_set_surface(
sk_tex.as_ref(),
smithay_tex.tex_id() as usize as *mut c_void,
TextureType::IMAGE_NO_MIPS,
smithay::backend::renderer::gles::ffi::RGBA8.into(),
smithay_tex.width() as i32,
smithay_tex.height() as i32,
1,
false,
);
sk_tex.set_sample(TextureSample::Point);
sk_tex.set_address_mode(TextureAddress::Clamp);
sk.tex_set_sample(sk_tex.as_ref(), TextureSample::Point);
sk.tex_set_address(sk_tex.as_ref(), TextureAddress::Clamp);
}
if let Some(material_offset) = self.material_offset.lock().delta() {
sk_mat.set_queue_offset(sk, *material_offset as i32);
sk.material_set_queue_offset(sk_mat.as_ref().as_ref(), *material_offset as i32);
}
let surface_size = renderer_surface_state.surface_size().unwrap();
@@ -179,7 +164,7 @@ impl CoreSurface {
self.apply_surface_materials();
}
pub fn frame(&self, sk: &StereoKitDraw, output: Output) {
pub fn frame(&self, sk: &impl StereoKitDraw, output: Output) {
let Some(wl_surface) = self.wl_surface() else { return };
send_frames_surface_tree(
@@ -195,10 +180,6 @@ impl CoreSurface {
*self.material_offset.lock().value_mut() = material_offset;
}
// pub fn set_geometry(&self, geometry: SurfaceGeometry) {
// *self.geometry.lock().value_mut() = Some(geometry);
// }
pub fn apply_material(&self, model: Arc<Model>, material_idx: u32) {
self.pending_material_applications
.lock()

View File

@@ -1,29 +1,34 @@
use crate::nodes::Node;
use super::{
panel_item::{PanelItem, RecommendedState, ToplevelState},
panel_item::{PanelItem, RecommendedState, SurfaceID},
state::WaylandState,
surface::SurfaceGeometry,
surface::CoreSurface,
SERIAL_COUNTER,
};
use mint::Vector2;
use nanoid::nanoid;
use parking_lot::Mutex;
use serde::Serialize;
use smithay::{
reexports::{
wayland_protocols::xdg::shell::server::{
xdg_popup::{self, XdgPopup},
xdg_positioner::{self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner},
xdg_surface::{self, XdgSurface},
xdg_toplevel::{self, XdgToplevel, EVT_WM_CAPABILITIES_SINCE},
xdg_wm_base::{self, XdgWmBase},
},
wayland_server::{
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle,
GlobalDispatch, New, Resource, WEnum, Weak,
},
use serde::{ser::SerializeSeq, Serialize, Serializer};
use smithay::reexports::{
wayland_protocols::xdg::shell::server::{
xdg_popup::{self, XdgPopup},
xdg_positioner::{self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner},
xdg_surface::{self, XdgSurface},
xdg_toplevel::{self, XdgToplevel, EVT_WM_CAPABILITIES_SINCE},
xdg_wm_base::{self, XdgWmBase},
},
wayland_server::{
backend::{ClientId, ObjectId},
protocol::wl_surface::WlSurface,
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum,
Weak as WlWeak,
},
wayland::compositor,
};
use std::sync::Arc;
use std::{
fmt::Debug,
sync::{Arc, Weak},
};
use tracing::{debug, warn};
impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
@@ -38,11 +43,6 @@ impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
data_init.init(resource, ());
}
}
#[derive(Debug)]
pub struct WaylandSurface {
wl_surface: Weak<WlSurface>,
geometry: Arc<Mutex<Option<SurfaceGeometry>>>,
}
impl Dispatch<XdgWmBase, (), WaylandState> for WaylandState {
fn request(
@@ -56,18 +56,11 @@ impl Dispatch<XdgWmBase, (), WaylandState> for WaylandState {
) {
match request {
xdg_wm_base::Request::CreatePositioner { id } => {
let positioner =
data_init.init(id, Arc::new(Mutex::new(PositionerData::default())));
let positioner = data_init.init(id, Mutex::new(PositionerData::default()));
debug!(?positioner, "Create XDG positioner");
}
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
let xdg_surface = data_init.init(
id,
WaylandSurface {
wl_surface: surface.downgrade(),
geometry: Arc::new(Mutex::new(None)),
},
);
let xdg_surface = data_init.init(id, Mutex::new(XdgSurfaceData::new(&surface)));
debug!(?xdg_surface, "Create XDG surface");
}
xdg_wm_base::Request::Pong { serial } => {
@@ -81,7 +74,7 @@ impl Dispatch<XdgWmBase, (), WaylandState> for WaylandState {
}
}
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, Clone, Copy)]
pub struct PositionerData {
size: Vector2<u32>,
anchor_rect_pos: Vector2<i32>,
@@ -107,13 +100,13 @@ impl Default for PositionerData {
}
}
impl Dispatch<XdgPositioner, Arc<Mutex<PositionerData>>, WaylandState> for WaylandState {
impl Dispatch<XdgPositioner, Mutex<PositionerData>, WaylandState> for WaylandState {
fn request(
_state: &mut WaylandState,
_client: &Client,
positioner: &XdgPositioner,
request: xdg_positioner::Request,
data: &Arc<Mutex<PositionerData>>,
data: &Mutex<PositionerData>,
_dhandle: &DisplayHandle,
_data_init: &mut DataInit<'_, WaylandState>,
) {
@@ -195,39 +188,68 @@ impl Dispatch<XdgPositioner, Arc<Mutex<PositionerData>>, WaylandState> for Wayla
}
}
#[derive(Debug, Clone)]
pub struct XdgSurfaceData {
pub wl_surface: Weak<WlSurface>,
pub xdg_surface: Weak<XdgSurface>,
pub geometry: Arc<Mutex<Option<SurfaceGeometry>>>,
#[derive(Debug, Serialize, Clone, Copy)]
pub struct Geometry {
pub origin: Vector2<i32>,
pub size: Vector2<u32>,
}
impl Dispatch<XdgSurface, WaylandSurface, WaylandState> for WaylandState {
impl Default for Geometry {
fn default() -> Self {
Self {
origin: Vector2::from([0; 2]),
size: Vector2::from([0; 2]),
}
}
}
pub struct XdgSurfaceData {
wl_surface: WlWeak<WlSurface>,
surface_id: SurfaceID,
panel_item: Weak<PanelItem>,
geometry: Option<Geometry>,
}
impl XdgSurfaceData {
pub fn new(wl_surface: &WlSurface) -> Self {
XdgSurfaceData {
wl_surface: wl_surface.downgrade(),
surface_id: SurfaceID::Toplevel,
panel_item: Weak::new(),
geometry: None,
}
}
pub fn get(xdg_surface: &XdgSurface) -> &Mutex<Self> {
xdg_surface.data::<Mutex<Self>>().unwrap()
}
pub fn wl_surface(&self) -> WlSurface {
self.wl_surface.upgrade().unwrap()
}
pub fn panel_item(&self) -> Option<Arc<PanelItem>> {
self.panel_item.upgrade()
}
}
// impl Clone for XdgSurfaceData {
// fn clone(&self) -> Self {
// Self {
// wl_surface: self.wl_surface.clone(),
// geometry: self.geometry.clone(),
// surface_type: Mutex::new(self.surface_type.lock().clone()),
// }
// }
// }
impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState {
fn request(
state: &mut WaylandState,
client: &Client,
xdg_surface: &XdgSurface,
request: xdg_surface::Request,
data: &WaylandSurface,
xdg_surface_data: &Mutex<XdgSurfaceData>,
_dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, WaylandState>,
) {
match request {
xdg_surface::Request::GetToplevel { id } => {
let toplevel_state = Arc::new(Mutex::new(ToplevelState {
queued_state: Some(Box::new(ToplevelState::default())),
..Default::default()
}));
let toplevel = data_init.init(
id,
XdgToplevelData {
state: toplevel_state,
xdg_surface_data: XdgSurfaceData {
wl_surface: data.wl_surface.clone(),
xdg_surface: xdg_surface.downgrade(),
geometry: data.geometry.clone(),
},
},
);
let toplevel_state = Mutex::new(ToplevelData::new(xdg_surface));
let toplevel = data_init.init(id, toplevel_state);
debug!(?toplevel, ?xdg_surface, "Create XDG toplevel");
if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE {
@@ -236,25 +258,99 @@ impl Dispatch<XdgSurface, WaylandSurface, WaylandState> for WaylandState {
toplevel.configure(0, 0, vec![]);
xdg_surface.configure(SERIAL_COUNTER.inc());
let (node, item) = PanelItem::create(
toplevel,
data.wl_surface.upgrade().unwrap(),
client.get_credentials(&state.display_handle).ok(),
state.seats.get(&client.id()).unwrap().clone(),
let client_credentials = client.get_credentials(&state.display_handle).ok();
let seat_data = state.seats.get(&client.id()).unwrap().clone();
let toplevel_weak = toplevel.downgrade();
CoreSurface::add_to(
&state.display,
state.display_handle.clone(),
&xdg_surface_data.lock().wl_surface(),
move |c| match c {
0 => {
let toplevel = toplevel_weak.upgrade().unwrap();
let toplevel_data = ToplevelData::get(&toplevel);
let xdg_surface = toplevel_data.lock().xdg_surface();
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface);
let wl_surface = xdg_surface_data.lock().wl_surface();
xdg_surface_data.lock().surface_id = SurfaceID::Toplevel;
let (node, panel_item) = PanelItem::create(
toplevel.clone(),
wl_surface.clone(),
client_credentials,
seat_data.clone(),
);
toplevel_data.lock().panel_item_node.replace(node);
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
}
_ => {
let toplevel = toplevel_weak.upgrade().unwrap();
let toplevel_data = ToplevelData::get(&toplevel);
let panel_item = toplevel_data.lock().panel_item().unwrap();
panel_item.commit_toplevel();
}
},
);
compositor::with_states(&data.wl_surface.upgrade().unwrap(), |surface_data| {
surface_data.data_map.insert_if_missing_threadsafe(|| node);
surface_data.data_map.insert_if_missing_threadsafe(|| item);
});
}
xdg_surface::Request::GetPopup {
id,
parent: _,
positioner: _,
parent,
positioner,
} => {
let popup = data_init.init(id, ());
debug!(?popup, ?xdg_surface, "Create XDG popup");
popup.popup_done(); // temporary hack to avoid apps locking up before popups are implemented
let parent_clone = parent.clone().unwrap();
let parent_data = parent_clone.data::<Mutex<XdgSurfaceData>>().unwrap().lock();
// let positioner_data = positioner
// .data::<Mutex<PositionerData>>()
// .unwrap()
// .lock()
// .clone();
// let parent = match &*parent_data {
// XdgSurfaceType::Toplevel(_) => SurfaceID::Toplevel,
// XdgSurfaceType::Popup(p) => {
// SurfaceID::Popup(p.upgrade().unwrap().uid.clone())
// }
// XdgSurfaceType::Unknown => return,
// };
let uid = nanoid!();
let popup_data = Mutex::new(PopupData::new(
uid.clone(),
xdg_surface,
parent_data.surface_id.clone(),
positioner,
));
let xdg_popup = data_init.init(id, popup_data);
xdg_surface_data.lock().surface_id = SurfaceID::Popup(uid);
let panel_item = parent_data.panel_item().unwrap();
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
panel_item.seat_data.new_surface(
&xdg_surface_data.lock().wl_surface(),
Arc::downgrade(&panel_item),
);
debug!(?xdg_popup, ?xdg_surface, "Create XDG popup");
let xdg_surface = xdg_surface.downgrade();
let xdg_popup = xdg_popup.downgrade();
CoreSurface::add_to(
&state.display,
state.display_handle.clone(),
&xdg_surface_data.lock().wl_surface.upgrade().unwrap(),
move |commit_count| match commit_count {
0 => xdg_surface
.upgrade()
.unwrap()
.configure(SERIAL_COUNTER.inc()),
c => {
let xdg_popup = xdg_popup.upgrade().unwrap();
let popup_data = PopupData::get(&xdg_popup);
let popup_data = popup_data.lock();
// panel_item.commit_popup(popup_data);
if c == 1 {
panel_item.new_popup(&xdg_popup, &*popup_data);
}
}
},
);
}
xdg_surface::Request::SetWindowGeometry {
x,
@@ -266,18 +362,11 @@ impl Dispatch<XdgSurface, WaylandSurface, WaylandState> for WaylandState {
?xdg_surface,
x, y, width, height, "Set XDG surface geometry"
);
let geometry = SurfaceGeometry {
origin: [x as u32, y as u32].into(),
let geometry = Geometry {
origin: [x, y].into(),
size: [width as u32, height as u32].into(),
};
*data.geometry.lock() = Some(geometry);
let Ok(wl_surface) = data.wl_surface.upgrade() else { return; };
compositor::with_states(&wl_surface, |data| {
// if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
// core_surface.set_geometry(geometry);
// }
data.data_map.insert_if_missing_threadsafe(|| geometry);
});
xdg_surface_data.lock().geometry.replace(geometry);
}
xdg_surface::Request::AckConfigure { serial } => {
debug!(?xdg_surface, serial, "Acknowledge XDG surface configure");
@@ -290,47 +379,104 @@ impl Dispatch<XdgSurface, WaylandSurface, WaylandState> for WaylandState {
}
}
#[derive(Debug)]
pub struct XdgToplevelData {
pub state: Arc<Mutex<ToplevelState>>,
pub xdg_surface_data: XdgSurfaceData,
fn serde_error<S: Serializer>(msg: &str) -> Result<S::Ok, S::Error> {
Err(serde::ser::Error::custom(msg))
}
impl XdgToplevelData {
#[derive(Debug, Clone)]
pub struct ToplevelData {
panel_item_node: Option<Arc<Node>>,
xdg_surface: WlWeak<XdgSurface>,
parent: Option<WlWeak<XdgToplevel>>,
title: Option<String>,
app_id: Option<String>,
max_size: Option<Vector2<u32>>,
min_size: Option<Vector2<u32>>,
states: Vec<u32>,
}
impl ToplevelData {
fn new(xdg_surface: &XdgSurface) -> Self {
ToplevelData {
panel_item_node: None,
xdg_surface: xdg_surface.downgrade(),
parent: None,
title: None,
app_id: None,
max_size: None,
min_size: None,
states: Vec::new(),
}
}
pub fn get(toplevel: &XdgToplevel) -> &Mutex<ToplevelData> {
toplevel.data::<Mutex<ToplevelData>>().unwrap()
}
pub fn xdg_surface(&self) -> XdgSurface {
self.xdg_surface.upgrade().unwrap()
}
fn panel_item(&self) -> Option<Arc<PanelItem>> {
let wl_surface = self.xdg_surface_data.wl_surface.upgrade().ok()?;
compositor::with_states(&wl_surface, |data| {
data.data_map.get::<Arc<PanelItem>>().cloned()
})
let xdg_surface = self.xdg_surface();
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface).lock();
xdg_surface_data.panel_item()
}
}
impl Dispatch<XdgToplevel, XdgToplevelData, WaylandState> for WaylandState {
impl Serialize for ToplevelData {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let xdg_surface = self.xdg_surface();
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface).lock();
let geometry = xdg_surface_data.geometry.clone();
let wl_surface = xdg_surface_data.wl_surface();
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return serde_error::<S>("Core surface not found")};
let Some(size) = core_surface.size() else {return serializer.serialize_none()};
let geometry = geometry.unwrap_or_else(|| Geometry {
origin: [0; 2].into(),
size,
});
let mut seq = serializer.serialize_seq(None)?;
// Parent UID
seq.serialize_element(&self.parent.as_ref().and_then(|p| {
Some(
ToplevelData::get(&p.upgrade().ok()?)
.lock()
.panel_item()?
.uid
.clone(),
)
}))?;
seq.serialize_element(&self.title)?;
seq.serialize_element(&self.app_id)?;
seq.serialize_element(&size)?;
seq.serialize_element(&self.min_size)?;
seq.serialize_element(&self.max_size)?;
seq.serialize_element(&geometry)?;
seq.serialize_element(&self.states)?;
seq.end()
}
}
impl Dispatch<XdgToplevel, Mutex<ToplevelData>, WaylandState> for WaylandState {
fn request(
_state: &mut WaylandState,
_client: &Client,
xdg_toplevel: &XdgToplevel,
request: xdg_toplevel::Request,
data: &XdgToplevelData,
data: &Mutex<ToplevelData>,
_dhandle: &DisplayHandle,
_data_init: &mut DataInit<'_, WaylandState>,
) {
match request {
xdg_toplevel::Request::SetParent { parent } => {
debug!(?xdg_toplevel, ?parent, "Set XDG Toplevel parent");
let mut state = data.state.lock();
let queued_state = state.queued_state.as_mut().unwrap();
queued_state.parent = parent.map(|toplevel| toplevel.downgrade());
data.lock().parent = parent.map(|toplevel| toplevel.downgrade());
}
xdg_toplevel::Request::SetTitle { title } => {
debug!(?xdg_toplevel, ?title, "Set XDG Toplevel title");
let mut state = data.state.lock();
let queued_state = state.queued_state.as_mut().unwrap();
queued_state.title = (!title.is_empty()).then_some(title);
data.lock().title = (!title.is_empty()).then_some(title);
}
xdg_toplevel::Request::SetAppId { app_id } => {
debug!(?xdg_toplevel, ?app_id, "Set XDG Toplevel app ID");
let mut state = data.state.lock();
let queued_state = state.queued_state.as_mut().unwrap();
queued_state.app_id = (!app_id.is_empty()).then_some(app_id);
data.lock().app_id = (!app_id.is_empty()).then_some(app_id);
}
xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
debug!(
@@ -344,7 +490,7 @@ impl Dispatch<XdgToplevel, XdgToplevelData, WaylandState> for WaylandState {
}
xdg_toplevel::Request::Move { seat, serial } => {
debug!(?xdg_toplevel, ?seat, serial, "XDG Toplevel move request");
let Some(panel_item) = data.panel_item() else { return };
let Some(panel_item) = data.lock().panel_item() else { return };
panel_item.recommend_toplevel_state(RecommendedState::Move);
}
xdg_toplevel::Request::Resize {
@@ -360,46 +506,42 @@ impl Dispatch<XdgToplevel, XdgToplevelData, WaylandState> for WaylandState {
?edges,
"XDG Toplevel resize request"
);
let Some(panel_item) = data.panel_item() else { return };
let Some(panel_item) = data.lock().panel_item() else { return };
panel_item.recommend_toplevel_state(RecommendedState::Resize(edges as u32));
}
xdg_toplevel::Request::SetMaxSize { width, height } => {
debug!(?xdg_toplevel, width, height, "Set XDG Toplevel max size");
let mut state = data.state.lock();
let queued_state = state.queued_state.as_mut().unwrap();
queued_state.max_size = (width > 1 || height > 1)
data.lock().max_size = (width > 1 || height > 1)
.then_some(Vector2::from([width as u32, height as u32]));
}
xdg_toplevel::Request::SetMinSize { width, height } => {
debug!(?xdg_toplevel, width, height, "Set XDG Toplevel min size");
let mut state = data.state.lock();
let queued_state = state.queued_state.as_mut().unwrap();
queued_state.min_size = (width > 1 || height > 1)
data.lock().min_size = (width > 1 || height > 1)
.then_some(Vector2::from([width as u32, height as u32]));
}
xdg_toplevel::Request::SetMaximized => {
let Some(panel_item) = data.panel_item() else { return };
let Some(panel_item) = data.lock().panel_item() else { return };
panel_item.recommend_toplevel_state(RecommendedState::Maximize(true));
}
xdg_toplevel::Request::UnsetMaximized => {
let Some(panel_item) = data.panel_item() else { return };
let Some(panel_item) = data.lock().panel_item() else { return };
panel_item.recommend_toplevel_state(RecommendedState::Maximize(false));
}
xdg_toplevel::Request::SetFullscreen { output: _ } => {
let Some(panel_item) = data.panel_item() else { return };
let Some(panel_item) = data.lock().panel_item() else { return };
panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true));
}
xdg_toplevel::Request::UnsetFullscreen => {
let Some(panel_item) = data.panel_item() else { return };
let Some(panel_item) = data.lock().panel_item() else { return };
panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true));
}
xdg_toplevel::Request::SetMinimized => {
let Some(panel_item) = data.panel_item() else { return };
let Some(panel_item) = data.lock().panel_item() else { return };
panel_item.recommend_toplevel_state(RecommendedState::Minimize);
}
xdg_toplevel::Request::Destroy => {
debug!(?xdg_toplevel, "Destroy XDG Toplevel");
let Some(panel_item) = data.panel_item() else { return };
let Some(panel_item) = data.lock().panel_item() else { return };
panel_item.on_drop();
}
_ => unreachable!(),
@@ -407,29 +549,121 @@ impl Dispatch<XdgToplevel, XdgToplevelData, WaylandState> for WaylandState {
}
}
impl Dispatch<XdgPopup, (), WaylandState> for WaylandState {
#[derive(Clone)]
pub struct PopupData {
pub uid: String,
grabbed: bool,
parent_id: SurfaceID,
positioner: XdgPositioner,
xdg_surface: WlWeak<XdgSurface>,
}
impl PopupData {
fn new(
uid: impl ToString,
xdg_surface: &XdgSurface,
parent_id: SurfaceID,
positioner: XdgPositioner,
) -> Self {
PopupData {
uid: uid.to_string(),
grabbed: false,
parent_id,
positioner,
xdg_surface: xdg_surface.downgrade(),
}
}
pub fn get(popup: &XdgPopup) -> &Mutex<Self> {
popup.data::<Mutex<Self>>().unwrap()
}
pub fn xdg_surface(&self) -> XdgSurface {
self.xdg_surface.upgrade().unwrap()
}
fn panel_item(&self) -> Option<Arc<PanelItem>> {
XdgSurfaceData::get(&self.xdg_surface()).lock().panel_item()
}
// fn get_parent(&self) -> Option<XdgSurface> {
// self.parent.as_ref()?.upgrade().ok()
// }
pub fn wl_surface(&self) -> WlSurface {
XdgSurfaceData::get(&self.xdg_surface()).lock().wl_surface()
}
pub fn positioner_data(&self) -> Option<PositionerData> {
Some(
self.positioner
.data::<Mutex<PositionerData>>()?
.lock()
.clone(),
)
}
}
impl Serialize for PopupData {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let Some(positioner_data) = self.positioner_data() else {return serde_error::<S>("Positioner not found")};
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(&self.uid)?;
seq.serialize_element(&self.parent_id)?;
seq.serialize_element(&positioner_data)?;
seq.end()
}
}
impl Debug for PopupData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("XdgPopupData")
.field("uid", &self.uid)
.field("positioner", &self.positioner)
.field("xdg_surface", &self.xdg_surface)
.finish()
}
}
impl Dispatch<XdgPopup, Mutex<PopupData>, WaylandState> for WaylandState {
fn request(
_state: &mut WaylandState,
_client: &Client,
xdg_popup: &XdgPopup,
request: xdg_popup::Request,
_data: &(),
data: &Mutex<PopupData>,
_dhandle: &DisplayHandle,
_data_init: &mut DataInit<'_, WaylandState>,
) {
match request {
xdg_popup::Request::Grab { seat, serial } => {
let mut data = data.lock();
data.grabbed = true;
debug!(?xdg_popup, ?seat, serial, "XDG popup grab");
xdg_popup.popup_done(); // temporary hack to avoid apps locking up before popups are implemented
let Some(panel_item) = data.panel_item() else {return};
panel_item.grab_keyboard(Some(SurfaceID::Popup(data.uid.clone())));
}
xdg_popup::Request::Reposition { positioner, token } => {
let mut data = data.lock();
debug!(?xdg_popup, ?positioner, token, "XDG popup reposition");
xdg_popup.popup_done(); // temporary hack to avoid apps locking up before popups are implemented
data.positioner = positioner;
let Some(panel_item) = data.panel_item() else {return};
panel_item.reposition_popup(&*data);
// xdg_popup.popup_done(); // temporary hack to avoid apps locking up before popups are implemented
}
xdg_popup::Request::Destroy => {
let data = data.lock();
debug!(?xdg_popup, "Destroy XDG popup");
if data.grabbed {
let Some(panel_item) = data.panel_item() else {return};
panel_item.grab_keyboard(None);
}
}
_ => unreachable!(),
}
}
fn destroyed(
_state: &mut WaylandState,
_client: ClientId,
_resource: ObjectId,
data: &Mutex<PopupData>,
) {
let data = data.lock();
let Some(panel_item) = data.panel_item() else {return};
panel_item.drop_popup(&data.uid);
}
}