Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wayland: allow binding to removed outputs #185

Merged
merged 2 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>,
}
Expand All @@ -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(),
};
Expand Down Expand Up @@ -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())
});
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -330,3 +343,7 @@ pub trait WaylandGlobal: Global + 'static {
let _ = globals;
}
}

pub trait RemovableWaylandGlobal: WaylandGlobal {
fn create_replacement(&self) -> Rc<dyn Global>;
}
8 changes: 4 additions & 4 deletions src/ifs/ext_session_lock_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
6 changes: 3 additions & 3 deletions src/ifs/jay_compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 4 additions & 5 deletions src/ifs/jay_output.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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>,
}

Expand All @@ -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(),
Expand All @@ -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));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ifs/jay_screencast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(());
};
Expand Down
74 changes: 57 additions & 17 deletions src/ifs/wl_output.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod removed_output;

use {
crate::{
backend,
Expand Down Expand Up @@ -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 {
Expand All @@ -80,7 +103,7 @@ pub struct OutputId {

impl WlOutputGlobal {
pub fn clear(&self) {
self.node.take();
self.opt.clear();
self.bindings.borrow_mut().clear();
}

Expand Down Expand Up @@ -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(),
}
}

Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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) {
Expand All @@ -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>,
Expand All @@ -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,
Expand All @@ -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,
});
}

Expand All @@ -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();
}
}
};
}
}

Expand Down
63 changes: 63 additions & 0 deletions src/ifs/wl_output/removed_output.rs
Original file line number Diff line number Diff line change
@@ -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);
Loading
Loading