Skip to content

Commit 6f8ca0e

Browse files
committed
fix: take care of removed outputs
1 parent 7d99a4e commit 6f8ca0e

File tree

2 files changed

+96
-17
lines changed

2 files changed

+96
-17
lines changed

src/gql.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub enum RiverEventType {
1818
OutputUrgentTags,
1919
OutputLayoutName,
2020
OutputLayoutNameClear,
21+
OutputRemoved,
2122
SeatFocusedOutput,
2223
SeatUnfocusedOutput,
2324
SeatFocusedView,
@@ -33,6 +34,7 @@ impl From<&river::Event> for RiverEventType {
3334
OutputUrgentTags { .. } => RiverEventType::OutputUrgentTags,
3435
OutputLayoutName { .. } => RiverEventType::OutputLayoutName,
3536
OutputLayoutNameClear { .. } => RiverEventType::OutputLayoutNameClear,
37+
OutputRemoved { .. } => RiverEventType::OutputRemoved,
3638
SeatFocusedOutput { .. } => RiverEventType::SeatFocusedOutput,
3739
SeatUnfocusedOutput { .. } => RiverEventType::SeatUnfocusedOutput,
3840
SeatFocusedView { .. } => RiverEventType::SeatFocusedView,
@@ -222,6 +224,25 @@ impl RiverSnapshot {
222224
state.layout_name = None;
223225
});
224226
}
227+
OutputRemoved { id, name } => {
228+
let gql_id = id_to_graphql(id);
229+
let key = gql_id.to_string();
230+
if let Some(state) = self.outputs.remove(&key) {
231+
if let Some(name_value) = state.name {
232+
self.output_names.remove(&name_value);
233+
}
234+
} else if let Some(name_value) = name.as_ref() {
235+
self.output_names.remove(name_value);
236+
}
237+
let clear_focus = self
238+
.seat_focused_output
239+
.as_ref()
240+
.map(|focused| focused.output_id == gql_id)
241+
.unwrap_or(false);
242+
if clear_focus {
243+
self.seat_focused_output = None;
244+
}
245+
}
225246
SeatFocusedOutput { id, name } => {
226247
self.seat_focused_output = Some(NamedOutputId {
227248
output_id: id_to_graphql(id),
@@ -444,6 +465,7 @@ fn event_types_for_name(name: &str) -> Vec<RiverEventType> {
444465
RiverEventType::OutputLayoutName,
445466
RiverEventType::OutputLayoutNameClear,
446467
],
468+
"OutputRemoved" => vec![RiverEventType::OutputRemoved],
447469
"SeatFocusedOutput" => vec![RiverEventType::SeatFocusedOutput],
448470
"SeatUnfocusedOutput" => vec![RiverEventType::SeatUnfocusedOutput],
449471
"SeatFocusedView" => vec![RiverEventType::SeatFocusedView],
@@ -473,6 +495,7 @@ fn event_output_name<'a>(event: &'a river::Event) -> Option<&'a str> {
473495
| OutputUrgentTags { name, .. }
474496
| OutputLayoutName { name, .. }
475497
| OutputLayoutNameClear { name, .. }
498+
| OutputRemoved { name, .. }
476499
| SeatFocusedOutput { name, .. }
477500
| SeatUnfocusedOutput { name, .. } => name.as_deref(),
478501

@@ -527,6 +550,7 @@ pub enum RiverEvent {
527550
OutputViewTags(GOutputViewTags),
528551
OutputUrgentTags(GOutputUrgentTags),
529552
OutputLayoutName(GOutputLayoutName),
553+
OutputRemoved(GOutputRemoved),
530554
SeatFocusedOutput(GSeatFocusedOutput),
531555
SeatUnfocusedOutput(GSeatUnfocusedOutput),
532556
SeatFocusedView(GSeatFocusedView),
@@ -632,6 +656,22 @@ impl GOutputLayoutName {
632656
}
633657
}
634658

659+
#[derive(Clone)]
660+
pub struct GOutputRemoved {
661+
pub output_id: ID,
662+
pub name: Option<String>,
663+
}
664+
#[Object(name = "OutputRemoved")]
665+
impl GOutputRemoved {
666+
async fn output_id(&self) -> &ID {
667+
&self.output_id
668+
}
669+
670+
async fn name(&self) -> Option<&str> {
671+
self.name.as_deref()
672+
}
673+
}
674+
635675
// no-op clear event omitted in minimal schema
636676

637677
#[derive(Clone)]
@@ -746,6 +786,13 @@ fn make_river_event(value: river::Event, include_lists: bool) -> RiverEvent {
746786
output_name: name,
747787
layout: String::new(),
748788
}),
789+
OutputRemoved {
790+
id: output_id,
791+
name,
792+
} => RiverEvent::OutputRemoved(GOutputRemoved {
793+
output_id: id_to_graphql(&output_id),
794+
name,
795+
}),
749796
SeatFocusedOutput {
750797
id: output_id,
751798
name,

src/river.rs

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::collections::{HashMap, HashSet};
22

33
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
44

@@ -52,6 +52,10 @@ pub enum Event {
5252
id: ObjectId,
5353
name: Option<String>,
5454
},
55+
OutputRemoved {
56+
id: ObjectId,
57+
name: Option<String>,
58+
},
5559

5660
SeatFocusedOutput {
5761
id: ObjectId,
@@ -70,8 +74,8 @@ pub enum Event {
7074
}
7175

7276
struct State {
73-
outputs: Vec<WlOutput>,
74-
seats: Vec<WlSeat>,
77+
outputs: HashMap<u32, WlOutput>,
78+
seats: HashMap<u32, WlSeat>,
7579
manager: Option<ZriverStatusManagerV1>,
7680
output_statuses: Vec<ZriverOutputStatusV1>,
7781
seat_statuses: Vec<ZriverSeatStatusV1>,
@@ -83,8 +87,8 @@ struct State {
8387
impl State {
8488
fn new(tx: UnboundedSender<Event>) -> Self {
8589
Self {
86-
outputs: Vec::new(),
87-
seats: Vec::new(),
90+
outputs: HashMap::new(),
91+
seats: HashMap::new(),
8892
manager: None,
8993
output_statuses: Vec::new(),
9094
seat_statuses: Vec::new(),
@@ -115,13 +119,13 @@ impl State {
115119

116120
fn create_status_for_all(&mut self, qh: &QueueHandle<Self>) {
117121
if self.manager.is_some() {
118-
let outs = self.outputs.clone();
119-
for o in &outs {
120-
self.maybe_create_status_for_output(qh, o);
122+
let outputs: Vec<_> = self.outputs.values().cloned().collect();
123+
for output in &outputs {
124+
self.maybe_create_status_for_output(qh, output);
121125
}
122-
let seats = self.seats.clone();
123-
for s in &seats {
124-
self.maybe_create_status_for_seat(qh, s);
126+
let seats: Vec<_> = self.seats.values().cloned().collect();
127+
for seat in &seats {
128+
self.maybe_create_status_for_seat(qh, seat);
125129
}
126130
}
127131
}
@@ -188,15 +192,13 @@ impl Dispatch<WlRegistry, ()> for State {
188192
} => match interface.as_str() {
189193
"wl_output" => {
190194
let output = registry.bind::<WlOutput, _, _>(name, version.min(4), qh, ());
191-
state.outputs.push(output);
192-
let last = state.outputs.last().unwrap().clone();
193-
state.maybe_create_status_for_output(qh, &last);
195+
state.maybe_create_status_for_output(qh, &output);
196+
state.outputs.insert(name, output);
194197
}
195198
"wl_seat" => {
196199
let seat = registry.bind::<WlSeat, _, _>(name, version.min(5), qh, ());
197-
state.seats.push(seat);
198-
let last = state.seats.last().unwrap().clone();
199-
state.maybe_create_status_for_seat(qh, &last);
200+
state.maybe_create_status_for_seat(qh, &seat);
201+
state.seats.insert(name, seat);
200202
}
201203
"zriver_status_manager_v1" => {
202204
let mgr =
@@ -206,6 +208,11 @@ impl Dispatch<WlRegistry, ()> for State {
206208
}
207209
_ => {}
208210
},
211+
wl_registry::Event::GlobalRemove { name } => {
212+
if !state.remove_output(name) {
213+
state.seats.remove(&name);
214+
}
215+
}
209216
_ => {}
210217
}
211218
}
@@ -344,6 +351,31 @@ fn parse_u32_array(bytes: &[u8]) -> Vec<u32> {
344351
v
345352
}
346353

354+
impl State {
355+
fn remove_output(&mut self, global: u32) -> bool {
356+
let Some(output) = self.outputs.remove(&global) else {
357+
return false;
358+
};
359+
let id = output.id();
360+
let label = self.output_label(&id);
361+
let protocol_id = id.protocol_id();
362+
363+
let mut removed_status_ids = HashSet::new();
364+
for (status_id, owner) in &self.output_status_owner {
365+
if owner.protocol_id() == protocol_id {
366+
removed_status_ids.insert(*status_id);
367+
}
368+
}
369+
self.output_status_owner
370+
.retain(|status_id, _| !removed_status_ids.contains(status_id));
371+
self.output_statuses
372+
.retain(|status| !removed_status_ids.contains(&status.id().protocol_id()));
373+
self.output_info.remove(&protocol_id);
374+
let _ = self.tx.send(Event::OutputRemoved { id, name: label });
375+
true
376+
}
377+
}
378+
347379
pub struct RiverStatus;
348380

349381
impl RiverStatus {

0 commit comments

Comments
 (0)