Merge branch 'StardustXR:main' into main
This commit is contained in:
830
Cargo.lock
generated
830
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
21
Cargo.toml
@@ -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 = "*"
|
||||
|
||||
@@ -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,
|
||||
|
||||
108
src/main.rs
108
src/main.rs
@@ -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!(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 }),
|
||||
);
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,3 +272,8 @@ impl Debug for Node {
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
impl Drop for Node {
|
||||
fn drop(&mut self) {
|
||||
// Debug breakpoint
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)?)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 => (),
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user