@@ -3,13 +3,14 @@ use super::CopyConfig;
33use super :: PasteConfig ;
44use super :: mime_type:: decide_mime_type;
55use crate :: protocol:: SourceData ;
6- use anyhow:: { Context , Error , Result , bail } ;
6+ use anyhow:: { Context , Error , Result } ;
77use nix:: unistd:: { pipe, read} ;
88use std:: collections:: HashMap ;
99use std:: ffi:: CString ;
1010use std:: fs:: File ;
1111use std:: io:: Write ;
1212use std:: os:: fd:: AsRawFd ;
13+ use wayrs_client:: core:: ObjectId ;
1314use wayrs_client:: protocol:: wl_seat:: WlSeat ;
1415use wayrs_client:: { Connection , EventCtx , IoMode } ;
1516use wayrs_protocols:: wlr_data_control_unstable_v1:: {
@@ -33,14 +34,20 @@ struct CopyEventState {
3334}
3435
3536struct 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+
4451impl 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
102141fn copy_wayland ( config : CopyConfig ) -> Result < ( ) > {
@@ -139,37 +178,18 @@ fn copy_wayland(config: CopyConfig) -> Result<()> {
139178
140179#[ allow( clippy:: collapsible_match) ]
141180fn 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