1
0
Fork 0
forked from wry/wry

Merge pull request #185 from mahkoh/jorth/output-removal

wayland: allow binding to removed outputs
This commit is contained in:
mahkoh 2024-04-25 20:10:57 +02:00 committed by GitHub
commit 5b851efe0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 212 additions and 70 deletions

View file

@ -127,6 +127,7 @@ pub trait Global: GlobalBase {
pub struct Globals {
next_name: NumCell<u32>,
registry: CopyHashMap<GlobalName, Rc<dyn Global>>,
removed: CopyHashMap<GlobalName, Rc<dyn Global>>,
pub outputs: CopyHashMap<GlobalName, Rc<WlOutputGlobal>>,
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
}
@ -136,6 +137,7 @@ impl Globals {
let slf = Self {
next_name: NumCell::new(1),
registry: CopyHashMap::new(),
removed: CopyHashMap::new(),
outputs: Default::default(),
seats: Default::default(),
};
@ -241,9 +243,17 @@ impl Globals {
Ok(global)
}
pub fn remove<T: WaylandGlobal>(&self, state: &State, global: &T) -> Result<(), GlobalsError> {
pub fn remove<T: RemovableWaylandGlobal>(
&self,
state: &State,
global: &T,
) -> Result<(), GlobalsError> {
let _global = self.take(global.name(), true)?;
global.remove(self);
let replacement = global.create_replacement();
assert_eq!(global.name(), replacement.name());
assert_eq!(global.interface().0, replacement.interface().0);
self.removed.set(global.name(), replacement);
self.broadcast(state, global.required_caps(), global.xwayland_only(), |r| {
r.send_global_remove(global.name())
});
@ -295,7 +305,10 @@ impl Globals {
let res = if remove {
self.registry.remove(&name)
} else {
self.registry.get(&name)
match self.registry.get(&name) {
Some(res) => Some(res),
_ => self.removed.get(&name),
}
};
match res {
Some(g) => Ok(g),
@ -330,3 +343,7 @@ pub trait WaylandGlobal: Global + 'static {
let _ = globals;
}
}
pub trait RemovableWaylandGlobal: WaylandGlobal {
fn create_replacement(&self) -> Rc<dyn Global>;
}

View file

@ -57,20 +57,20 @@ impl ExtSessionLockV1RequestHandler for ExtSessionLockV1 {
surface,
tracker: Default::default(),
serial: Default::default(),
output: output.global.node.get(),
output: output.global.clone(),
seat_state: Default::default(),
version: self.version,
});
track!(new.client, new);
new.install()?;
self.client.add_client_obj(&new)?;
if !output.global.destroyed.get() && !self.finished.get() {
if let Some(node) = output.global.node.get() {
if !self.finished.get() {
if let Some(node) = output.global.node() {
if node.lock_surface.is_some() {
return Err(ExtSessionLockV1Error::OutputAlreadyLocked);
}
node.set_lock_surface(Some(new.clone()));
let pos = output.global.pos.get();
let pos = node.global.pos.get();
new.change_extents(pos);
self.client.state.tree_changed();
}

View file

@ -21,7 +21,7 @@ use {
leaks::Tracker,
object::{Object, Version},
screenshoter::take_screenshot,
utils::{clonecell::CloneCell, errorfmt::ErrorFmt},
utils::errorfmt::ErrorFmt,
wire::{jay_compositor::*, JayCompositorId, JayScreenshotId},
},
bstr::ByteSlice,
@ -264,12 +264,12 @@ impl JayCompositorRequestHandler for JayCompositor {
let jo = Rc::new(JayOutput {
id: req.id,
client: self.client.clone(),
output: CloneCell::new(output.global.node.get()),
output: output.global.clone(),
tracker: Default::default(),
});
track!(self.client, jo);
self.client.add_client_obj(&jo)?;
if let Some(node) = jo.output.get() {
if let Some(node) = jo.output.node() {
node.jay_outputs.set((self.client.id, req.id), jo.clone());
jo.send_linear_id();
} else {

View file

@ -1,10 +1,9 @@
use {
crate::{
client::{Client, ClientError},
ifs::wl_output::OutputGlobalOpt,
leaks::Tracker,
object::{Object, Version},
tree::OutputNode,
utils::clonecell::CloneCell,
wire::{jay_output::*, JayOutputId},
},
std::rc::Rc,
@ -14,7 +13,7 @@ use {
pub struct JayOutput {
pub id: JayOutputId,
pub client: Rc<Client>,
pub output: CloneCell<Option<Rc<OutputNode>>>,
pub output: Rc<OutputGlobalOpt>,
pub tracker: Tracker<Self>,
}
@ -24,7 +23,7 @@ impl JayOutput {
}
pub fn send_linear_id(&self) {
if let Some(output) = self.output.get() {
if let Some(output) = self.output.node() {
self.client.event(LinearId {
self_id: self.id,
linear_id: output.id.raw(),
@ -33,7 +32,7 @@ impl JayOutput {
}
fn remove_from_node(&self) {
if let Some(output) = self.output.get() {
if let Some(output) = self.output.node() {
output.jay_outputs.remove(&(self.client.id, self.id));
}
}

View file

@ -519,7 +519,7 @@ impl JayScreencastRequestHandler for JayScreencast {
if let Some(new) = target {
match new {
PendingTarget::Output(o) => {
let Some(o) = o.output.get() else {
let Some(o) = o.output.node() else {
self.do_destroy();
return Ok(());
};

View file

@ -1,3 +1,5 @@
mod removed_output;
use {
crate::{
backend,
@ -55,13 +57,34 @@ pub struct WlOutputGlobal {
pub output_id: Rc<OutputId>,
pub mode: Cell<backend::Mode>,
pub modes: Vec<backend::Mode>,
pub node: CloneCell<Option<Rc<OutputNode>>>,
pub width_mm: i32,
pub height_mm: i32,
pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>,
pub destroyed: Cell<bool>,
pub legacy_scale: Cell<u32>,
pub persistent: Rc<PersistentOutputState>,
pub opt: Rc<OutputGlobalOpt>,
}
#[derive(Default)]
pub struct OutputGlobalOpt {
pub global: CloneCell<Option<Rc<WlOutputGlobal>>>,
pub node: CloneCell<Option<Rc<OutputNode>>>,
}
impl OutputGlobalOpt {
pub fn get(&self) -> Option<Rc<WlOutputGlobal>> {
self.global.get()
}
pub fn node(&self) -> Option<Rc<OutputNode>> {
self.node.get()
}
pub fn clear(&self) {
self.node.take();
self.global.take();
}
}
pub struct PersistentOutputState {
@ -80,7 +103,7 @@ pub struct OutputId {
impl WlOutputGlobal {
pub fn clear(&self) {
self.node.take();
self.opt.clear();
self.bindings.borrow_mut().clear();
}
@ -110,13 +133,13 @@ impl WlOutputGlobal {
output_id: output_id.clone(),
mode: Cell::new(*mode),
modes,
node: Default::default(),
width_mm,
height_mm,
bindings: Default::default(),
destroyed: Cell::new(false),
legacy_scale: Cell::new(scale.round_up()),
persistent: persistent_state.clone(),
opt: Default::default(),
}
}
@ -169,7 +192,7 @@ impl WlOutputGlobal {
version: Version,
) -> Result<(), WlOutputError> {
let obj = Rc::new(WlOutput {
global: self.clone(),
global: self.opt.clone(),
id,
xdg_outputs: Default::default(),
client: client.clone(),
@ -208,13 +231,15 @@ impl WlOutputGlobal {
global_base!(WlOutputGlobal, WlOutput, WlOutputError);
const OUTPUT_VERSION: u32 = 4;
impl Global for WlOutputGlobal {
fn singleton(&self) -> bool {
false
}
fn version(&self) -> u32 {
4
OUTPUT_VERSION
}
fn break_loops(&self) {
@ -225,7 +250,7 @@ impl Global for WlOutputGlobal {
dedicated_add_global!(WlOutputGlobal, outputs);
pub struct WlOutput {
pub global: Rc<WlOutputGlobal>,
pub global: Rc<OutputGlobalOpt>,
pub id: WlOutputId,
pub xdg_outputs: CopyHashMap<ZxdgOutputV1Id, Rc<ZxdgOutputV1>>,
client: Rc<Client>,
@ -239,23 +264,29 @@ pub const SEND_NAME_SINCE: Version = Version(4);
impl WlOutput {
fn send_geometry(&self) {
let pos = self.global.pos.get();
let Some(global) = self.global.get() else {
return;
};
let pos = global.pos.get();
let event = Geometry {
self_id: self.id,
x: pos.x1(),
y: pos.y1(),
physical_width: self.global.width_mm,
physical_height: self.global.height_mm,
physical_width: global.width_mm,
physical_height: global.height_mm,
subpixel: SP_UNKNOWN,
make: &self.global.output_id.manufacturer,
model: &self.global.output_id.model,
transform: self.global.persistent.transform.get().to_wl(),
make: &global.output_id.manufacturer,
model: &global.output_id.model,
transform: global.persistent.transform.get().to_wl(),
};
self.client.event(event);
}
fn send_mode(&self) {
let mode = self.global.mode.get();
let Some(global) = self.global.get() else {
return;
};
let mode = global.mode.get();
let event = Mode {
self_id: self.id,
flags: MODE_CURRENT,
@ -267,17 +298,23 @@ impl WlOutput {
}
fn send_scale(self: &Rc<Self>) {
let Some(global) = self.global.get() else {
return;
};
let event = Scale {
self_id: self.id,
factor: self.global.legacy_scale.get() as _,
factor: global.legacy_scale.get() as _,
};
self.client.event(event);
}
fn send_name(&self) {
let Some(global) = self.global.get() else {
return;
};
self.client.event(Name {
self_id: self.id,
name: &self.global.connector.name,
name: &global.connector.name,
});
}
@ -287,12 +324,15 @@ impl WlOutput {
}
fn remove_binding(&self) {
if let Entry::Occupied(mut e) = self.global.bindings.borrow_mut().entry(self.client.id) {
let Some(global) = self.global.get() else {
return;
};
if let Entry::Occupied(mut e) = global.bindings.borrow_mut().entry(self.client.id) {
e.get_mut().remove(&self.id);
if e.get().is_empty() {
e.remove();
}
}
};
}
}

View file

@ -0,0 +1,63 @@
use {
crate::{
client::{Client, ClientError},
globals::{Global, GlobalName, RemovableWaylandGlobal},
ifs::wl_output::{WlOutput, WlOutputGlobal, OUTPUT_VERSION},
object::Version,
wire::WlOutputId,
},
std::rc::Rc,
thiserror::Error,
};
struct RemovedOutputGlobal {
name: GlobalName,
}
impl RemovedOutputGlobal {
fn bind_(
self: Rc<Self>,
id: WlOutputId,
client: &Rc<Client>,
version: Version,
) -> Result<(), RemovedOutputError> {
let obj = Rc::new(WlOutput {
global: Default::default(),
id,
xdg_outputs: Default::default(),
client: client.clone(),
version,
tracker: Default::default(),
});
track!(client, obj);
client.add_client_obj(&obj)?;
Ok(())
}
}
global_base!(RemovedOutputGlobal, WlOutput, RemovedOutputError);
impl Global for RemovedOutputGlobal {
fn singleton(&self) -> bool {
false
}
fn version(&self) -> u32 {
OUTPUT_VERSION
}
}
simple_add_global!(RemovedOutputGlobal);
impl RemovableWaylandGlobal for WlOutputGlobal {
fn create_replacement(&self) -> Rc<dyn Global> {
Rc::new(RemovedOutputGlobal { name: self.name })
}
}
#[derive(Debug, Error)]
enum RemovedOutputError {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(RemovedOutputError, ClientError);

View file

@ -3,13 +3,14 @@ use {
client::{Client, ClientError},
fixed::Fixed,
ifs::{
wl_output::OutputGlobalOpt,
wl_seat::{NodeSeatState, WlSeatGlobal},
wl_surface::{SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError},
},
leaks::Tracker,
object::{Object, Version},
rect::Rect,
tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode},
tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor},
utils::numcell::NumCell,
wire::{ext_session_lock_surface_v1::*, ExtSessionLockSurfaceV1Id, WlSurfaceId},
},
@ -24,7 +25,7 @@ pub struct ExtSessionLockSurfaceV1 {
pub surface: Rc<WlSurface>,
pub tracker: Tracker<Self>,
pub serial: NumCell<u32>,
pub output: Option<Rc<OutputNode>>,
pub output: Rc<OutputGlobalOpt>,
pub seat_state: NodeSeatState,
pub version: Version,
}
@ -73,7 +74,7 @@ impl ExtSessionLockSurfaceV1RequestHandler for ExtSessionLockSurfaceV1 {
impl ExtSessionLockSurfaceV1 {
pub fn destroy_node(&self) {
if let Some(output) = &self.output {
if let Some(output) = &self.output.node() {
if let Some(ls) = output.lock_surface.get() {
if ls.node_id == self.node_id {
output.set_lock_surface(None);

View file

@ -321,7 +321,7 @@ impl XdgToplevelRequestHandler for XdgToplevel {
self.states.borrow_mut().insert(STATE_FULLSCREEN);
'set_fullscreen: {
let output = if req.output.is_some() {
match client.lookup(req.output)?.global.node.get() {
match client.lookup(req.output)?.global.node() {
Some(node) => node,
_ => {
log::error!("Output global has no node attached");

View file

@ -2,6 +2,7 @@ use {
crate::{
client::{Client, ClientError},
ifs::{
wl_output::OutputGlobalOpt,
wl_seat::NodeSeatState,
wl_surface::{PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError},
zwlr_layer_shell_v1::{ZwlrLayerShellV1, OVERLAY},
@ -10,7 +11,7 @@ use {
object::Object,
rect::Rect,
renderer::Renderer,
tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode},
tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor},
utils::{
bitflags::BitflagsExt, cell_ext::CellExt, linkedlist::LinkedNode, numcell::NumCell,
option_ext::OptionExt,
@ -43,7 +44,7 @@ pub struct ZwlrLayerSurfaceV1 {
pub shell: Rc<ZwlrLayerShellV1>,
pub client: Rc<Client>,
pub surface: Rc<WlSurface>,
pub output: Rc<OutputNode>,
pub output: Rc<OutputGlobalOpt>,
pub namespace: String,
pub tracker: Tracker<Self>,
output_pos: Cell<Rect>,
@ -96,7 +97,7 @@ impl ZwlrLayerSurfaceV1 {
id: ZwlrLayerSurfaceV1Id,
shell: &Rc<ZwlrLayerShellV1>,
surface: &Rc<WlSurface>,
output: &Rc<OutputNode>,
output: &Rc<OutputGlobalOpt>,
layer: u32,
namespace: &str,
) -> Self {
@ -131,7 +132,9 @@ impl ZwlrLayerSurfaceV1 {
return Err(ZwlrLayerSurfaceV1Error::AlreadyAttached(self.surface.id));
}
self.surface.ext.set(self.clone());
self.surface.set_output(&self.output);
if let Some(output) = self.output.node() {
self.surface.set_output(&output);
}
Ok(())
}
@ -241,6 +244,9 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
impl ZwlrLayerSurfaceV1 {
fn pre_commit(&self, pending: &mut PendingState) -> Result<(), ZwlrLayerSurfaceV1Error> {
let Some(global) = self.output.get() else {
return Ok(());
};
let pending = pending.layer_surface.get_or_insert_default_ext();
let mut send_configure = mem::replace(&mut pending.any, false);
if let Some(size) = pending.size.take() {
@ -269,14 +275,14 @@ impl ZwlrLayerSurfaceV1 {
return Err(ZwlrLayerSurfaceV1Error::WidthZero);
}
send_configure = true;
width = self.output.global.position().width();
width = global.position().width();
}
if height == 0 {
if !anchor.contains(TOP | BOTTOM) {
return Err(ZwlrLayerSurfaceV1Error::HeightZero);
}
send_configure = true;
height = self.output.global.position().height();
height = global.position().height();
}
self.size.set((width, height));
}
@ -300,12 +306,15 @@ impl ZwlrLayerSurfaceV1 {
}
pub fn compute_position(&self) {
let Some(global) = self.output.get() else {
return;
};
let (width, height) = self.size.get();
let mut anchor = self.anchor.get();
if anchor == 0 {
anchor = LEFT | RIGHT | TOP | BOTTOM;
}
let opos = self.output.global.pos.get();
let opos = global.pos.get();
let mut x1 = 0;
let mut y1 = 0;
if anchor.contains(LEFT) {
@ -349,6 +358,9 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
}
fn after_apply_commit(self: Rc<Self>) {
let Some(output) = self.output.node() else {
return;
};
let buffer_is_some = self.surface.buffer.is_some();
let was_mapped = self.mapped.get();
if self.mapped.get() {
@ -362,13 +374,13 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
}
}
} else if buffer_is_some {
let layer = &self.output.layers[self.layer.get() as usize];
let layer = &output.layers[self.layer.get() as usize];
self.link.set(Some(layer.add_last(self.clone())));
self.mapped.set(true);
self.compute_position();
}
if self.mapped.get() != was_mapped {
self.output.update_visible();
output.update_visible();
}
if self.mapped.get() {
match self.keyboard_interactivity.get() {

View file

@ -57,17 +57,17 @@ impl ZwlrLayerShellV1RequestHandler for ZwlrLayerShellV1 {
let surface = self.client.lookup(req.surface)?;
let output = 'get_output: {
if req.output.is_some() {
self.client.lookup(req.output)?.global.node.get().unwrap()
self.client.lookup(req.output)?.global.clone()
} else {
for seat in self.client.state.seat_queue.rev_iter() {
let output = seat.get_output();
if !output.is_dummy {
break 'get_output output;
break 'get_output output.global.opt.clone();
}
}
let outputs = self.client.state.outputs.lock();
if let Some(output) = outputs.values().next() {
break 'get_output output.node.clone();
break 'get_output output.node.global.opt.clone();
}
return Err(ZwlrLayerShellV1Error::NoOutputs);
}

View file

@ -4,7 +4,7 @@ use {
format::XRGB8888,
ifs::{
wl_buffer::{WlBuffer, WlBufferError, WlBufferStorage},
wl_output::WlOutputGlobal,
wl_output::OutputGlobalOpt,
},
leaks::Tracker,
object::{Object, Version},
@ -22,7 +22,7 @@ pub struct ZwlrScreencopyFrameV1 {
pub id: ZwlrScreencopyFrameV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub output: Rc<WlOutputGlobal>,
pub output: Rc<OutputGlobalOpt>,
pub rect: Rect,
pub overlay_cursor: bool,
pub used: Cell<bool>,
@ -46,14 +46,16 @@ impl ZwlrScreencopyFrameV1 {
}
pub fn send_damage(&self) {
let pos = self.output.pos.get();
self.client.event(Damage {
self_id: self.id,
x: 0,
y: 0,
width: pos.width() as _,
height: pos.height() as _,
});
if let Some(output) = self.output.get() {
let pos = output.pos.get();
self.client.event(Damage {
self_id: self.id,
x: 0,
y: 0,
width: pos.width() as _,
height: pos.height() as _,
});
}
}
pub fn send_buffer(&self) {
@ -95,7 +97,7 @@ impl ZwlrScreencopyFrameV1 {
if self.used.replace(true) {
return Err(ZwlrScreencopyFrameV1Error::AlreadyUsed);
}
let Some(node) = self.output.node.get() else {
let Some(node) = self.output.node() else {
self.send_failed();
return Ok(());
};
@ -114,7 +116,9 @@ impl ZwlrScreencopyFrameV1 {
}
self.buffer.set(Some(buffer));
if !with_damage {
self.output.connector.connector.damage();
if let Some(global) = self.output.get() {
global.connector.connector.damage();
}
}
self.with_damage.set(with_damage);
node.screencopies
@ -124,7 +128,7 @@ impl ZwlrScreencopyFrameV1 {
}
fn detach(&self) {
if let Some(node) = self.output.node.get() {
if let Some(node) = self.output.node() {
node.screencopies.remove(&(self.client.id, self.id));
node.screencast_changed();
}

View file

@ -105,10 +105,13 @@ impl ZwlrScreencopyManagerV1 {
region: Option<Rect>,
) -> Result<(), ZwlrScreencopyManagerV1Error> {
let output = self.client.lookup(output)?;
let mode = output.global.mode.get();
let Some(global) = output.global.get() else {
return Ok(());
};
let mode = global.mode.get();
let mut rect = Rect::new_sized(0, 0, mode.width, mode.height).unwrap();
if let Some(region) = region {
let scale = output.global.persistent.scale.get().to_f64();
let scale = global.persistent.scale.get().to_f64();
let x1 = (region.x1() as f64 * scale).round() as i32;
let y1 = (region.y1() as f64 * scale).round() as i32;
let x2 = (region.x2() as f64 * scale).round() as i32;

View file

@ -60,11 +60,14 @@ impl ZxdgOutputV1 {
}
pub fn send_updates(&self) {
let pos = self.output.global.position();
let Some(global) = self.output.global.get() else {
return;
};
let pos = global.position();
self.send_logical_position(pos.x1(), pos.y1());
self.send_logical_size(pos.width(), pos.height());
if self.version >= NAME_SINCE {
self.send_name(&self.output.global.connector.name);
self.send_name(&global.connector.name);
}
if self.version >= NO_DONE_SINCE {
if self.output.version >= SEND_DONE_SINCE {

View file

@ -22,7 +22,7 @@ use {
SyncFile,
},
gfx_apis::create_gfx_context,
globals::{Globals, GlobalsError, WaylandGlobal},
globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal},
ifs::{
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
ext_session_lock_v1::ExtSessionLockV1,
@ -511,7 +511,7 @@ impl State {
self.globals.add_global(self, global)
}
pub fn remove_global<T: WaylandGlobal>(&self, global: &T) -> Result<(), GlobalsError> {
pub fn remove_global<T: RemovableWaylandGlobal>(&self, global: &T) -> Result<(), GlobalsError> {
self.globals.remove(self, global)
}

View file

@ -158,7 +158,8 @@ impl ConnectorHandler {
node: on.clone(),
});
self.state.outputs.set(self.id, output_data);
global.node.set(Some(on.clone()));
global.opt.node.set(Some(on.clone()));
global.opt.global.set(Some(global.clone()));
let mut ws_to_move = VecDeque::new();
if self.state.outputs.len() == 1 {
let seats = self.state.globals.seats.lock();
@ -227,10 +228,9 @@ impl ConnectorHandler {
if let Some(config) = self.state.config.get() {
config.connector_disconnected(self.id);
}
global.node.set(None);
global.clear();
for (_, jo) in on.jay_outputs.lock().drain() {
jo.send_destroyed();
jo.output.take();
}
let screencasts: Vec<_> = on.screencasts.lock().values().cloned().collect();
for sc in screencasts {