rewrite built-in wayland compositor egl → vulkan
This commit is contained in:
@@ -26,7 +26,7 @@ use crate::{
|
||||
manifest::{install_manifest, uninstall_manifest},
|
||||
overlay::OpenVrOverlayData,
|
||||
},
|
||||
task::{InputTask, OpenVrTask, OverlayTask, TaskType},
|
||||
task::{OpenVrTask, OverlayTask, TaskType},
|
||||
},
|
||||
config::save_state,
|
||||
graphics::{GpuFutures, init_openvr_graphics},
|
||||
@@ -42,9 +42,6 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
use crate::{backend::wayvr::WayVRAction, overlays::wayvr::wayvr_action};
|
||||
|
||||
pub mod helpers;
|
||||
pub mod input;
|
||||
pub mod lines;
|
||||
@@ -157,51 +154,50 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
}
|
||||
FRAME_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
// extremely cursed
|
||||
const VREVENT_QUIT: u32 = EVREventType::VREvent_Quit as u32;
|
||||
const VREVENT_TRACKED_ACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceActivated as u32;
|
||||
const VREVENT_TRACKED_DEACTIVATED: u32 =
|
||||
EVREventType::VREvent_TrackedDeviceDeactivated as u32;
|
||||
const VREVENT_TRACKED_UPDATED: u32 = EVREventType::VREvent_TrackedDeviceUpdated as u32;
|
||||
const VREVENT_SEATED_ZERO: u32 = EVREventType::VREvent_SeatedZeroPoseReset as u32;
|
||||
const VREVENT_STANDING_ZERO: u32 = EVREventType::VREvent_StandingZeroPoseReset as u32;
|
||||
const VREVENT_CHAPERONE_CHANGED: u32 =
|
||||
EVREventType::VREvent_ChaperoneUniverseHasChanged as u32;
|
||||
const VREVENT_SCENE_APP_CHANGED: u32 = EVREventType::VREvent_SceneApplicationChanged as u32;
|
||||
const VREVENT_IPD_CHANGED: u32 = EVREventType::VREvent_IpdChanged as u32;
|
||||
{
|
||||
// extremely cursed
|
||||
const EV_QUIT: u32 = EVREventType::VREvent_Quit as u32;
|
||||
const EV_DEV_ACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceActivated as u32;
|
||||
const EV_DEV_DEACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceDeactivated as u32;
|
||||
const EV_DEV_UPDATED: u32 = EVREventType::VREvent_TrackedDeviceUpdated as u32;
|
||||
const EV_SEAT_ZERO: u32 = EVREventType::VREvent_SeatedZeroPoseReset as u32;
|
||||
const EV_STAND_ZERO: u32 = EVREventType::VREvent_StandingZeroPoseReset as u32;
|
||||
const EV_CHAP_CHANGED: u32 = EVREventType::VREvent_ChaperoneUniverseHasChanged as u32;
|
||||
const EV_SCENE_CHANGED: u32 = EVREventType::VREvent_SceneApplicationChanged as u32;
|
||||
const EV_IPD_CHANGED: u32 = EVREventType::VREvent_IpdChanged as u32;
|
||||
|
||||
while let Some(event) = system_mgr.poll_next_event() {
|
||||
match event.event_type {
|
||||
VREVENT_QUIT => {
|
||||
log::warn!("Received quit event, shutting down.");
|
||||
break 'main_loop;
|
||||
}
|
||||
VREVENT_TRACKED_ACTIVATED
|
||||
| VREVENT_TRACKED_DEACTIVATED
|
||||
| VREVENT_TRACKED_UPDATED => {
|
||||
next_device_update = Instant::now();
|
||||
}
|
||||
VREVENT_SEATED_ZERO
|
||||
| VREVENT_STANDING_ZERO
|
||||
| VREVENT_CHAPERONE_CHANGED
|
||||
| VREVENT_SCENE_APP_CHANGED => {
|
||||
playspace.playspace_changed(&mut compositor_mgr, &mut chaperone_mgr);
|
||||
}
|
||||
VREVENT_IPD_CHANGED => {
|
||||
if let Ok(ipd) = system_mgr.get_tracked_device_property::<f32>(
|
||||
TrackedDeviceIndex::HMD,
|
||||
ETrackedDeviceProperty::Prop_UserIpdMeters_Float,
|
||||
) {
|
||||
let ipd = (ipd * 1000.0).round();
|
||||
if (ipd - app.input_state.ipd).abs() > 0.05 {
|
||||
log::info!("IPD: {:.1} mm -> {:.1} mm", app.input_state.ipd, ipd);
|
||||
Toast::new(ToastTopic::IpdChange, "IPD".into(), format!("{ipd:.1} mm"))
|
||||
.submit(&mut app);
|
||||
}
|
||||
app.input_state.ipd = ipd;
|
||||
while let Some(event) = system_mgr.poll_next_event() {
|
||||
match event.event_type {
|
||||
EV_QUIT => {
|
||||
log::warn!("Received quit event, shutting down.");
|
||||
break 'main_loop;
|
||||
}
|
||||
EV_DEV_ACTIVATED | EV_DEV_DEACTIVATED | EV_DEV_UPDATED => {
|
||||
next_device_update = Instant::now();
|
||||
}
|
||||
EV_SEAT_ZERO | EV_STAND_ZERO | EV_CHAP_CHANGED | EV_SCENE_CHANGED => {
|
||||
playspace.playspace_changed(&mut compositor_mgr, &mut chaperone_mgr);
|
||||
}
|
||||
EV_IPD_CHANGED => {
|
||||
if let Ok(ipd) = system_mgr.get_tracked_device_property::<f32>(
|
||||
TrackedDeviceIndex::HMD,
|
||||
ETrackedDeviceProperty::Prop_UserIpdMeters_Float,
|
||||
) {
|
||||
let ipd = (ipd * 1000.0).round();
|
||||
if (ipd - app.input_state.ipd).abs() > 0.05 {
|
||||
log::info!("IPD: {:.1} mm -> {:.1} mm", app.input_state.ipd, ipd);
|
||||
Toast::new(
|
||||
ToastTopic::IpdChange,
|
||||
"IPD".into(),
|
||||
format!("{ipd:.1} mm"),
|
||||
)
|
||||
.submit(&mut app);
|
||||
}
|
||||
app.input_state.ipd = ipd;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,9 +231,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "wayvr")]
|
||||
TaskType::WayVR(action) => {
|
||||
wayvr_action(&mut app, &mut overlays, &action);
|
||||
}
|
||||
TaskType::WayVR(_action) => { /* TODO */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,9 +261,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
.pointers
|
||||
.iter()
|
||||
.any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard)
|
||||
{
|
||||
wayvr_action(&mut app, &mut overlays, &WayVRAction::ToggleDashboard);
|
||||
}
|
||||
{ /* TODO */ }
|
||||
|
||||
overlays
|
||||
.values_mut()
|
||||
@@ -337,11 +329,6 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
.values_mut()
|
||||
.for_each(|o| o.after_render(universe.clone(), &mut overlay_mgr, &app.gfx));
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
if let Some(wayland_server) = app.wayland_server.as_ref() {
|
||||
wayland_server.borrow_mut().data.tick_finish()?;
|
||||
}
|
||||
|
||||
// chaperone
|
||||
} // main_loop
|
||||
|
||||
|
||||
@@ -35,9 +35,6 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
use crate::{backend::wayvr::WayVRAction, overlays::wayvr::wayvr_action};
|
||||
|
||||
mod blocker;
|
||||
mod helpers;
|
||||
mod input;
|
||||
@@ -149,8 +146,8 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
};
|
||||
|
||||
let pointer_lines = [
|
||||
lines.allocate(&xr_state, &mut app)?,
|
||||
lines.allocate(&xr_state, &mut app)?,
|
||||
lines.allocate(&xr_state, &app)?,
|
||||
lines.allocate(&xr_state, &app)?,
|
||||
];
|
||||
|
||||
let watch_id = overlays.lookup(WATCH_NAME).unwrap(); // want panic
|
||||
@@ -303,9 +300,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
.pointers
|
||||
.iter()
|
||||
.any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard)
|
||||
{
|
||||
wayvr_action(&mut app, &mut overlays, &WayVRAction::ToggleDashboard);
|
||||
}
|
||||
{ /* TODO */ }
|
||||
|
||||
watch_fade(&mut app, overlays.mut_by_id(watch_id).unwrap()); // want panic
|
||||
if let Some(ref mut space_mover) = playspace {
|
||||
@@ -458,11 +453,6 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
}
|
||||
// End layer composition
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
if let Some(wayland_server) = app.wayland_server.as_ref() {
|
||||
wayland_server.borrow_mut().data.tick_finish()?;
|
||||
}
|
||||
|
||||
// Begin layer submit
|
||||
layers.sort_by(|a, b| b.0.total_cmp(&a.0));
|
||||
|
||||
@@ -502,10 +492,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
}
|
||||
#[cfg(feature = "openvr")]
|
||||
TaskType::OpenVR(_) => {}
|
||||
#[cfg(feature = "wayvr")]
|
||||
TaskType::WayVR(action) => {
|
||||
wayvr_action(&mut app, &mut overlays, &action);
|
||||
}
|
||||
TaskType::WayVR(_action) => { /* TODO */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,11 @@ use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask};
|
||||
use super::{
|
||||
ProcessWayVREnv,
|
||||
comp::{self, ClientState},
|
||||
display, process,
|
||||
process,
|
||||
};
|
||||
|
||||
pub struct WayVRClient {
|
||||
pub client: wayland_server::Client,
|
||||
pub display_handle: display::DisplayHandle,
|
||||
pub pid: u32,
|
||||
}
|
||||
|
||||
@@ -103,10 +102,13 @@ impl WayVRCompositor {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn cleanup_handles(&mut self) {
|
||||
self.state.cleanup();
|
||||
}
|
||||
|
||||
fn accept_connection(
|
||||
&mut self,
|
||||
stream: UnixStream,
|
||||
displays: &mut display::DisplayVec,
|
||||
processes: &mut process::ProcessVec,
|
||||
) -> anyhow::Result<()> {
|
||||
let client = self
|
||||
@@ -126,16 +128,12 @@ impl WayVRCompositor {
|
||||
{
|
||||
// Find process with matching auth key
|
||||
if process.auth_key.as_str() == auth_key {
|
||||
// Check if display handle is valid
|
||||
if displays.get(&process.display_handle).is_some() {
|
||||
// Add client
|
||||
self.add_client(WayVRClient {
|
||||
client,
|
||||
display_handle: process.display_handle,
|
||||
pid: creds.pid as u32,
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
// Add client
|
||||
self.add_client(WayVRClient {
|
||||
client,
|
||||
pid: creds.pid as u32,
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,13 +156,9 @@ impl WayVRCompositor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn accept_connections(
|
||||
&mut self,
|
||||
displays: &mut display::DisplayVec,
|
||||
processes: &mut process::ProcessVec,
|
||||
) -> anyhow::Result<()> {
|
||||
fn accept_connections(&mut self, processes: &mut process::ProcessVec) -> anyhow::Result<()> {
|
||||
if let Some(stream) = self.listener.accept()?
|
||||
&& let Err(e) = self.accept_connection(stream, displays, processes)
|
||||
&& let Err(e) = self.accept_connection(stream, processes)
|
||||
{
|
||||
log::error!("Failed to accept connection: {e}");
|
||||
}
|
||||
@@ -172,12 +166,8 @@ impl WayVRCompositor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tick_wayland(
|
||||
&mut self,
|
||||
displays: &mut display::DisplayVec,
|
||||
processes: &mut process::ProcessVec,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Err(e) = self.accept_connections(displays, processes) {
|
||||
pub fn tick_wayland(&mut self, processes: &mut process::ProcessVec) -> anyhow::Result<()> {
|
||||
if let Err(e) = self.accept_connections(processes) {
|
||||
log::error!("accept_connections failed: {e}");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::renderer::ImportDma;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::backend::renderer::utils::on_commit_buffer_handler;
|
||||
use smithay::backend::renderer::{BufferType, buffer_type};
|
||||
use smithay::input::{Seat, SeatHandler, SeatState};
|
||||
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||
use smithay::reexports::wayland_server;
|
||||
use smithay::reexports::wayland_server::Resource;
|
||||
use smithay::reexports::wayland_server::protocol::{wl_buffer, wl_seat, wl_surface};
|
||||
use smithay::reexports::wayland_server::protocol::{wl_buffer, wl_output, wl_seat, wl_surface};
|
||||
use smithay::wayland::buffer::BufferHandler;
|
||||
use smithay::wayland::dmabuf::{
|
||||
DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier,
|
||||
DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf,
|
||||
};
|
||||
use smithay::wayland::output::OutputHandler;
|
||||
use smithay::wayland::shm::{ShmHandler, ShmState};
|
||||
use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents};
|
||||
use smithay::wayland::single_pixel_buffer::get_single_pixel_buffer;
|
||||
use smithay::{
|
||||
delegate_compositor, delegate_data_device, delegate_dmabuf, delegate_output, delegate_seat,
|
||||
delegate_shm, delegate_xdg_shell,
|
||||
@@ -23,7 +22,7 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
use smithay::utils::Serial;
|
||||
use smithay::wayland::compositor::{
|
||||
self, SurfaceAttributes, TraversalAction, with_surface_tree_downward,
|
||||
self, BufferAssignment, SurfaceAttributes, TraversalAction, with_surface_tree_downward,
|
||||
};
|
||||
|
||||
use smithay::wayland::selection::SelectionHandler;
|
||||
@@ -37,12 +36,14 @@ use wayland_server::Client;
|
||||
use wayland_server::backend::{ClientData, ClientId, DisconnectReason};
|
||||
use wayland_server::protocol::wl_surface::WlSurface;
|
||||
|
||||
use crate::backend::wayvr::SurfaceBufWithImage;
|
||||
use crate::backend::wayvr::image_importer::ImageImporter;
|
||||
use crate::ipc::event_queue::SyncEventQueue;
|
||||
|
||||
use super::WayVRTask;
|
||||
|
||||
pub struct Application {
|
||||
pub gles_renderer: GlesRenderer,
|
||||
pub image_importer: ImageImporter,
|
||||
pub dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
|
||||
pub compositor: compositor::CompositorState,
|
||||
pub xdg_shell: XdgShellState,
|
||||
@@ -57,6 +58,10 @@ impl Application {
|
||||
pub fn check_redraw(&mut self, surface: &WlSurface) -> bool {
|
||||
self.redraw_requests.remove(&surface.id())
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) {
|
||||
self.image_importer.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
impl compositor::CompositorHandler for Application {
|
||||
@@ -72,7 +77,74 @@ impl compositor::CompositorHandler for Application {
|
||||
}
|
||||
|
||||
fn commit(&mut self, surface: &WlSurface) {
|
||||
on_commit_buffer_handler::<Self>(surface);
|
||||
smithay::wayland::compositor::with_states(surface, |states| {
|
||||
let mut guard = states.cached_state.get::<SurfaceAttributes>();
|
||||
let attrs = guard.current();
|
||||
|
||||
match attrs.buffer.take() {
|
||||
Some(BufferAssignment::NewBuffer(buffer)) => {
|
||||
let current = SurfaceBufWithImage::get_from_surface(states);
|
||||
|
||||
if current.is_none_or(|c| c.buffer != buffer) {
|
||||
match buffer_type(&buffer) {
|
||||
Some(BufferType::Dma) => {
|
||||
let dmabuf = get_dmabuf(&buffer).unwrap(); // always Ok due to buffer_type
|
||||
if let Ok(image) =
|
||||
self.image_importer.get_or_import_dmabuf(dmabuf.clone())
|
||||
{
|
||||
let sbwi = SurfaceBufWithImage {
|
||||
image,
|
||||
buffer,
|
||||
transform: wl_transform_to_frame_transform(
|
||||
attrs.buffer_transform,
|
||||
),
|
||||
scale: attrs.buffer_scale,
|
||||
};
|
||||
sbwi.apply_to_surface(states);
|
||||
}
|
||||
}
|
||||
Some(BufferType::Shm) => {
|
||||
with_buffer_contents(&buffer, |data, size, buf| {
|
||||
if let Ok(image) =
|
||||
self.image_importer.import_shm(data, size, buf)
|
||||
{
|
||||
let sbwi = SurfaceBufWithImage {
|
||||
image,
|
||||
buffer: buffer.clone(),
|
||||
transform: wl_transform_to_frame_transform(
|
||||
attrs.buffer_transform,
|
||||
),
|
||||
scale: attrs.buffer_scale,
|
||||
};
|
||||
sbwi.apply_to_surface(states);
|
||||
}
|
||||
});
|
||||
}
|
||||
Some(BufferType::SinglePixel) => {
|
||||
let spb = get_single_pixel_buffer(&buffer).unwrap(); // always Ok
|
||||
if let Ok(image) = self.image_importer.import_spb(spb) {
|
||||
let sbwi = SurfaceBufWithImage {
|
||||
image,
|
||||
buffer,
|
||||
transform: wl_transform_to_frame_transform(
|
||||
// does this even matter
|
||||
attrs.buffer_transform,
|
||||
),
|
||||
scale: attrs.buffer_scale,
|
||||
};
|
||||
sbwi.apply_to_surface(states);
|
||||
}
|
||||
}
|
||||
Some(other) => log::warn!("Unsupported wl_buffer format: {other:?}"),
|
||||
None => { /* don't draw anything */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(BufferAssignment::Removed) => {}
|
||||
None => {}
|
||||
}
|
||||
});
|
||||
|
||||
self.redraw_requests.insert(surface.id());
|
||||
}
|
||||
}
|
||||
@@ -198,7 +270,7 @@ impl DmabufHandler for Application {
|
||||
dmabuf: Dmabuf,
|
||||
notifier: ImportNotifier,
|
||||
) {
|
||||
if self.gles_renderer.import_dmabuf(&dmabuf, None).is_ok() {
|
||||
if self.image_importer.get_or_import_dmabuf(dmabuf).is_ok() {
|
||||
let _ = notifier.successful::<Self>();
|
||||
} else {
|
||||
notifier.failed();
|
||||
@@ -235,3 +307,19 @@ pub fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) {
|
||||
|_, _, &()| true,
|
||||
);
|
||||
}
|
||||
|
||||
fn wl_transform_to_frame_transform(
|
||||
transform: wl_output::Transform,
|
||||
) -> wlx_capture::frame::Transform {
|
||||
match transform {
|
||||
wl_output::Transform::Normal => wlx_capture::frame::Transform::Normal,
|
||||
wl_output::Transform::_90 => wlx_capture::frame::Transform::Rotated90,
|
||||
wl_output::Transform::_180 => wlx_capture::frame::Transform::Rotated180,
|
||||
wl_output::Transform::_270 => wlx_capture::frame::Transform::Rotated270,
|
||||
wl_output::Transform::Flipped => wlx_capture::frame::Transform::Flipped,
|
||||
wl_output::Transform::Flipped90 => wlx_capture::frame::Transform::Flipped90,
|
||||
wl_output::Transform::Flipped180 => wlx_capture::frame::Transform::Flipped180,
|
||||
wl_output::Transform::Flipped270 => wlx_capture::frame::Transform::Flipped270,
|
||||
_ => wlx_capture::frame::Transform::Undefined,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,606 +0,0 @@
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
|
||||
use smithay::{
|
||||
backend::renderer::{
|
||||
Bind, Color32F, Frame, Renderer,
|
||||
element::{
|
||||
Kind,
|
||||
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
|
||||
},
|
||||
gles::{GlesRenderer, GlesTexture, ffi},
|
||||
utils::draw_render_elements,
|
||||
},
|
||||
input,
|
||||
utils::{Logical, Point, Rectangle, Size, Transform},
|
||||
wayland::shell::xdg::ToplevelSurface,
|
||||
};
|
||||
use wayvr_ipc::packet_server;
|
||||
|
||||
use crate::{
|
||||
backend::wayvr::time::get_millis, gen_id, ipc::event_queue::SyncEventQueue,
|
||||
subsystem::hid::WheelDelta, windowing::OverlayID,
|
||||
};
|
||||
|
||||
use super::{
|
||||
BlitMethod, WayVRSignal, client::WayVRCompositor, comp::send_frames_surface_tree, egl_data,
|
||||
process, smithay_wrapper, time, window,
|
||||
};
|
||||
|
||||
fn generate_auth_key() -> String {
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
uuid.to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DisplayWindow {
|
||||
pub window_handle: window::WindowHandle,
|
||||
pub toplevel: ToplevelSurface,
|
||||
pub process_handle: process::ProcessHandle,
|
||||
}
|
||||
|
||||
pub struct SpawnProcessResult {
|
||||
pub auth_key: String,
|
||||
pub child: std::process::Child,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DisplayTask {
|
||||
ProcessCleanup(process::ProcessHandle),
|
||||
}
|
||||
|
||||
const MAX_DISPLAY_SIZE: u16 = 8192;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Display {
|
||||
// Display info stuff
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
pub name: String,
|
||||
pub visible: bool,
|
||||
pub layout: packet_server::WvrDisplayWindowLayout,
|
||||
pub overlay_id: Option<OverlayID>,
|
||||
pub wants_redraw: bool,
|
||||
pub rendered_frame_count: u32,
|
||||
pub primary: bool,
|
||||
pub wm: Rc<RefCell<window::WindowManager>>,
|
||||
pub displayed_windows: Vec<DisplayWindow>,
|
||||
wayland_env: super::WaylandEnv,
|
||||
last_pressed_time_ms: u64,
|
||||
pub no_windows_since: Option<u64>,
|
||||
|
||||
// Render data stuff
|
||||
gles_texture: GlesTexture, // TODO: drop texture
|
||||
egl_image: khronos_egl::Image,
|
||||
egl_data: Rc<egl_data::EGLData>,
|
||||
|
||||
pub render_data: egl_data::RenderData,
|
||||
|
||||
pub tasks: SyncEventQueue<DisplayTask>,
|
||||
}
|
||||
|
||||
impl Drop for Display {
|
||||
fn drop(&mut self) {
|
||||
let _ = self
|
||||
.egl_data
|
||||
.egl
|
||||
.destroy_image(self.egl_data.display, self.egl_image);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DisplayInitParams<'a> {
|
||||
pub wm: Rc<RefCell<window::WindowManager>>,
|
||||
pub config: &'a super::Config,
|
||||
pub renderer: &'a mut GlesRenderer,
|
||||
pub egl_data: Rc<egl_data::EGLData>,
|
||||
pub wayland_env: super::WaylandEnv,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
pub name: &'a str,
|
||||
pub primary: bool,
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn new(params: DisplayInitParams) -> anyhow::Result<Self> {
|
||||
if params.width > MAX_DISPLAY_SIZE {
|
||||
anyhow::bail!(
|
||||
"display width ({}) is larger than {}",
|
||||
params.width,
|
||||
MAX_DISPLAY_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
if params.height > MAX_DISPLAY_SIZE {
|
||||
anyhow::bail!(
|
||||
"display height ({}) is larger than {}",
|
||||
params.height,
|
||||
MAX_DISPLAY_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
let tex_format = ffi::RGBA;
|
||||
let internal_format = ffi::RGBA8;
|
||||
|
||||
let tex_id = params.renderer.with_context(|gl| {
|
||||
smithay_wrapper::create_framebuffer_texture(
|
||||
gl,
|
||||
u32::from(params.width),
|
||||
u32::from(params.height),
|
||||
tex_format,
|
||||
internal_format,
|
||||
)
|
||||
})?;
|
||||
|
||||
let egl_image = params.egl_data.create_egl_image(tex_id)?;
|
||||
|
||||
let render_data = match params.config.blit_method {
|
||||
BlitMethod::Dmabuf => match params.egl_data.create_dmabuf_data(&egl_image) {
|
||||
Ok(dmabuf_data) => egl_data::RenderData::Dmabuf(dmabuf_data),
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"create_dmabuf_data failed: {e:?}. Using software blitting (This will be slow!)"
|
||||
);
|
||||
egl_data::RenderData::Software(None)
|
||||
}
|
||||
},
|
||||
BlitMethod::Software => egl_data::RenderData::Software(None),
|
||||
};
|
||||
|
||||
let opaque = false;
|
||||
let size = (i32::from(params.width), i32::from(params.height)).into();
|
||||
let gles_texture = unsafe {
|
||||
GlesTexture::from_raw(params.renderer, Some(tex_format), opaque, tex_id, size)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
egl_data: params.egl_data,
|
||||
width: params.width,
|
||||
height: params.height,
|
||||
name: String::from(params.name),
|
||||
primary: params.primary,
|
||||
wayland_env: params.wayland_env,
|
||||
wm: params.wm,
|
||||
displayed_windows: Vec::new(),
|
||||
render_data,
|
||||
egl_image,
|
||||
gles_texture,
|
||||
last_pressed_time_ms: 0,
|
||||
no_windows_since: None,
|
||||
overlay_id: None,
|
||||
tasks: SyncEventQueue::new(),
|
||||
visible: true,
|
||||
wants_redraw: true,
|
||||
rendered_frame_count: 0,
|
||||
layout: packet_server::WvrDisplayWindowLayout::Tiling,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_packet(&self, handle: DisplayHandle) -> packet_server::WvrDisplay {
|
||||
packet_server::WvrDisplay {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
name: self.name.clone(),
|
||||
visible: self.visible,
|
||||
handle: handle.as_packet(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_window(
|
||||
&mut self,
|
||||
window_handle: window::WindowHandle,
|
||||
process_handle: process::ProcessHandle,
|
||||
toplevel: &ToplevelSurface,
|
||||
) {
|
||||
log::debug!("Attaching toplevel surface into display");
|
||||
self.displayed_windows.push(DisplayWindow {
|
||||
window_handle,
|
||||
process_handle,
|
||||
toplevel: toplevel.clone(),
|
||||
});
|
||||
self.reposition_windows();
|
||||
}
|
||||
|
||||
pub fn remove_window(&mut self, window_handle: window::WindowHandle) {
|
||||
self.displayed_windows
|
||||
.retain(|disp| disp.window_handle != window_handle);
|
||||
}
|
||||
|
||||
pub fn reposition_windows(&mut self) {
|
||||
let window_count = self.displayed_windows.len();
|
||||
|
||||
match &self.layout {
|
||||
packet_server::WvrDisplayWindowLayout::Tiling => {
|
||||
let mut i = 0;
|
||||
for win in &mut self.displayed_windows {
|
||||
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) {
|
||||
if !window.visible {
|
||||
continue;
|
||||
}
|
||||
let d_cur = i as f32 / window_count as f32;
|
||||
let d_next = (i + 1) as f32 / window_count as f32;
|
||||
|
||||
let left = (d_cur * f32::from(self.width)) as i32;
|
||||
let right = (d_next * f32::from(self.width)) as i32;
|
||||
|
||||
window.set_pos(left, 0);
|
||||
window.set_size((right - left) as u32, u32::from(self.height));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
packet_server::WvrDisplayWindowLayout::Stacking(opts) => {
|
||||
let do_margins = |margins: &packet_server::Margins, window: &mut window::Window| {
|
||||
let top = i32::from(margins.top);
|
||||
let bottom = i32::from(self.height) - i32::from(margins.bottom);
|
||||
let left = i32::from(margins.left);
|
||||
let right = i32::from(self.width) - i32::from(margins.right);
|
||||
let width = right - left;
|
||||
let height = bottom - top;
|
||||
if width < 0 || height < 0 {
|
||||
return; // wrong parameters, do nothing!
|
||||
}
|
||||
|
||||
window.set_pos(left, top);
|
||||
window.set_size(width as u32, height as u32);
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
for win in &mut self.displayed_windows {
|
||||
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) {
|
||||
if !window.visible {
|
||||
continue;
|
||||
}
|
||||
do_margins(
|
||||
if i == 0 {
|
||||
&opts.margins_first
|
||||
} else {
|
||||
&opts.margins_rest
|
||||
},
|
||||
window,
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(
|
||||
&mut self,
|
||||
config: &super::Config,
|
||||
handle: &DisplayHandle,
|
||||
signals: &mut SyncEventQueue<WayVRSignal>,
|
||||
) {
|
||||
if self.visible {
|
||||
if !self.displayed_windows.is_empty() {
|
||||
self.no_windows_since = None;
|
||||
} else if let Some(auto_hide_delay) = config.auto_hide_delay
|
||||
&& let Some(s) = self.no_windows_since
|
||||
&& s + u64::from(auto_hide_delay) < get_millis()
|
||||
{
|
||||
// Auto-hide after specific time
|
||||
signals.send(WayVRSignal::DisplayVisibility(*handle, false));
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(task) = self.tasks.read() {
|
||||
match task {
|
||||
DisplayTask::ProcessCleanup(process_handle) => {
|
||||
let count = self.displayed_windows.len();
|
||||
self.displayed_windows
|
||||
.retain(|win| win.process_handle != process_handle);
|
||||
log::info!(
|
||||
"Cleanup finished for display \"{}\". Current window count: {}",
|
||||
self.name,
|
||||
self.displayed_windows.len()
|
||||
);
|
||||
self.no_windows_since = Some(get_millis());
|
||||
|
||||
if count != self.displayed_windows.len() {
|
||||
signals.send(WayVRSignal::BroadcastStateChanged(
|
||||
packet_server::WvrStateChanged::WindowRemoved,
|
||||
));
|
||||
}
|
||||
|
||||
self.reposition_windows();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::significant_drop_tightening)]
|
||||
pub fn tick_render(&mut self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> {
|
||||
let mut gles_texture = self.gles_texture.clone();
|
||||
let mut target = renderer.bind(&mut gles_texture)?;
|
||||
|
||||
let size = Size::from((i32::from(self.width), i32::from(self.height)));
|
||||
let damage: Rectangle<i32, smithay::utils::Physical> = Rectangle::from_size(size);
|
||||
|
||||
let elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = self
|
||||
.displayed_windows
|
||||
.iter()
|
||||
.flat_map(|display_window| {
|
||||
let wm = self.wm.borrow_mut();
|
||||
if let Some(window) = wm.windows.get(&display_window.window_handle) {
|
||||
if !window.visible {
|
||||
return vec![];
|
||||
}
|
||||
render_elements_from_surface_tree(
|
||||
renderer,
|
||||
display_window.toplevel.wl_surface(),
|
||||
(window.pos_x, window.pos_y),
|
||||
1.0,
|
||||
1.0,
|
||||
Kind::Unspecified,
|
||||
)
|
||||
} else {
|
||||
// Failed to fetch window
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut frame = renderer.render(&mut target, size, Transform::Normal)?;
|
||||
|
||||
let clear_color = if self.displayed_windows.is_empty() {
|
||||
Color32F::new(0.5, 0.5, 0.5, 0.5)
|
||||
} else {
|
||||
Color32F::new(0.0, 0.0, 0.0, 0.0)
|
||||
};
|
||||
|
||||
frame.clear(clear_color, &[damage])?;
|
||||
|
||||
draw_render_elements(&mut frame, 1.0, &elements, &[damage])?;
|
||||
|
||||
let _sync_point = frame.finish()?;
|
||||
|
||||
for window in &self.displayed_windows {
|
||||
send_frames_surface_tree(window.toplevel.wl_surface(), time_ms as u32);
|
||||
}
|
||||
|
||||
if let egl_data::RenderData::Software(_) = &self.render_data {
|
||||
// Read OpenGL texture into memory. Slow!
|
||||
let pixel_data = renderer.with_context(|gl| unsafe {
|
||||
gl.BindTexture(ffi::TEXTURE_2D, self.gles_texture.tex_id());
|
||||
|
||||
let len = self.width as usize * self.height as usize * 4;
|
||||
let mut data: Box<[u8]> = Box::new_uninit_slice(len).assume_init();
|
||||
gl.ReadPixels(
|
||||
0,
|
||||
0,
|
||||
i32::from(self.width),
|
||||
i32::from(self.height),
|
||||
ffi::RGBA,
|
||||
ffi::UNSIGNED_BYTE,
|
||||
data.as_mut_ptr().cast(),
|
||||
);
|
||||
|
||||
let data: Arc<[u8]> = Arc::from(data);
|
||||
data
|
||||
})?;
|
||||
|
||||
self.render_data =
|
||||
egl_data::RenderData::Software(Some(egl_data::RenderSoftwarePixelsData {
|
||||
data: pixel_data,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
}));
|
||||
}
|
||||
|
||||
self.rendered_frame_count += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_hovered_window(&self, cursor_x: u32, cursor_y: u32) -> Option<window::WindowHandle> {
|
||||
let wm = self.wm.borrow();
|
||||
|
||||
for cell in self.displayed_windows.iter().rev() {
|
||||
if let Some(window) = wm.windows.get(&cell.window_handle) {
|
||||
if !window.visible {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cursor_x as i32) >= window.pos_x
|
||||
&& (cursor_x as i32) < window.pos_x + window.size_x as i32
|
||||
&& (cursor_y as i32) >= window.pos_y
|
||||
&& (cursor_y as i32) < window.pos_y + window.size_y as i32
|
||||
{
|
||||
return Some(cell.window_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub const fn trigger_rerender(&mut self) {
|
||||
self.wants_redraw = true;
|
||||
}
|
||||
|
||||
pub fn set_visible(&mut self, visible: bool) {
|
||||
log::info!("Display \"{}\" visible: {}", self.name.as_str(), visible);
|
||||
if self.visible == visible {
|
||||
return;
|
||||
}
|
||||
self.visible = visible;
|
||||
if visible {
|
||||
self.no_windows_since = None;
|
||||
self.trigger_rerender();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_layout(&mut self, layout: packet_server::WvrDisplayWindowLayout) {
|
||||
log::info!("Display \"{}\" layout: {:?}", self.name.as_str(), layout);
|
||||
if self.layout == layout {
|
||||
return;
|
||||
}
|
||||
self.layout = layout;
|
||||
self.trigger_rerender();
|
||||
self.reposition_windows();
|
||||
}
|
||||
|
||||
pub fn send_mouse_move(
|
||||
&self,
|
||||
config: &super::Config,
|
||||
manager: &mut WayVRCompositor,
|
||||
x: u32,
|
||||
y: u32,
|
||||
) {
|
||||
let current_ms = time::get_millis();
|
||||
if self.last_pressed_time_ms + u64::from(config.click_freeze_time_ms) > current_ms {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(window_handle) = self.get_hovered_window(x, y) {
|
||||
let wm = self.wm.borrow();
|
||||
if let Some(window) = wm.windows.get(&window_handle) {
|
||||
let surf = window.toplevel.wl_surface().clone();
|
||||
let point = Point::<f64, Logical>::from((
|
||||
f64::from(x as i32 - window.pos_x),
|
||||
f64::from(y as i32 - window.pos_y),
|
||||
));
|
||||
|
||||
manager.seat_pointer.motion(
|
||||
&mut manager.state,
|
||||
Some((surf, Point::from((0.0, 0.0)))),
|
||||
&input::pointer::MotionEvent {
|
||||
serial: manager.serial_counter.next_serial(),
|
||||
time: 0,
|
||||
location: point,
|
||||
},
|
||||
);
|
||||
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn get_mouse_index_number(index: super::MouseIndex) -> u32 {
|
||||
match index {
|
||||
super::MouseIndex::Left => 0x110, /* BTN_LEFT */
|
||||
super::MouseIndex::Center => 0x112, /* BTN_MIDDLE */
|
||||
super::MouseIndex::Right => 0x111, /* BTN_RIGHT */
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_mouse_down(&mut self, manager: &mut WayVRCompositor, index: super::MouseIndex) {
|
||||
// Change keyboard focus to pressed window
|
||||
let loc = manager.seat_pointer.current_location();
|
||||
|
||||
self.last_pressed_time_ms = time::get_millis();
|
||||
|
||||
if let Some(window_handle) =
|
||||
self.get_hovered_window(loc.x.max(0.0) as u32, loc.y.max(0.0) as u32)
|
||||
{
|
||||
let wm = self.wm.borrow();
|
||||
if let Some(window) = wm.windows.get(&window_handle) {
|
||||
let surf = window.toplevel.wl_surface().clone();
|
||||
|
||||
manager.seat_keyboard.set_focus(
|
||||
&mut manager.state,
|
||||
Some(surf),
|
||||
manager.serial_counter.next_serial(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
manager.seat_pointer.button(
|
||||
&mut manager.state,
|
||||
&input::pointer::ButtonEvent {
|
||||
button: Self::get_mouse_index_number(index),
|
||||
serial: manager.serial_counter.next_serial(),
|
||||
time: 0,
|
||||
state: smithay::backend::input::ButtonState::Pressed,
|
||||
},
|
||||
);
|
||||
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
pub fn send_mouse_up(manager: &mut WayVRCompositor, index: super::MouseIndex) {
|
||||
manager.seat_pointer.button(
|
||||
&mut manager.state,
|
||||
&input::pointer::ButtonEvent {
|
||||
button: Self::get_mouse_index_number(index),
|
||||
serial: manager.serial_counter.next_serial(),
|
||||
time: 0,
|
||||
state: smithay::backend::input::ButtonState::Released,
|
||||
},
|
||||
);
|
||||
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
pub fn send_mouse_scroll(manager: &mut WayVRCompositor, delta: WheelDelta) {
|
||||
manager.seat_pointer.axis(
|
||||
&mut manager.state,
|
||||
input::pointer::AxisFrame {
|
||||
source: None,
|
||||
relative_direction: (
|
||||
smithay::backend::input::AxisRelativeDirection::Identical,
|
||||
smithay::backend::input::AxisRelativeDirection::Identical,
|
||||
),
|
||||
time: 0,
|
||||
axis: (f64::from(delta.x), f64::from(-delta.y)),
|
||||
v120: Some((0, (delta.y * -64.0) as i32)),
|
||||
stop: (false, false),
|
||||
},
|
||||
);
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
fn configure_env(&self, cmd: &mut std::process::Command, auth_key: &str) {
|
||||
cmd.env_remove("DISPLAY"); // Goodbye X11
|
||||
cmd.env("WAYLAND_DISPLAY", self.wayland_env.display_num_string());
|
||||
cmd.env("WAYVR_DISPLAY_AUTH", auth_key);
|
||||
}
|
||||
|
||||
pub fn spawn_process(
|
||||
&mut self,
|
||||
exec_path: &str,
|
||||
args: &[&str],
|
||||
env: &[(&str, &str)],
|
||||
working_dir: Option<&str>,
|
||||
) -> anyhow::Result<SpawnProcessResult> {
|
||||
log::info!("Spawning subprocess with exec path \"{exec_path}\"");
|
||||
|
||||
let auth_key = generate_auth_key();
|
||||
|
||||
let mut cmd = std::process::Command::new(exec_path);
|
||||
self.configure_env(&mut cmd, auth_key.as_str());
|
||||
cmd.args(args);
|
||||
if let Some(working_dir) = working_dir {
|
||||
cmd.current_dir(working_dir);
|
||||
}
|
||||
|
||||
for e in env {
|
||||
cmd.env(e.0, e.1);
|
||||
}
|
||||
|
||||
match cmd.spawn() {
|
||||
Ok(child) => Ok(SpawnProcessResult { auth_key, child }),
|
||||
Err(e) => {
|
||||
anyhow::bail!(
|
||||
"Failed to launch process with path \"{exec_path}\": {e}. Make sure your exec path exists."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen_id!(DisplayVec, Display, DisplayCell, DisplayHandle);
|
||||
|
||||
impl DisplayHandle {
|
||||
pub const fn from_packet(handle: packet_server::WvrDisplayHandle) -> Self {
|
||||
Self {
|
||||
generation: handle.generation,
|
||||
idx: handle.idx,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn as_packet(&self) -> packet_server::WvrDisplayHandle {
|
||||
packet_server::WvrDisplayHandle {
|
||||
idx: self.idx,
|
||||
generation: self.generation,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::backend::wayvr::egl_ex::{
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC, PFNEGLQUERYDMABUFFORMATSEXTPROC,
|
||||
PFNEGLQUERYDMABUFMODIFIERSEXTPROC,
|
||||
};
|
||||
|
||||
use super::egl_ex;
|
||||
use anyhow::Context;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EGLData {
|
||||
pub egl: khronos_egl::Instance<khronos_egl::Static>,
|
||||
pub display: khronos_egl::Display,
|
||||
pub config: khronos_egl::Config,
|
||||
pub context: khronos_egl::Context,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bind_egl_function {
|
||||
($func_type:ident, $func:expr) => {
|
||||
std::mem::transmute_copy::<_, $func_type>($func).unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DMAbufModifierInfo {
|
||||
pub modifiers: Vec<u64>,
|
||||
pub fourcc: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderDMAbufData {
|
||||
pub fd: i32,
|
||||
pub stride: i32,
|
||||
pub offset: i32,
|
||||
pub mod_info: DMAbufModifierInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderSoftwarePixelsData {
|
||||
pub data: Arc<[u8]>,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RenderData {
|
||||
Dmabuf(RenderDMAbufData),
|
||||
Software(Option<RenderSoftwarePixelsData>), // will be set if the next image data is available
|
||||
}
|
||||
|
||||
fn load_egl_func(
|
||||
egl: &khronos_egl::Instance<khronos_egl::Static>,
|
||||
func_name: &str,
|
||||
) -> anyhow::Result<extern "system" fn()> {
|
||||
let raw_fn = egl
|
||||
.get_proc_address(func_name)
|
||||
.ok_or_else(|| anyhow::anyhow!("Required EGL function {func_name} not found"))?;
|
||||
Ok(raw_fn)
|
||||
}
|
||||
|
||||
fn get_disp(
|
||||
egl: &khronos_egl::Instance<khronos_egl::Static>,
|
||||
) -> anyhow::Result<khronos_egl::Display> {
|
||||
unsafe {
|
||||
if let Ok(func) = load_egl_func(egl, "eglGetPlatformDisplayEXT") {
|
||||
let egl_get_platform_display_ext =
|
||||
bind_egl_function!(PFNEGLGETPLATFORMDISPLAYEXTPROC, &func);
|
||||
|
||||
let display_ext = egl_get_platform_display_ext(
|
||||
egl_ex::EGL_PLATFORM_WAYLAND_EXT, // platform
|
||||
std::ptr::null_mut(), // void *native_display
|
||||
std::ptr::null_mut(), // EGLint *attrib_list
|
||||
);
|
||||
|
||||
if display_ext.is_null() {
|
||||
log::warn!("eglGetPlatformDisplayEXT failed, using eglGetDisplay instead");
|
||||
} else {
|
||||
return Ok(khronos_egl::Display::from_ptr(display_ext));
|
||||
}
|
||||
}
|
||||
|
||||
egl
|
||||
.get_display(khronos_egl::DEFAULT_DISPLAY)
|
||||
.context(
|
||||
"Both eglGetPlatformDisplayEXT and eglGetDisplay failed. This shouldn't happen unless you don't have any display manager running. Cannot continue, check your EGL installation."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl EGLData {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let egl = khronos_egl::Instance::new(khronos_egl::Static);
|
||||
let display = get_disp(&egl)?;
|
||||
|
||||
let (major, minor) = egl.initialize(display)?;
|
||||
log::debug!("EGL version: {major}.{minor}");
|
||||
|
||||
let attrib_list = [
|
||||
khronos_egl::RED_SIZE,
|
||||
8,
|
||||
khronos_egl::GREEN_SIZE,
|
||||
8,
|
||||
khronos_egl::BLUE_SIZE,
|
||||
8,
|
||||
khronos_egl::SURFACE_TYPE,
|
||||
khronos_egl::WINDOW_BIT,
|
||||
khronos_egl::RENDERABLE_TYPE,
|
||||
khronos_egl::OPENGL_BIT,
|
||||
khronos_egl::NONE,
|
||||
];
|
||||
|
||||
let config = egl
|
||||
.choose_first_config(display, &attrib_list)?
|
||||
.context("Failed to get EGL config")?;
|
||||
|
||||
egl.bind_api(khronos_egl::OPENGL_ES_API)?;
|
||||
|
||||
log::debug!("eglCreateContext");
|
||||
|
||||
// Require OpenGL ES 3.0
|
||||
let context_attrib_list = [
|
||||
khronos_egl::CONTEXT_MAJOR_VERSION,
|
||||
3,
|
||||
khronos_egl::CONTEXT_MINOR_VERSION,
|
||||
0,
|
||||
khronos_egl::NONE,
|
||||
];
|
||||
|
||||
let context = egl.create_context(display, config, None, &context_attrib_list)?;
|
||||
|
||||
log::debug!("eglMakeCurrent");
|
||||
|
||||
egl.make_current(display, None, None, Some(context))?;
|
||||
|
||||
Ok(Self {
|
||||
egl,
|
||||
display,
|
||||
config,
|
||||
context,
|
||||
})
|
||||
}
|
||||
|
||||
fn query_dmabuf_mod_info(&self) -> anyhow::Result<DMAbufModifierInfo> {
|
||||
let target_fourcc = 0x3432_4258; //XB24
|
||||
|
||||
unsafe {
|
||||
let egl_query_dmabuf_formats_ext = bind_egl_function!(
|
||||
PFNEGLQUERYDMABUFFORMATSEXTPROC,
|
||||
&load_egl_func(&self.egl, "eglQueryDmaBufFormatsEXT")?
|
||||
);
|
||||
|
||||
// Query format count
|
||||
let mut num_formats: khronos_egl::Int = 0;
|
||||
egl_query_dmabuf_formats_ext(
|
||||
self.display.as_ptr(),
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
&raw mut num_formats,
|
||||
);
|
||||
|
||||
// Retrieve format list
|
||||
let mut formats: Vec<i32> = vec![0; num_formats as usize];
|
||||
egl_query_dmabuf_formats_ext(
|
||||
self.display.as_ptr(),
|
||||
num_formats,
|
||||
formats.as_mut_ptr(),
|
||||
&raw mut num_formats,
|
||||
);
|
||||
|
||||
/*for (idx, format) in formats.iter().enumerate() {
|
||||
let bytes = format.to_le_bytes();
|
||||
log::trace!(
|
||||
"idx {}, format {}{}{}{} (hex {:#x})",
|
||||
idx,
|
||||
bytes[0] as char,
|
||||
bytes[1] as char,
|
||||
bytes[2] as char,
|
||||
bytes[3] as char,
|
||||
format
|
||||
);
|
||||
}*/
|
||||
|
||||
let egl_query_dmabuf_modifiers_ext = bind_egl_function!(
|
||||
PFNEGLQUERYDMABUFMODIFIERSEXTPROC,
|
||||
&load_egl_func(&self.egl, "eglQueryDmaBufModifiersEXT")?
|
||||
);
|
||||
|
||||
let mut num_mods: khronos_egl::Int = 0;
|
||||
|
||||
// Query modifier count
|
||||
egl_query_dmabuf_modifiers_ext(
|
||||
self.display.as_ptr(),
|
||||
target_fourcc,
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
&raw mut num_mods,
|
||||
);
|
||||
|
||||
if num_mods == 0 {
|
||||
anyhow::bail!("eglQueryDmaBufModifiersEXT modifier count is zero");
|
||||
}
|
||||
|
||||
let mut mods: Vec<u64> = vec![0; num_mods as usize];
|
||||
egl_query_dmabuf_modifiers_ext(
|
||||
self.display.as_ptr(),
|
||||
target_fourcc,
|
||||
num_mods,
|
||||
mods.as_mut_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
&raw mut num_mods,
|
||||
);
|
||||
|
||||
if mods[0] == 0xFFFF_FFFF_FFFF_FFFF {
|
||||
anyhow::bail!("modifier is -1")
|
||||
}
|
||||
|
||||
log::trace!("Modifier list:");
|
||||
for modifier in &mods {
|
||||
log::trace!("{modifier:#x}");
|
||||
}
|
||||
|
||||
// We should not change these modifier values. Passing all of them to the Vulkan dmabuf
|
||||
// texture system causes significant graphical corruption due to invalid memory layout and
|
||||
// tiling on this specific GPU model (very probably others also have the same issue).
|
||||
// It is not guaranteed that this modifier will be present in other models.
|
||||
// If not, the full list of modifiers will be passed. Further testing is required.
|
||||
// For now, it looks like only NAVI32-based gpus have this problem.
|
||||
let mod_whitelist: [u64; 2] = [
|
||||
0x200_0000_2086_bf04, /* AMD RX 7800 XT, Navi32 */
|
||||
0x200_0000_1866_bf04, /* AMD RX 7600 XT, Navi33 */
|
||||
];
|
||||
|
||||
for modifier in &mod_whitelist {
|
||||
if mods.contains(modifier) {
|
||||
log::warn!("Using whitelisted dmabuf tiling modifier: {modifier:#x}");
|
||||
mods = vec![*modifier, 0x0 /* also important (???) */];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DMAbufModifierInfo {
|
||||
modifiers: mods,
|
||||
fourcc: target_fourcc as u32,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_dmabuf_data(
|
||||
&self,
|
||||
egl_image: &khronos_egl::Image,
|
||||
) -> anyhow::Result<RenderDMAbufData> {
|
||||
use egl_ex::PFNEGLEXPORTDMABUFIMAGEMESAPROC as FUNC;
|
||||
unsafe {
|
||||
let egl_export_dmabuf_image_mesa =
|
||||
bind_egl_function!(FUNC, &load_egl_func(&self.egl, "eglExportDMABUFImageMESA")?);
|
||||
|
||||
let mut fds: [i32; 3] = [0; 3];
|
||||
let mut strides: [i32; 3] = [0; 3];
|
||||
let mut offsets: [i32; 3] = [0; 3];
|
||||
|
||||
let ret = egl_export_dmabuf_image_mesa(
|
||||
self.display.as_ptr(),
|
||||
egl_image.as_ptr(),
|
||||
fds.as_mut_ptr(),
|
||||
strides.as_mut_ptr(),
|
||||
offsets.as_mut_ptr(),
|
||||
);
|
||||
|
||||
if ret != khronos_egl::TRUE {
|
||||
anyhow::bail!("eglExportDMABUFImageMESA failed with return code {ret}");
|
||||
}
|
||||
|
||||
if fds[0] <= 0 {
|
||||
anyhow::bail!("fd is <=0 (got {})", fds[0]);
|
||||
}
|
||||
|
||||
// many planes in RGB data?
|
||||
if fds[1] != 0 || strides[1] != 0 || offsets[1] != 0 {
|
||||
anyhow::bail!("multi-planar data received, packed RGB expected");
|
||||
}
|
||||
|
||||
if strides[0] < 0 {
|
||||
anyhow::bail!("strides is < 0");
|
||||
}
|
||||
|
||||
if offsets[0] < 0 {
|
||||
anyhow::bail!("offsets is < 0");
|
||||
}
|
||||
|
||||
let mod_info = self.query_dmabuf_mod_info()?;
|
||||
|
||||
Ok(RenderDMAbufData {
|
||||
fd: fds[0],
|
||||
stride: strides[0],
|
||||
offset: offsets[0],
|
||||
mod_info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_egl_image(&self, gl_tex_id: u32) -> anyhow::Result<khronos_egl::Image> {
|
||||
unsafe {
|
||||
Ok(self.egl.create_image(
|
||||
self.display,
|
||||
self.context,
|
||||
khronos_egl::GL_TEXTURE_2D as std::ffi::c_uint,
|
||||
khronos_egl::ClientBuffer::from_ptr(gl_tex_id as *mut std::ffi::c_void),
|
||||
&[khronos_egl::ATTRIB_NONE],
|
||||
)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
pub const EGL_PLATFORM_WAYLAND_EXT: khronos_egl::Enum = 0x31D8;
|
||||
|
||||
// eglGetPlatformDisplayEXT
|
||||
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_platform_base.txt
|
||||
pub type PFNEGLGETPLATFORMDISPLAYEXTPROC = Option<
|
||||
unsafe extern "C" fn(
|
||||
platform: khronos_egl::Enum,
|
||||
native_display: *mut std::ffi::c_void,
|
||||
attrib_list: *mut khronos_egl::Enum,
|
||||
) -> khronos_egl::EGLDisplay,
|
||||
>;
|
||||
|
||||
// eglExportDMABUFImageMESA
|
||||
// https://registry.khronos.org/EGL/extensions/MESA/EGL_MESA_image_dma_buf_export.txt
|
||||
pub type PFNEGLEXPORTDMABUFIMAGEMESAPROC = Option<
|
||||
unsafe extern "C" fn(
|
||||
dpy: khronos_egl::EGLDisplay,
|
||||
image: khronos_egl::EGLImage,
|
||||
fds: *mut i32,
|
||||
strides: *mut khronos_egl::Int,
|
||||
offsets: *mut khronos_egl::Int,
|
||||
) -> khronos_egl::Boolean,
|
||||
>;
|
||||
|
||||
// eglQueryDmaBufModifiersEXT
|
||||
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
|
||||
pub type PFNEGLQUERYDMABUFMODIFIERSEXTPROC = Option<
|
||||
unsafe extern "C" fn(
|
||||
dpy: khronos_egl::EGLDisplay,
|
||||
format: khronos_egl::Int,
|
||||
max_modifiers: khronos_egl::Int,
|
||||
modifiers: *mut u64,
|
||||
external_only: *mut khronos_egl::Boolean,
|
||||
num_modifiers: *mut khronos_egl::Int,
|
||||
) -> khronos_egl::Boolean,
|
||||
>;
|
||||
|
||||
// eglQueryDmaBufFormatsEXT
|
||||
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
|
||||
pub type PFNEGLQUERYDMABUFFORMATSEXTPROC = Option<
|
||||
unsafe extern "C" fn(
|
||||
dpy: khronos_egl::EGLDisplay,
|
||||
max_formats: khronos_egl::Int,
|
||||
formats: *mut khronos_egl::Int,
|
||||
num_formats: *mut khronos_egl::Int,
|
||||
) -> khronos_egl::Boolean,
|
||||
>;
|
||||
111
wlx-overlay-s/src/backend/wayvr/image_importer.rs
Normal file
111
wlx-overlay-s/src/backend/wayvr/image_importer.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
use std::{collections::HashMap, os::fd::AsRawFd, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use smithay::{
|
||||
backend::allocator::{
|
||||
Buffer,
|
||||
dmabuf::{Dmabuf, WeakDmabuf},
|
||||
},
|
||||
wayland::{
|
||||
shm::{BufferData, shm_format_to_fourcc},
|
||||
single_pixel_buffer::SinglePixelBufferUserData,
|
||||
},
|
||||
};
|
||||
use vulkano::{format::Format, image::view::ImageView};
|
||||
use wgui::gfx::WGfx;
|
||||
use wlx_capture::frame::{DmabufFrame, FrameFormat, Transform};
|
||||
|
||||
use crate::graphics::dmabuf::{WGfxDmabuf, fourcc_to_vk};
|
||||
|
||||
pub struct ImageImporter {
|
||||
gfx: Arc<WGfx>,
|
||||
dmabufs: HashMap<WeakDmabuf, Arc<ImageView>>,
|
||||
}
|
||||
|
||||
impl ImageImporter {
|
||||
pub fn new(gfx: Arc<WGfx>) -> Self {
|
||||
Self {
|
||||
gfx,
|
||||
dmabufs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_spb(
|
||||
&mut self,
|
||||
spb: &SinglePixelBufferUserData,
|
||||
) -> anyhow::Result<Arc<ImageView>> {
|
||||
let mut cmd_buf = self.gfx.create_xfer_command_buffer(
|
||||
vulkano::command_buffer::CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
let rgba = spb.rgba8888();
|
||||
let image = cmd_buf.upload_image(1, 1, Format::R8G8B8A8_UNORM, &rgba)?;
|
||||
|
||||
cmd_buf.build_and_execute_now()?; //TODO: async
|
||||
|
||||
let image_view = ImageView::new_default(image)?;
|
||||
Ok(image_view)
|
||||
}
|
||||
|
||||
pub fn import_shm(
|
||||
&mut self,
|
||||
data: *const u8,
|
||||
size: usize,
|
||||
bd: BufferData,
|
||||
) -> anyhow::Result<Arc<ImageView>> {
|
||||
let mut cmd_buf = self.gfx.create_xfer_command_buffer(
|
||||
vulkano::command_buffer::CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
let fourcc = shm_format_to_fourcc(bd.format)
|
||||
.with_context(|| format!("Could not conver {:?} to fourcc", bd.format))?;
|
||||
|
||||
let format = fourcc_to_vk(fourcc)
|
||||
.with_context(|| format!("Could not convert {fourcc} to vkFormat"))?;
|
||||
|
||||
let data = unsafe { std::slice::from_raw_parts(data, size) };
|
||||
let image = cmd_buf.upload_image(bd.width as _, bd.height as _, format, data)?;
|
||||
|
||||
cmd_buf.build_and_execute_now()?; //TODO: async
|
||||
|
||||
let image_view = ImageView::new_default(image)?;
|
||||
Ok(image_view)
|
||||
}
|
||||
|
||||
pub fn get_or_import_dmabuf(&mut self, dmabuf: Dmabuf) -> anyhow::Result<Arc<ImageView>> {
|
||||
let mut frame = DmabufFrame {
|
||||
format: FrameFormat {
|
||||
width: dmabuf.width(),
|
||||
height: dmabuf.height(),
|
||||
drm_format: dmabuf.format(),
|
||||
transform: Transform::Undefined,
|
||||
},
|
||||
num_planes: dmabuf.num_planes(),
|
||||
planes: Default::default(),
|
||||
mouse: None,
|
||||
};
|
||||
|
||||
for (i, handle) in dmabuf.handles().enumerate() {
|
||||
// even if the original OwnedFd is dropped, the vkImage will hold reference on the DMA-buf
|
||||
frame.planes[i].fd = Some(handle.as_raw_fd());
|
||||
}
|
||||
|
||||
for (i, offset) in dmabuf.offsets().enumerate() {
|
||||
frame.planes[i].offset = offset;
|
||||
}
|
||||
|
||||
for (i, stride) in dmabuf.strides().enumerate() {
|
||||
frame.planes[i].stride = stride as _;
|
||||
}
|
||||
|
||||
let image = self.gfx.dmabuf_texture(frame)?;
|
||||
let image_view = ImageView::new_default(image)?;
|
||||
self.dmabufs.insert(dmabuf.weak(), image_view.clone());
|
||||
|
||||
Ok(image_view)
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) {
|
||||
self.dmabufs.retain(|k, _| !k.is_gone());
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,22 @@
|
||||
pub mod client;
|
||||
mod comp;
|
||||
pub mod display;
|
||||
pub mod egl_data;
|
||||
mod egl_ex;
|
||||
mod handle;
|
||||
mod image_importer;
|
||||
pub mod process;
|
||||
mod smithay_wrapper;
|
||||
mod time;
|
||||
pub mod window;
|
||||
use anyhow::Context;
|
||||
use comp::Application;
|
||||
use display::{Display, DisplayInitParams, DisplayVec};
|
||||
use process::ProcessVec;
|
||||
use serde::Deserialize;
|
||||
use slotmap::SecondaryMap;
|
||||
use smallvec::SmallVec;
|
||||
use smithay::{
|
||||
backend::{
|
||||
egl,
|
||||
renderer::{ImportDma, gles::GlesRenderer},
|
||||
},
|
||||
input::{SeatState, keyboard::XkbConfig},
|
||||
output::{Mode, Output},
|
||||
reexports::wayland_server::{self, backend::ClientId},
|
||||
reexports::wayland_server::{self, backend::ClientId, protocol::wl_buffer},
|
||||
wayland::{
|
||||
compositor,
|
||||
compositor::{self, SurfaceData},
|
||||
dmabuf::{DmabufFeedbackBuilder, DmabufState},
|
||||
selection::data_device::DataDeviceState,
|
||||
shell::xdg::{ToplevelSurface, XdgShellState},
|
||||
@@ -33,20 +26,28 @@ use smithay::{
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet},
|
||||
mem::MaybeUninit,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use time::get_millis;
|
||||
use wayvr_ipc::{
|
||||
packet_client::{self},
|
||||
packet_server,
|
||||
};
|
||||
use vulkano::image::view::ImageView;
|
||||
use wayvr_ipc::packet_server;
|
||||
use wgui::gfx::WGfx;
|
||||
use wlx_capture::frame::Transform;
|
||||
use xkbcommon::xkb;
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
task::{OverlayTask, TaskType},
|
||||
wayvr::{image_importer::ImageImporter, window::Window},
|
||||
},
|
||||
graphics::WGfxExtras,
|
||||
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},
|
||||
state::AppState,
|
||||
subsystem::hid::{MODS_TO_KEYS, WheelDelta},
|
||||
windowing::{OverlayID, OverlaySelector},
|
||||
};
|
||||
|
||||
const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle";
|
||||
@@ -109,17 +110,17 @@ pub struct Config {
|
||||
|
||||
pub struct WayVRState {
|
||||
time_start: u64,
|
||||
pub displays: display::DisplayVec,
|
||||
pub manager: client::WayVRCompositor,
|
||||
pub wm: Rc<RefCell<window::WindowManager>>,
|
||||
egl_data: Rc<egl_data::EGLData>,
|
||||
pub processes: process::ProcessVec,
|
||||
pub config: Config,
|
||||
dashboard_display: Option<display::DisplayHandle>,
|
||||
pub tasks: SyncEventQueue<WayVRTask>,
|
||||
ticks: u64,
|
||||
cur_modifiers: u8,
|
||||
signals: SyncEventQueue<WayVRSignal>,
|
||||
mouse_freeze: Instant,
|
||||
window_to_overlay: HashMap<window::WindowHandle, OverlayID>,
|
||||
overlay_to_window: SecondaryMap<OverlayID, window::WindowHandle>,
|
||||
}
|
||||
|
||||
pub struct WayVR {
|
||||
@@ -134,15 +135,16 @@ pub enum MouseIndex {
|
||||
|
||||
pub enum TickTask {
|
||||
NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message
|
||||
NewDisplay(
|
||||
packet_client::WvrDisplayCreateParams,
|
||||
Option<display::DisplayHandle>, /* existing handle? */
|
||||
),
|
||||
}
|
||||
|
||||
impl WayVR {
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
pub fn new(config: Config, signals: SyncEventQueue<WayVRSignal>) -> anyhow::Result<Self> {
|
||||
pub fn new(
|
||||
gfx: Arc<WGfx>,
|
||||
gfx_extras: &WGfxExtras,
|
||||
config: Config,
|
||||
signals: SyncEventQueue<WayVRSignal>,
|
||||
) -> anyhow::Result<Self> {
|
||||
log::info!("Initializing WayVR");
|
||||
let display: wayland_server::Display<Application> = wayland_server::Display::new()?;
|
||||
let dh = display.handle();
|
||||
@@ -153,8 +155,8 @@ impl WayVR {
|
||||
let data_device = DataDeviceState::new::<Application>(&dh);
|
||||
let mut seat = seat_state.new_wl_seat(&dh, "wayvr");
|
||||
|
||||
let dummy_width = 1280;
|
||||
let dummy_height = 720;
|
||||
let dummy_width = 1920;
|
||||
let dummy_height = 1080;
|
||||
let dummy_milli_hz = 60000; /* refresh rate in millihertz */
|
||||
|
||||
let output = Output::new(
|
||||
@@ -176,53 +178,38 @@ impl WayVR {
|
||||
output.change_current_state(Some(mode), None, None, None);
|
||||
output.set_preferred(mode);
|
||||
|
||||
let egl_data = egl_data::EGLData::new()?;
|
||||
|
||||
let smithay_display = smithay_wrapper::get_egl_display(&egl_data)?;
|
||||
let smithay_context = smithay_wrapper::get_egl_context(&egl_data, &smithay_display)?;
|
||||
|
||||
let render_node = egl::EGLDevice::device_for_display(&smithay_display)
|
||||
.and_then(|device| device.try_get_render_node());
|
||||
|
||||
let gles_renderer = unsafe { GlesRenderer::new(smithay_context)? };
|
||||
|
||||
let dmabuf_default_feedback = match render_node {
|
||||
Ok(Some(node)) => {
|
||||
let dmabuf_formats = gles_renderer.dmabuf_formats();
|
||||
let dmabuf_default_feedback =
|
||||
DmabufFeedbackBuilder::new(node.dev_id(), dmabuf_formats)
|
||||
.build()
|
||||
.unwrap();
|
||||
Some(dmabuf_default_feedback)
|
||||
}
|
||||
Ok(None) => {
|
||||
log::warn!("dmabuf: Failed to query render node");
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("dmabuf: Failed to get egl device for display: {err}");
|
||||
None
|
||||
}
|
||||
let main_device = {
|
||||
let (major, minor) = gfx_extras.drm_device.as_ref().context("No DRM device!")?;
|
||||
libc::makedev(*major as _, *minor as _)
|
||||
};
|
||||
|
||||
let dmabuf_state = dmabuf_default_feedback.map_or_else(
|
||||
|| {
|
||||
let dmabuf_formats = gles_renderer.dmabuf_formats();
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global =
|
||||
dmabuf_state.create_global::<Application>(&display.handle(), dmabuf_formats);
|
||||
(dmabuf_state, dmabuf_global, None)
|
||||
},
|
||||
|default_feedback| {
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global = dmabuf_state
|
||||
.create_global_with_default_feedback::<Application>(
|
||||
&display.handle(),
|
||||
&default_feedback,
|
||||
);
|
||||
(dmabuf_state, dmabuf_global, Some(default_feedback))
|
||||
},
|
||||
);
|
||||
// this will throw a compile-time error if smithay's drm-fourcc is out of sync with wlx-capture's
|
||||
let mut formats: Vec<smithay::backend::allocator::Format> = vec![];
|
||||
|
||||
for f in gfx_extras.drm_formats.iter() {
|
||||
formats.push(f.clone());
|
||||
}
|
||||
|
||||
let dmabuf_state = DmabufFeedbackBuilder::new(main_device, formats.clone())
|
||||
.build()
|
||||
.map_or_else(
|
||||
|_| {
|
||||
log::info!("Falling back to zwp_linux_dmabuf_v1 version 3.");
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global =
|
||||
dmabuf_state.create_global::<Application>(&display.handle(), formats);
|
||||
(dmabuf_state, dmabuf_global, None)
|
||||
},
|
||||
|default_feedback| {
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global = dmabuf_state
|
||||
.create_global_with_default_feedback::<Application>(
|
||||
&display.handle(),
|
||||
&default_feedback,
|
||||
);
|
||||
(dmabuf_state, dmabuf_global, Some(default_feedback))
|
||||
},
|
||||
);
|
||||
|
||||
let seat_keyboard = seat.add_keyboard(
|
||||
XkbConfig::default(),
|
||||
@@ -233,7 +220,10 @@ impl WayVR {
|
||||
|
||||
let tasks = SyncEventQueue::new();
|
||||
|
||||
let dma_importer = ImageImporter::new(gfx);
|
||||
|
||||
let state = Application {
|
||||
image_importer: dma_importer,
|
||||
compositor,
|
||||
xdg_shell,
|
||||
seat_state,
|
||||
@@ -242,7 +232,6 @@ impl WayVR {
|
||||
wayvr_tasks: tasks.clone(),
|
||||
redraw_requests: HashSet::new(),
|
||||
dmabuf_state,
|
||||
gles_renderer,
|
||||
};
|
||||
|
||||
let time_start = get_millis();
|
||||
@@ -250,48 +239,21 @@ impl WayVR {
|
||||
let state = WayVRState {
|
||||
time_start,
|
||||
manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?,
|
||||
displays: DisplayVec::new(),
|
||||
processes: ProcessVec::new(),
|
||||
egl_data: Rc::new(egl_data),
|
||||
wm: Rc::new(RefCell::new(window::WindowManager::new())),
|
||||
config,
|
||||
dashboard_display: None,
|
||||
ticks: 0,
|
||||
tasks,
|
||||
cur_modifiers: 0,
|
||||
signals,
|
||||
mouse_freeze: Instant::now(),
|
||||
window_to_overlay: HashMap::new(),
|
||||
overlay_to_window: SecondaryMap::new(),
|
||||
};
|
||||
|
||||
Ok(Self { state })
|
||||
}
|
||||
|
||||
pub fn render_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<bool> {
|
||||
let display = self
|
||||
.state
|
||||
.displays
|
||||
.get_mut(&display)
|
||||
.context(STR_INVALID_HANDLE_DISP)?;
|
||||
|
||||
/* Buffer warm-up is required, always two first calls of this function are always rendered */
|
||||
if !display.wants_redraw && display.rendered_frame_count >= 2 {
|
||||
// Nothing changed, do not render
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !display.visible {
|
||||
// Display is invisible, do not render
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// millis since the start of wayvr
|
||||
let time_ms = get_millis() - self.state.time_start;
|
||||
|
||||
display.tick_render(&mut self.state.manager.state.gles_renderer, time_ms)?;
|
||||
display.wants_redraw = false;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
pub fn tick_events(&mut self, app: &mut AppState) -> anyhow::Result<Vec<TickTask>> {
|
||||
let mut tasks: Vec<TickTask> = Vec::new();
|
||||
@@ -303,43 +265,17 @@ impl WayVR {
|
||||
signals: &app.wayvr_signals,
|
||||
});
|
||||
|
||||
// Check for redraw events
|
||||
for (_, disp) in self.state.displays.iter_mut() {
|
||||
for disp_window in &disp.displayed_windows {
|
||||
if self
|
||||
.state
|
||||
.manager
|
||||
.state
|
||||
.check_redraw(disp_window.toplevel.wl_surface())
|
||||
{
|
||||
disp.wants_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tick all child processes
|
||||
let mut to_remove: SmallVec<[(process::ProcessHandle, display::DisplayHandle); 2]> =
|
||||
SmallVec::new();
|
||||
let mut to_remove: SmallVec<[process::ProcessHandle; 2]> = SmallVec::new();
|
||||
|
||||
for (handle, process) in self.state.processes.iter_mut() {
|
||||
if !process.is_running() {
|
||||
to_remove.push((handle, process.display_handle()));
|
||||
to_remove.push(handle);
|
||||
}
|
||||
}
|
||||
|
||||
for (p_handle, disp_handle) in &to_remove {
|
||||
for p_handle in &to_remove {
|
||||
self.state.processes.remove(p_handle);
|
||||
|
||||
if let Some(display) = self.state.displays.get_mut(disp_handle) {
|
||||
display
|
||||
.tasks
|
||||
.send(display::DisplayTask::ProcessCleanup(*p_handle));
|
||||
display.wants_redraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (handle, display) in self.state.displays.iter_mut() {
|
||||
display.tick(&self.state.config, &handle, &mut app.wayvr_signals);
|
||||
}
|
||||
|
||||
if !to_remove.is_empty() {
|
||||
@@ -374,16 +310,10 @@ impl WayVR {
|
||||
.state
|
||||
.wm
|
||||
.borrow_mut()
|
||||
.create_window(client.display_handle, &toplevel);
|
||||
.create_window(&toplevel, process_handle);
|
||||
|
||||
let Some(display) = self.state.displays.get_mut(&client.display_handle)
|
||||
else {
|
||||
// This shouldn't happen, scream if it does
|
||||
log::error!("Could not attach window handle into display");
|
||||
continue;
|
||||
};
|
||||
//TODO: create overlay
|
||||
|
||||
display.add_window(window_handle, process_handle, &toplevel);
|
||||
app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged(
|
||||
packet_server::WvrStateChanged::WindowCreated,
|
||||
));
|
||||
@@ -401,18 +331,15 @@ impl WayVR {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(display) = self.state.displays.get_mut(&client.display_handle)
|
||||
else {
|
||||
log::warn!("DropToplevel: Couldn't find matching display");
|
||||
continue;
|
||||
};
|
||||
if let Some(oid) = self.state.window_to_overlay.get(&window_handle) {
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Drop(
|
||||
OverlaySelector::Id(*oid),
|
||||
)));
|
||||
}
|
||||
|
||||
display.remove_window(window_handle);
|
||||
wm.remove_window(window_handle);
|
||||
|
||||
drop(wm);
|
||||
|
||||
display.reposition_windows();
|
||||
}
|
||||
}
|
||||
WayVRTask::ProcessTerminationRequest(process_handle) => {
|
||||
@@ -423,12 +350,11 @@ impl WayVR {
|
||||
}
|
||||
}
|
||||
|
||||
self.state
|
||||
.manager
|
||||
.tick_wayland(&mut self.state.displays, &mut self.state.processes)?;
|
||||
self.state.manager.tick_wayland(&mut self.state.processes)?;
|
||||
|
||||
if self.state.ticks.is_multiple_of(200) {
|
||||
self.state.manager.cleanup_clients();
|
||||
self.state.manager.cleanup_handles();
|
||||
}
|
||||
|
||||
self.state.ticks += 1;
|
||||
@@ -436,44 +362,6 @@ impl WayVR {
|
||||
Ok(tasks)
|
||||
}
|
||||
|
||||
pub fn tick_finish(&mut self) -> anyhow::Result<()> {
|
||||
self.state
|
||||
.manager
|
||||
.state
|
||||
.gles_renderer
|
||||
.with_context(|gl| unsafe {
|
||||
gl.Flush();
|
||||
gl.Finish();
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_primary_display(displays: &DisplayVec) -> Option<display::DisplayHandle> {
|
||||
for (idx, cell) in displays.vec.iter().enumerate() {
|
||||
if let Some(cell) = cell
|
||||
&& cell.obj.primary
|
||||
{
|
||||
return Some(DisplayVec::get_handle(cell, idx));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_display_by_name(
|
||||
displays: &DisplayVec,
|
||||
name: &str,
|
||||
) -> Option<display::DisplayHandle> {
|
||||
for (idx, cell) in displays.vec.iter().enumerate() {
|
||||
if let Some(cell) = cell
|
||||
&& cell.obj.name == name
|
||||
{
|
||||
return Some(DisplayVec::get_handle(cell, idx));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) {
|
||||
self.state
|
||||
.tasks
|
||||
@@ -482,24 +370,30 @@ impl WayVR {
|
||||
}
|
||||
|
||||
impl WayVRState {
|
||||
pub fn send_mouse_move(&mut self, display: display::DisplayHandle, x: u32, y: u32) {
|
||||
if let Some(display) = self.displays.get(&display) {
|
||||
display.send_mouse_move(&self.config, &mut self.manager, x, y);
|
||||
pub fn send_mouse_move(&mut self, handle: window::WindowHandle, x: u32, y: u32) {
|
||||
if self.mouse_freeze > Instant::now() {
|
||||
return;
|
||||
}
|
||||
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&handle) {
|
||||
window.send_mouse_move(&mut self.manager, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_mouse_down(&mut self, display: display::DisplayHandle, index: MouseIndex) {
|
||||
if let Some(display) = self.displays.get_mut(&display) {
|
||||
display.send_mouse_down(&mut self.manager, index);
|
||||
pub fn send_mouse_down(&mut self, handle: window::WindowHandle, index: MouseIndex) {
|
||||
self.mouse_freeze =
|
||||
Instant::now() + Duration::from_millis(self.config.click_freeze_time_ms as _);
|
||||
|
||||
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&handle) {
|
||||
window.send_mouse_down(&mut self.manager, index);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_mouse_up(&mut self, index: MouseIndex) {
|
||||
Display::send_mouse_up(&mut self.manager, index);
|
||||
Window::send_mouse_up(&mut self.manager, index);
|
||||
}
|
||||
|
||||
pub fn send_mouse_scroll(&mut self, delta: WheelDelta) {
|
||||
Display::send_mouse_scroll(&mut self.manager, delta);
|
||||
Window::send_mouse_scroll(&mut self.manager, delta);
|
||||
}
|
||||
|
||||
pub fn send_key(&mut self, virtual_key: u32, down: bool) {
|
||||
@@ -523,125 +417,9 @@ impl WayVRState {
|
||||
self.cur_modifiers = modifiers;
|
||||
}
|
||||
|
||||
pub fn set_display_visible(&mut self, display: display::DisplayHandle, visible: bool) {
|
||||
if let Some(display) = self.displays.get_mut(&display) {
|
||||
display.set_visible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_display_layout(
|
||||
&mut self,
|
||||
display: display::DisplayHandle,
|
||||
layout: packet_server::WvrDisplayWindowLayout,
|
||||
) {
|
||||
if let Some(display) = self.displays.get_mut(&display) {
|
||||
display.set_layout(layout);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_render_data(
|
||||
&self,
|
||||
display: display::DisplayHandle,
|
||||
) -> Option<&egl_data::RenderData> {
|
||||
self.displays
|
||||
.get(&display)
|
||||
.map(|display| &display.render_data)
|
||||
}
|
||||
|
||||
pub fn create_display(
|
||||
&mut self,
|
||||
width: u16,
|
||||
height: u16,
|
||||
name: &str,
|
||||
primary: bool,
|
||||
) -> anyhow::Result<display::DisplayHandle> {
|
||||
let display = display::Display::new(DisplayInitParams {
|
||||
wm: self.wm.clone(),
|
||||
egl_data: self.egl_data.clone(),
|
||||
renderer: &mut self.manager.state.gles_renderer,
|
||||
wayland_env: self.manager.wayland_env.clone(),
|
||||
config: &self.config,
|
||||
width,
|
||||
height,
|
||||
name,
|
||||
primary,
|
||||
})?;
|
||||
let handle = self.displays.add(display);
|
||||
|
||||
self.signals.send(WayVRSignal::BroadcastStateChanged(
|
||||
packet_server::WvrStateChanged::DisplayCreated,
|
||||
));
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
pub fn destroy_display(&mut self, handle: display::DisplayHandle) -> anyhow::Result<()> {
|
||||
let Some(display) = self.displays.get(&handle) else {
|
||||
anyhow::bail!("Display not found");
|
||||
};
|
||||
|
||||
if let Some(overlay_id) = display.overlay_id {
|
||||
self.signals.send(WayVRSignal::DropOverlay(overlay_id));
|
||||
} else {
|
||||
log::warn!("Destroying display without OverlayID set"); // This shouldn't happen, but log it anyways.
|
||||
}
|
||||
|
||||
let mut process_names = Vec::<String>::new();
|
||||
|
||||
for (_, process) in self.processes.iter_mut() {
|
||||
if process.display_handle() == handle {
|
||||
process_names.push(process.get_name());
|
||||
}
|
||||
}
|
||||
|
||||
if !display.displayed_windows.is_empty() || !process_names.is_empty() {
|
||||
anyhow::bail!(
|
||||
"Display is not empty. Attached processes: {}",
|
||||
process_names.join(", ")
|
||||
);
|
||||
}
|
||||
|
||||
self.manager.cleanup_clients();
|
||||
|
||||
for client in &self.manager.clients {
|
||||
if client.display_handle == handle {
|
||||
// This shouldn't happen, but make sure we are all set to destroy this display
|
||||
anyhow::bail!("Wayland client still exists");
|
||||
}
|
||||
}
|
||||
|
||||
self.displays.remove(&handle);
|
||||
|
||||
self.signals.send(WayVRSignal::BroadcastStateChanged(
|
||||
packet_server::WvrStateChanged::DisplayRemoved,
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_or_create_dashboard_display(
|
||||
&mut self,
|
||||
width: u16,
|
||||
height: u16,
|
||||
name: &str,
|
||||
) -> anyhow::Result<(bool /* newly created? */, display::DisplayHandle)> {
|
||||
if let Some(handle) = &self.dashboard_display {
|
||||
// ensure it still exists
|
||||
if self.displays.get(handle).is_some() {
|
||||
return Ok((false, *handle));
|
||||
}
|
||||
}
|
||||
|
||||
let new_disp = self.create_display(width, height, name, false)?;
|
||||
self.dashboard_display = Some(new_disp);
|
||||
|
||||
Ok((true, new_disp))
|
||||
}
|
||||
|
||||
// Check if process with given arguments already exists
|
||||
pub fn process_query(
|
||||
&self,
|
||||
display_handle: display::DisplayHandle,
|
||||
exec_path: &str,
|
||||
args: &[&str],
|
||||
_env: &[(&str, &str)],
|
||||
@@ -650,10 +428,7 @@ impl WayVRState {
|
||||
if let Some(cell) = &cell
|
||||
&& let process::Process::Managed(process) = &cell.obj
|
||||
{
|
||||
if process.display_handle != display_handle
|
||||
|| process.exec_path != exec_path
|
||||
|| process.args != args
|
||||
{
|
||||
if process.exec_path != exec_path || process.args != args {
|
||||
continue;
|
||||
}
|
||||
return Some(process::ProcessVec::get_handle(cell, idx));
|
||||
@@ -663,40 +438,39 @@ impl WayVRState {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn add_external_process(
|
||||
&mut self,
|
||||
display_handle: display::DisplayHandle,
|
||||
pid: u32,
|
||||
) -> process::ProcessHandle {
|
||||
pub fn add_external_process(&mut self, pid: u32) -> process::ProcessHandle {
|
||||
self.processes
|
||||
.add(process::Process::External(process::ExternalProcess {
|
||||
pid,
|
||||
display_handle,
|
||||
}))
|
||||
.add(process::Process::External(process::ExternalProcess { pid }))
|
||||
}
|
||||
|
||||
pub fn spawn_process(
|
||||
&mut self,
|
||||
display_handle: display::DisplayHandle,
|
||||
exec_path: &str,
|
||||
args: &[&str],
|
||||
env: &[(&str, &str)],
|
||||
working_dir: Option<&str>,
|
||||
userdata: HashMap<String, String>,
|
||||
) -> anyhow::Result<process::ProcessHandle> {
|
||||
let display = self
|
||||
.displays
|
||||
.get_mut(&display_handle)
|
||||
.context(STR_INVALID_HANDLE_DISP)?;
|
||||
let auth_key = generate_auth_key();
|
||||
|
||||
let res = display.spawn_process(exec_path, args, env, working_dir)?;
|
||||
let mut cmd = std::process::Command::new(exec_path);
|
||||
self.configure_env(&mut cmd, auth_key.as_str());
|
||||
cmd.args(args);
|
||||
if let Some(working_dir) = working_dir {
|
||||
cmd.current_dir(working_dir);
|
||||
}
|
||||
|
||||
for e in env {
|
||||
cmd.env(e.0, e.1);
|
||||
}
|
||||
|
||||
let child = cmd.spawn().context("Failed to spawn child process")?;
|
||||
|
||||
let handle = self
|
||||
.processes
|
||||
.add(process::Process::Managed(process::WayVRProcess {
|
||||
auth_key: res.auth_key,
|
||||
child: res.child,
|
||||
display_handle,
|
||||
auth_key,
|
||||
child,
|
||||
exec_path: String::from(exec_path),
|
||||
userdata,
|
||||
args: args.iter().map(|x| String::from(*x)).collect(),
|
||||
@@ -713,6 +487,25 @@ impl WayVRState {
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn configure_env(&self, cmd: &mut std::process::Command, auth_key: &str) {
|
||||
cmd.env_remove("DISPLAY"); // Goodbye X11
|
||||
cmd.env(
|
||||
"WAYLAND_DISPLAY",
|
||||
self.manager.wayland_env.display_num_string(),
|
||||
);
|
||||
cmd.env("WAYVR_DISPLAY_AUTH", auth_key);
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_auth_key() -> String {
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
uuid.to_string()
|
||||
}
|
||||
|
||||
pub struct SpawnProcessResult {
|
||||
pub auth_key: String,
|
||||
pub child: std::process::Child,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
@@ -733,3 +526,34 @@ pub enum WayVRAction {
|
||||
},
|
||||
ToggleDashboard,
|
||||
}
|
||||
|
||||
struct SurfaceBufWithImageContainer {
|
||||
inner: RefCell<SurfaceBufWithImage>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SurfaceBufWithImage {
|
||||
buffer: wl_buffer::WlBuffer,
|
||||
pub image: Arc<ImageView>,
|
||||
pub transform: Transform,
|
||||
pub scale: i32,
|
||||
}
|
||||
|
||||
impl SurfaceBufWithImage {
|
||||
fn apply_to_surface(self, surface_data: &SurfaceData) {
|
||||
let container = surface_data.data_map.get_or_insert(|| unsafe {
|
||||
SurfaceBufWithImageContainer {
|
||||
inner: RefCell::new(MaybeUninit::uninit().assume_init()),
|
||||
}
|
||||
});
|
||||
|
||||
container.inner.replace(self);
|
||||
}
|
||||
|
||||
pub fn get_from_surface(surface_data: &SurfaceData) -> Option<Self> {
|
||||
surface_data
|
||||
.data_map
|
||||
.get::<SurfaceBufWithImageContainer>()
|
||||
.map(|x| x.inner.borrow().clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,11 @@ use wayvr_ipc::packet_server;
|
||||
|
||||
use crate::gen_id;
|
||||
|
||||
use super::display;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct WayVRProcess {
|
||||
pub auth_key: String,
|
||||
pub child: std::process::Child,
|
||||
pub display_handle: display::DisplayHandle,
|
||||
|
||||
pub exec_path: String,
|
||||
pub args: Vec<String>,
|
||||
@@ -24,7 +21,6 @@ pub struct WayVRProcess {
|
||||
#[derive(Debug)]
|
||||
pub struct ExternalProcess {
|
||||
pub pid: u32,
|
||||
pub display_handle: display::DisplayHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -34,13 +30,6 @@ pub enum Process {
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub const fn display_handle(&self) -> display::DisplayHandle {
|
||||
match self {
|
||||
Self::Managed(p) => p.display_handle,
|
||||
Self::External(p) => p.display_handle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
match self {
|
||||
Self::Managed(p) => p.is_running(),
|
||||
@@ -67,13 +56,11 @@ impl Process {
|
||||
Self::Managed(p) => packet_server::WvrProcess {
|
||||
name: p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
userdata: p.userdata.clone(),
|
||||
display_handle: p.display_handle.as_packet(),
|
||||
handle: handle.as_packet(),
|
||||
},
|
||||
Self::External(p) => packet_server::WvrProcess {
|
||||
name: p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
userdata: HashMap::default(),
|
||||
display_handle: p.display_handle.as_packet(),
|
||||
handle: handle.as_packet(),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
use super::egl_data;
|
||||
use smithay::backend::{egl as smithay_egl, renderer::gles::ffi};
|
||||
|
||||
pub fn get_egl_display(data: &egl_data::EGLData) -> anyhow::Result<smithay_egl::EGLDisplay> {
|
||||
Ok(unsafe { smithay_egl::EGLDisplay::from_raw(data.display.as_ptr(), data.config.as_ptr())? })
|
||||
}
|
||||
|
||||
pub fn get_egl_context(
|
||||
data: &egl_data::EGLData,
|
||||
display: &smithay_egl::EGLDisplay,
|
||||
) -> anyhow::Result<smithay_egl::EGLContext> {
|
||||
let display_ptr = display.get_display_handle().handle;
|
||||
debug_assert!(std::ptr::eq(display_ptr, data.display.as_ptr()));
|
||||
let config_ptr = data.config.as_ptr();
|
||||
let context_ptr = data.context.as_ptr();
|
||||
Ok(unsafe { smithay_egl::EGLContext::from_raw(display_ptr, config_ptr, context_ptr)? })
|
||||
}
|
||||
|
||||
pub fn create_framebuffer_texture(
|
||||
gl: &ffi::Gles2,
|
||||
width: u32,
|
||||
height: u32,
|
||||
tex_format: u32,
|
||||
internal_format: u32,
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
let mut tex = 0;
|
||||
gl.GenTextures(1, &raw mut tex);
|
||||
gl.BindTexture(ffi::TEXTURE_2D, tex);
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_MIN_FILTER,
|
||||
ffi::NEAREST as i32,
|
||||
);
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_MAG_FILTER,
|
||||
ffi::NEAREST as i32,
|
||||
);
|
||||
gl.TexImage2D(
|
||||
ffi::TEXTURE_2D,
|
||||
0,
|
||||
internal_format as i32,
|
||||
width as i32,
|
||||
height as i32,
|
||||
0,
|
||||
tex_format,
|
||||
ffi::UNSIGNED_BYTE,
|
||||
std::ptr::null(),
|
||||
);
|
||||
gl.BindTexture(ffi::TEXTURE_2D, 0);
|
||||
tex
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,36 @@
|
||||
use smithay::wayland::shell::xdg::ToplevelSurface;
|
||||
use smithay::{
|
||||
input,
|
||||
utils::{Logical, Point},
|
||||
wayland::shell::xdg::ToplevelSurface,
|
||||
};
|
||||
use wayvr_ipc::packet_server;
|
||||
|
||||
use crate::gen_id;
|
||||
|
||||
use super::display;
|
||||
use crate::{
|
||||
backend::wayvr::{client::WayVRCompositor, process},
|
||||
gen_id,
|
||||
subsystem::hid::WheelDelta,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Window {
|
||||
pub pos_x: i32,
|
||||
pub pos_y: i32,
|
||||
pub size_x: u32,
|
||||
pub size_y: u32,
|
||||
pub visible: bool,
|
||||
pub toplevel: ToplevelSurface,
|
||||
pub display_handle: display::DisplayHandle,
|
||||
pub process: process::ProcessHandle,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(display_handle: display::DisplayHandle, toplevel: &ToplevelSurface) -> Self {
|
||||
fn new(toplevel: &ToplevelSurface, process: process::ProcessHandle) -> Self {
|
||||
Self {
|
||||
pos_x: 0,
|
||||
pos_y: 0,
|
||||
size_x: 0,
|
||||
size_y: 0,
|
||||
visible: true,
|
||||
toplevel: toplevel.clone(),
|
||||
display_handle,
|
||||
process,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn set_pos(&mut self, pos_x: i32, pos_y: i32) {
|
||||
self.pos_x = pos_x;
|
||||
self.pos_y = pos_y;
|
||||
}
|
||||
|
||||
pub fn set_size(&mut self, size_x: u32, size_y: u32) {
|
||||
self.toplevel.with_pending_state(|state| {
|
||||
//state.bounds = Some((size_x as i32, size_y as i32).into());
|
||||
@@ -44,6 +41,86 @@ impl Window {
|
||||
self.size_x = size_x;
|
||||
self.size_y = size_y;
|
||||
}
|
||||
|
||||
pub fn send_mouse_move(&self, manager: &mut WayVRCompositor, x: u32, y: u32) {
|
||||
let surf = self.toplevel.wl_surface().clone();
|
||||
let point = Point::<f64, Logical>::from((f64::from(x as i32), f64::from(y as i32)));
|
||||
|
||||
manager.seat_pointer.motion(
|
||||
&mut manager.state,
|
||||
Some((surf, Point::from((0.0, 0.0)))),
|
||||
&input::pointer::MotionEvent {
|
||||
serial: manager.serial_counter.next_serial(),
|
||||
time: 0,
|
||||
location: point,
|
||||
},
|
||||
);
|
||||
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
const fn get_mouse_index_number(index: super::MouseIndex) -> u32 {
|
||||
match index {
|
||||
super::MouseIndex::Left => 0x110, /* BTN_LEFT */
|
||||
super::MouseIndex::Center => 0x112, /* BTN_MIDDLE */
|
||||
super::MouseIndex::Right => 0x111, /* BTN_RIGHT */
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_mouse_down(&mut self, manager: &mut WayVRCompositor, index: super::MouseIndex) {
|
||||
let surf = self.toplevel.wl_surface().clone();
|
||||
|
||||
// Change keyboard focus to pressed window
|
||||
manager.seat_keyboard.set_focus(
|
||||
&mut manager.state,
|
||||
Some(surf),
|
||||
manager.serial_counter.next_serial(),
|
||||
);
|
||||
|
||||
manager.seat_pointer.button(
|
||||
&mut manager.state,
|
||||
&input::pointer::ButtonEvent {
|
||||
button: Self::get_mouse_index_number(index),
|
||||
serial: manager.serial_counter.next_serial(),
|
||||
time: 0,
|
||||
state: smithay::backend::input::ButtonState::Pressed,
|
||||
},
|
||||
);
|
||||
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
pub fn send_mouse_up(manager: &mut WayVRCompositor, index: super::MouseIndex) {
|
||||
manager.seat_pointer.button(
|
||||
&mut manager.state,
|
||||
&input::pointer::ButtonEvent {
|
||||
button: Self::get_mouse_index_number(index),
|
||||
serial: manager.serial_counter.next_serial(),
|
||||
time: 0,
|
||||
state: smithay::backend::input::ButtonState::Released,
|
||||
},
|
||||
);
|
||||
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
pub fn send_mouse_scroll(manager: &mut WayVRCompositor, delta: WheelDelta) {
|
||||
manager.seat_pointer.axis(
|
||||
&mut manager.state,
|
||||
input::pointer::AxisFrame {
|
||||
source: None,
|
||||
relative_direction: (
|
||||
smithay::backend::input::AxisRelativeDirection::Identical,
|
||||
smithay::backend::input::AxisRelativeDirection::Identical,
|
||||
),
|
||||
time: 0,
|
||||
axis: (f64::from(delta.x), f64::from(-delta.y)),
|
||||
v120: Some((0, (delta.y * -64.0) as i32)),
|
||||
stop: (false, false),
|
||||
},
|
||||
);
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -72,10 +149,10 @@ impl WindowManager {
|
||||
|
||||
pub fn create_window(
|
||||
&mut self,
|
||||
display_handle: display::DisplayHandle,
|
||||
toplevel: &ToplevelSurface,
|
||||
process: process::ProcessHandle,
|
||||
) -> WindowHandle {
|
||||
self.windows.add(Window::new(display_handle, toplevel))
|
||||
self.windows.add(Window::new(toplevel, process))
|
||||
}
|
||||
|
||||
pub fn remove_window(&mut self, window_handle: WindowHandle) {
|
||||
|
||||
Reference in New Issue
Block a user