Skip to content

Commit dea81e0

Browse files
MaxVerevkinbeeender
authored andcommitted
Simplify wayland code
1 parent e5f8c1a commit dea81e0

File tree

1 file changed

+74
-103
lines changed

1 file changed

+74
-103
lines changed

src/clipboard/wayland.rs

Lines changed: 74 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ use super::CopyConfig;
33
use super::PasteConfig;
44
use super::mime_type::decide_mime_type;
55
use crate::protocol::SourceData;
6-
use anyhow::{Context, Error, Result, bail};
6+
use anyhow::{Context, Error, Result};
77
use nix::unistd::{pipe, read};
88
use std::collections::HashMap;
99
use std::ffi::CString;
1010
use std::fs::File;
1111
use std::io::Write;
1212
use std::os::fd::AsRawFd;
13+
use wayrs_client::core::ObjectId;
1314
use wayrs_client::protocol::wl_seat::WlSeat;
1415
use wayrs_client::{Connection, EventCtx, IoMode};
1516
use wayrs_protocols::wlr_data_control_unstable_v1::{
@@ -33,14 +34,20 @@ struct CopyEventState {
3334
}
3435

3536
struct PasteEventState {
36-
finishied: bool,
37-
result: Option<Error>,
3837
// Stored offers for selection and primary selection (middle-click paste).
3938
offers: HashMap<ZwlrDataControlOfferV1, Vec<String>>,
39+
stage: PasteEventStage,
4040

4141
config: PasteConfig,
4242
}
4343

44+
enum PasteEventStage {
45+
Done,
46+
Err(Error),
47+
CollectingOffers,
48+
GotSelection(ObjectId),
49+
}
50+
4451
impl ClipBackend for WaylandBackend {
4552
fn copy(&self, config: CopyConfig) -> Result<()> {
4653
copy_wayland(config)
@@ -77,26 +84,58 @@ fn paste_wayland(cfg: PasteConfig) -> Result<()> {
7784
);
7885

7986
let mut state = PasteEventState {
80-
finishied: false,
81-
result: None,
8287
offers: HashMap::new(),
88+
stage: PasteEventStage::CollectingOffers,
8389
config: cfg,
8490
};
8591

86-
client.conn.flush(IoMode::Blocking).unwrap();
87-
loop {
88-
if state.finishied {
89-
break;
92+
let selection_id = loop {
93+
match state.stage {
94+
PasteEventStage::Done => return Ok(()),
95+
PasteEventStage::Err(err) => return Err(err),
96+
PasteEventStage::CollectingOffers => (),
97+
PasteEventStage::GotSelection(id) => break id,
9098
}
99+
100+
client.conn.flush(IoMode::Blocking).unwrap();
91101
client.conn.recv_events(IoMode::Blocking).unwrap();
92102
client.conn.dispatch_events(&mut state);
93-
}
103+
};
104+
105+
let (offer, supported_types) = state.offers.get_key_value(&selection_id).unwrap();
94106

95-
if state.result.is_none() {
107+
// with "-l", list the mime-types and return
108+
if state.config.list_types_only {
109+
for mt in supported_types {
110+
writeln!(state.config.writter, "{mt}")?;
111+
}
96112
return Ok(());
97113
}
98114

99-
bail!(state.result.unwrap());
115+
let mime_type = CString::new(decide_mime_type(
116+
&state.config.expected_mime_type,
117+
supported_types,
118+
)?)?;
119+
120+
// offer.receive needs a fd to write, we cannot use the stdin since the read side of the
121+
// pipe may close earlier before all data written.
122+
let fds = pipe()?;
123+
offer.receive(&mut client.conn, mime_type, fds.1);
124+
client.conn.flush(IoMode::Blocking)?;
125+
126+
let mut buffer = vec![0; 1024 * 4];
127+
loop {
128+
// Read from the pipe until EOF
129+
let n = read(fds.0.as_raw_fd(), &mut buffer)?;
130+
if n > 0 {
131+
// Write the content to the destination
132+
state.config.writter.write(&buffer[0..n])?;
133+
} else {
134+
break;
135+
}
136+
}
137+
138+
Ok(())
100139
}
101140

102141
fn copy_wayland(config: CopyConfig) -> Result<()> {
@@ -139,37 +178,18 @@ fn copy_wayland(config: CopyConfig) -> Result<()> {
139178

140179
#[allow(clippy::collapsible_match)]
141180
fn wl_device_cb_for_paste(ctx: EventCtx<PasteEventState, ZwlrDataControlDeviceV1>) {
142-
macro_rules! unwrap_or_return {
143-
( $e:expr, $report_error:expr) => {
144-
match $e {
145-
Ok(x) => x,
146-
Err(e) => {
147-
if $report_error {
148-
ctx.state.result = Some(e.into())
149-
} else {
150-
// Errors like empty clipboard are not real problems
151-
log::error!("{}", e)
152-
}
153-
ctx.state.finishied = true;
154-
ctx.conn.break_dispatch_loop();
155-
return;
156-
}
157-
}
158-
};
159-
}
160-
161181
match ctx.event {
162182
// Received before Selection or PrimarySelection
163183
// Need to request mime-types here
164184
zwlr_data_control_device_v1::Event::DataOffer(offer) => {
165185
if ctx.state.offers.insert(offer, Vec::new()).is_some() {
166186
log::error!("Duplicated offer received")
167187
}
168-
ctx.conn.set_callback_for(offer, move |ctx| {
188+
ctx.conn.set_callback_for(offer, |ctx| {
169189
if let zwlr_data_control_offer_v1::Event::Offer(mime_type) = ctx.event {
170190
if let Ok(str) = mime_type.to_str() {
171191
let new_type = str.to_string();
172-
let mime_types = ctx.state.offers.get_mut(&offer).unwrap();
192+
let mime_types = ctx.state.offers.get_mut(&ctx.proxy).unwrap();
173193
if !mime_types.iter().any(|s| new_type.eq(s)) {
174194
// Duplicated mime-types could be reported (wl-paste -l shows the same)
175195
mime_types.push(new_type);
@@ -180,81 +200,32 @@ fn wl_device_cb_for_paste(ctx: EventCtx<PasteEventState, ZwlrDataControlDeviceV1
180200
}
181201
});
182202
}
183-
// Do paste here
184-
zwlr_data_control_device_v1::Event::PrimarySelection(o)
185-
| zwlr_data_control_device_v1::Event::Selection(o) => {
186-
match ctx.event {
187-
zwlr_data_control_device_v1::Event::PrimarySelection(_) => {
188-
if !ctx.state.config.use_primary {
189-
return;
190-
}
191-
}
192-
_ => {
193-
if ctx.state.config.use_primary {
194-
return;
195-
}
196-
}
197-
}
198-
if o.is_none() {
199-
log::error!("No data in the clipboard");
200-
ctx.state.finishied = true;
201-
ctx.conn.break_dispatch_loop();
202-
return;
203-
}
204-
let obj_id = o.unwrap();
205-
206-
let (offer, supported_types) = ctx
207-
.state
208-
.offers
209-
.iter()
210-
.find(|pair| *(pair.0) == obj_id)
211-
.unwrap();
212-
213-
// with "-l", list the mime-types and return
214-
if ctx.state.config.list_types_only {
215-
for mt in supported_types {
216-
writeln!(ctx.state.config.writter, "{}", mt).unwrap()
217-
}
218-
ctx.state.finishied = true;
219-
ctx.conn.break_dispatch_loop();
220-
return;
203+
zwlr_data_control_device_v1::Event::Selection(o) => {
204+
if !ctx.state.config.use_primary {
205+
let Some(obj_id) = o else {
206+
log::error!("No data in the clipboard");
207+
ctx.state.stage = PasteEventStage::Done;
208+
ctx.conn.break_dispatch_loop();
209+
return;
210+
};
211+
ctx.state.stage = PasteEventStage::GotSelection(obj_id);
221212
}
222-
223-
let str = unwrap_or_return!(
224-
decide_mime_type(&ctx.state.config.expected_mime_type, supported_types),
225-
false
226-
);
227-
let mime_type = unwrap_or_return!(CString::new(str), true);
228-
229-
// offer.receive needs a fd to write, we cannot use the stdin since the read side of the
230-
// pipe may close earlier before all data written.
231-
let fds = unwrap_or_return!(pipe(), true);
232-
offer.receive(ctx.conn, mime_type, fds.1);
233-
// This looks strange, but it is working. It seems offer.receive is a request but nont a
234-
// blocking call, which needs an extra loop to finish. Maybe a callback needs to be set
235-
// to wait until it is processed, but I have no idea how to do that.
236-
// conn.set_callback_for() doesn't work for the offer here.
237-
ctx.conn.blocking_roundtrip().unwrap();
238-
let mut buffer = vec![0; 1024 * 4];
239-
loop {
240-
// Read from the pipe until EOF
241-
let n = unwrap_or_return!(read(fds.0.as_raw_fd(), &mut buffer), true);
242-
if n > 0 {
243-
// Write the content to the destination
244-
unwrap_or_return!(ctx.state.config.writter.write(&buffer[0..n]), true);
245-
} else {
246-
break;
247-
}
213+
}
214+
zwlr_data_control_device_v1::Event::PrimarySelection(o) => {
215+
if ctx.state.config.use_primary {
216+
let Some(obj_id) = o else {
217+
log::error!("No data in the clipboard");
218+
ctx.state.stage = PasteEventStage::Done;
219+
ctx.conn.break_dispatch_loop();
220+
return;
221+
};
222+
ctx.state.stage = PasteEventStage::GotSelection(obj_id);
248223
}
249-
250-
offer.destroy(ctx.conn);
251-
ctx.state.finishied = true;
252-
ctx.conn.break_dispatch_loop();
253224
}
254225
zwlr_data_control_device_v1::Event::Finished => {
255226
log::debug!("Received 'Finished' event");
256-
ctx.state.result = Some(Error::msg("The data control object has been destroyed"));
257-
ctx.state.finishied = true;
227+
ctx.state.stage =
228+
PasteEventStage::Err(Error::msg("The data control object has been destroyed"));
258229
ctx.conn.break_dispatch_loop();
259230
}
260231
_ => unreachable!("Unexpected event for device callback"),

0 commit comments

Comments
 (0)