new workspace
This commit is contained in:
428
wlx-capture/src/wayland.rs
Normal file
428
wlx-capture/src/wayland.rs
Normal file
@@ -0,0 +1,428 @@
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use idmap::IdMap;
|
||||
use log::debug;
|
||||
|
||||
use smithay_client_toolkit::reexports::{
|
||||
protocols::xdg::xdg_output::zv1::client::{
|
||||
zxdg_output_manager_v1::ZxdgOutputManagerV1,
|
||||
zxdg_output_v1::{self, ZxdgOutputV1},
|
||||
},
|
||||
protocols_wlr::{
|
||||
export_dmabuf::v1::client::zwlr_export_dmabuf_manager_v1::ZwlrExportDmabufManagerV1,
|
||||
screencopy::v1::client::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1,
|
||||
},
|
||||
};
|
||||
|
||||
pub use wayland_client;
|
||||
use wayland_client::{
|
||||
backend::WaylandError,
|
||||
globals::{registry_queue_init, GlobalList, GlobalListContents},
|
||||
protocol::{
|
||||
wl_output::{self, Transform, WlOutput},
|
||||
wl_registry::{self, WlRegistry},
|
||||
wl_seat::WlSeat,
|
||||
wl_shm::WlShm,
|
||||
},
|
||||
Connection, Dispatch, EventQueue, Proxy, QueueHandle,
|
||||
};
|
||||
|
||||
pub enum OutputChangeEvent {
|
||||
/// New output has been created.
|
||||
Create(u32),
|
||||
/// Logical position or size has changed, but no changes required in terms of rendering.
|
||||
Logical(u32),
|
||||
/// Resolution or transform has changed, textures need to be recreated.
|
||||
Physical(u32),
|
||||
/// Output has been destroyed.
|
||||
Destroy(u32),
|
||||
}
|
||||
|
||||
pub struct WlxOutput {
|
||||
pub wl_output: WlOutput,
|
||||
pub id: u32,
|
||||
pub name: Arc<str>,
|
||||
pub make: Arc<str>,
|
||||
pub model: Arc<str>,
|
||||
pub size: (i32, i32),
|
||||
pub logical_pos: (i32, i32),
|
||||
pub logical_size: (i32, i32),
|
||||
pub transform: Transform,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
pub struct WlxClient {
|
||||
pub connection: Arc<Connection>,
|
||||
pub xdg_output_mgr: ZxdgOutputManagerV1,
|
||||
pub maybe_wlr_dmabuf_mgr: Option<ZwlrExportDmabufManagerV1>,
|
||||
pub maybe_wlr_screencopy_mgr: Option<ZwlrScreencopyManagerV1>,
|
||||
pub wl_seat: WlSeat,
|
||||
pub wl_shm: WlShm,
|
||||
pub outputs: IdMap<u32, WlxOutput>,
|
||||
pub queue: Arc<Mutex<EventQueue<Self>>>,
|
||||
pub globals: GlobalList,
|
||||
pub queue_handle: QueueHandle<Self>,
|
||||
default_output_name: Arc<str>,
|
||||
events: VecDeque<OutputChangeEvent>,
|
||||
}
|
||||
|
||||
impl WlxClient {
|
||||
pub fn new() -> Option<Self> {
|
||||
let connection = Connection::connect_to_env()
|
||||
.inspect_err(|e| log::info!("Wayland connection: {e:?}"))
|
||||
.ok()?;
|
||||
let (globals, queue) = registry_queue_init::<Self>(&connection)
|
||||
.inspect_err(|e| log::info!("Wayland queue init: {e:?}"))
|
||||
.ok()?;
|
||||
let qh = queue.handle();
|
||||
|
||||
let mut state = Self {
|
||||
connection: Arc::new(connection),
|
||||
xdg_output_mgr: globals
|
||||
.bind(&qh, 2..=3, ())
|
||||
.expect(ZxdgOutputManagerV1::interface().name),
|
||||
wl_seat: globals
|
||||
.bind(&qh, 4..=9, ())
|
||||
.expect(WlSeat::interface().name),
|
||||
wl_shm: globals.bind(&qh, 1..=1, ()).expect(WlShm::interface().name),
|
||||
maybe_wlr_dmabuf_mgr: globals.bind(&qh, 1..=1, ()).ok(),
|
||||
maybe_wlr_screencopy_mgr: globals.bind(&qh, 2..=2, ()).ok(),
|
||||
outputs: IdMap::new(),
|
||||
queue: Arc::new(Mutex::new(queue)),
|
||||
globals,
|
||||
queue_handle: qh,
|
||||
default_output_name: "Unknown".into(),
|
||||
events: VecDeque::new(),
|
||||
};
|
||||
|
||||
for o in state.globals.contents().clone_list().iter() {
|
||||
if o.interface == WlOutput::interface().name {
|
||||
state.add_output(o.name, o.version);
|
||||
}
|
||||
}
|
||||
|
||||
state.dispatch();
|
||||
|
||||
Some(state)
|
||||
}
|
||||
|
||||
fn add_output(&mut self, name: u32, version: u32) {
|
||||
let wl_output: WlOutput =
|
||||
self.globals
|
||||
.registry()
|
||||
.bind(name, version, &self.queue_handle, name);
|
||||
self.xdg_output_mgr
|
||||
.get_xdg_output(&wl_output, &self.queue_handle, name);
|
||||
let output = WlxOutput {
|
||||
wl_output,
|
||||
id: name,
|
||||
name: self.default_output_name.clone(),
|
||||
make: self.default_output_name.clone(),
|
||||
model: self.default_output_name.clone(),
|
||||
size: (0, 0),
|
||||
logical_pos: (0, 0),
|
||||
logical_size: (0, 0),
|
||||
transform: Transform::Normal,
|
||||
done: false,
|
||||
};
|
||||
|
||||
self.outputs.insert(name, output);
|
||||
}
|
||||
|
||||
pub fn get_desktop_origin(&self) -> (i32, i32) {
|
||||
let mut origin = (i32::MAX, i32::MAX);
|
||||
for output in self.outputs.values() {
|
||||
origin.0 = origin.0.min(output.logical_pos.0);
|
||||
origin.1 = origin.1.min(output.logical_pos.1);
|
||||
}
|
||||
origin
|
||||
}
|
||||
|
||||
/// Get the logical width and height of the desktop.
|
||||
pub fn get_desktop_extent(&self) -> (i32, i32) {
|
||||
let mut extent = (0, 0);
|
||||
for output in self.outputs.values() {
|
||||
extent.0 = extent.0.max(output.logical_pos.0 + output.logical_size.0);
|
||||
extent.1 = extent.1.max(output.logical_pos.1 + output.logical_size.1);
|
||||
}
|
||||
let origin = self.get_desktop_origin();
|
||||
(extent.0 - origin.0, extent.1 - origin.1)
|
||||
}
|
||||
|
||||
pub fn iter_events(&mut self) -> impl Iterator<Item = OutputChangeEvent> + '_ {
|
||||
self.events.drain(..)
|
||||
}
|
||||
|
||||
/// Dispatch pending events and block until finished.
|
||||
pub fn dispatch(&mut self) {
|
||||
if let Ok(mut queue_mut) = self.queue.clone().lock() {
|
||||
let _ = queue_mut.blocking_dispatch(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch pending events without blocking.
|
||||
pub fn dispatch_pending(&mut self) {
|
||||
if let Ok(mut queue_mut) = self.queue.clone().lock() {
|
||||
if let Some(reader) = queue_mut.prepare_read() {
|
||||
match reader.read() {
|
||||
Ok(n) => match queue_mut.dispatch_pending(self) {
|
||||
Ok(n2) => {
|
||||
log::debug!("Read {}, dispatched {} pending events", n, n2);
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("Error while dispatching {} pending events: {:?}", n, err);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
if let WaylandError::Io(ref e) = err {
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
return;
|
||||
}
|
||||
}
|
||||
log::warn!("Error while reading from event queue: {:?}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = queue_mut.dispatch_pending(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wl_transform_to_frame_transform(transform: Transform) -> crate::frame::Transform {
|
||||
match transform {
|
||||
Transform::Normal => crate::frame::Transform::Normal,
|
||||
Transform::_90 => crate::frame::Transform::Rotated90,
|
||||
Transform::_180 => crate::frame::Transform::Rotated180,
|
||||
Transform::_270 => crate::frame::Transform::Rotated270,
|
||||
Transform::Flipped => crate::frame::Transform::Flipped,
|
||||
Transform::Flipped90 => crate::frame::Transform::Flipped90,
|
||||
Transform::Flipped180 => crate::frame::Transform::Flipped180,
|
||||
Transform::Flipped270 => crate::frame::Transform::Flipped270,
|
||||
_ => crate::frame::Transform::Undefined,
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZxdgOutputV1, u32> for WlxClient {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_proxy: &ZxdgOutputV1,
|
||||
event: <ZxdgOutputV1 as Proxy>::Event,
|
||||
data: &u32,
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
fn finalize_output(output: &mut WlxOutput) {
|
||||
if output.logical_size.0 < 0 {
|
||||
output.logical_pos.0 += output.logical_size.0;
|
||||
output.logical_size.0 *= -1;
|
||||
}
|
||||
if output.logical_size.1 < 0 {
|
||||
output.logical_pos.1 += output.logical_size.1;
|
||||
output.logical_size.1 *= -1;
|
||||
}
|
||||
if !output.done {
|
||||
output.done = true;
|
||||
debug!(
|
||||
"Discovered WlOutput {}; Size: {:?}; Logical Size: {:?}; Pos: {:?}",
|
||||
output.name, output.size, output.logical_size, output.logical_pos
|
||||
);
|
||||
}
|
||||
}
|
||||
match event {
|
||||
zxdg_output_v1::Event::Name { name } => {
|
||||
if let Some(output) = state.outputs.get_mut(*data) {
|
||||
output.name = name.into();
|
||||
}
|
||||
}
|
||||
zxdg_output_v1::Event::LogicalPosition { x, y } => {
|
||||
if let Some(output) = state.outputs.get_mut(*data) {
|
||||
output.logical_pos = (x, y);
|
||||
let was_done = output.done;
|
||||
if output.logical_size != (0, 0) {
|
||||
finalize_output(output);
|
||||
}
|
||||
if was_done {
|
||||
log::info!(
|
||||
"{}: Logical pos changed to {:?}",
|
||||
output.name,
|
||||
output.logical_pos,
|
||||
);
|
||||
state.events.push_back(OutputChangeEvent::Logical(*data));
|
||||
} else {
|
||||
state.events.push_back(OutputChangeEvent::Create(*data));
|
||||
}
|
||||
}
|
||||
}
|
||||
zxdg_output_v1::Event::LogicalSize { width, height } => {
|
||||
if let Some(output) = state.outputs.get_mut(*data) {
|
||||
output.logical_size = (width, height);
|
||||
let was_done = output.done;
|
||||
if output.logical_pos != (0, 0) {
|
||||
finalize_output(output);
|
||||
}
|
||||
if was_done {
|
||||
log::info!(
|
||||
"{}: Logical size changed to {:?}",
|
||||
output.name,
|
||||
output.logical_size,
|
||||
);
|
||||
state.events.push_back(OutputChangeEvent::Logical(*data));
|
||||
} else {
|
||||
state.events.push_back(OutputChangeEvent::Create(*data));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlOutput, u32> for WlxClient {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_proxy: &WlOutput,
|
||||
event: <WlOutput as Proxy>::Event,
|
||||
data: &u32,
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
wl_output::Event::Mode { width, height, .. } => {
|
||||
if let Some(output) = state.outputs.get_mut(*data) {
|
||||
output.size = (width, height);
|
||||
if output.done {
|
||||
log::info!(
|
||||
"{}: Resolution changed {:?} -> {:?}",
|
||||
output.name,
|
||||
output.size,
|
||||
(width, height)
|
||||
);
|
||||
state.events.push_back(OutputChangeEvent::Physical(*data));
|
||||
}
|
||||
}
|
||||
}
|
||||
wl_output::Event::Geometry {
|
||||
make,
|
||||
model,
|
||||
transform,
|
||||
..
|
||||
} => {
|
||||
if let Some(output) = state.outputs.get_mut(*data) {
|
||||
let transform = transform.into_result().unwrap_or(Transform::Normal);
|
||||
let old_transform = output.transform;
|
||||
output.transform = transform;
|
||||
if output.done && old_transform != transform {
|
||||
log::info!(
|
||||
"{}: Transform changed {:?} -> {:?}",
|
||||
output.name,
|
||||
output.transform,
|
||||
transform
|
||||
);
|
||||
state.events.push_back(OutputChangeEvent::Physical(*data));
|
||||
state.events.push_back(OutputChangeEvent::Logical(*data));
|
||||
}
|
||||
output.make = make.into();
|
||||
output.model = model.into();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlRegistry, GlobalListContents> for WlxClient {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_proxy: &WlRegistry,
|
||||
event: <WlRegistry as Proxy>::Event,
|
||||
_data: &GlobalListContents,
|
||||
conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
wl_registry::Event::Global {
|
||||
name,
|
||||
interface,
|
||||
version,
|
||||
} => {
|
||||
if interface == WlOutput::interface().name {
|
||||
state.add_output(name, version);
|
||||
let _ = conn.roundtrip();
|
||||
}
|
||||
}
|
||||
wl_registry::Event::GlobalRemove { name } => {
|
||||
if let Some(output) = state.outputs.remove(name) {
|
||||
log::info!("{}: Device removed", output.name);
|
||||
state.events.push_back(OutputChangeEvent::Destroy(name));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Plumbing below
|
||||
|
||||
impl Dispatch<ZxdgOutputManagerV1, ()> for WlxClient {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &ZxdgOutputManagerV1,
|
||||
_event: <ZxdgOutputManagerV1 as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZwlrExportDmabufManagerV1, ()> for WlxClient {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &ZwlrExportDmabufManagerV1,
|
||||
_event: <ZwlrExportDmabufManagerV1 as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZwlrScreencopyManagerV1, ()> for WlxClient {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &ZwlrScreencopyManagerV1,
|
||||
_event: <ZwlrScreencopyManagerV1 as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlSeat, ()> for WlxClient {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &WlSeat,
|
||||
_event: <WlSeat as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlShm, ()> for WlxClient {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &WlShm,
|
||||
_event: <WlShm as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user