diff --git a/src/app.rs b/src/app.rs index 0a1db11..20c0874 100644 --- a/src/app.rs +++ b/src/app.rs @@ -369,6 +369,18 @@ pub trait App: 'static /// Return any pending messages from external sources (timers, async, etc.). fn poll_external( &mut self ) -> Vec { vec![] } + /// Tags for which the app wants a fresh `xdg-activation-v1` token this + /// iteration. Drained once per loop; each tag is echoed back verbatim + /// through [`App::on_activation_token`] once the compositor issues the + /// token, so the app can hand it to a child it is about to spawn via the + /// `$XDG_ACTIVATION_TOKEN` launch convention. Default none. + fn take_activation_requests( &mut self ) -> Vec { Vec::new() } + + /// Deliver a token issued for an earlier [`App::take_activation_requests`] + /// tag. `tag` is the string the app handed out; `token` is the opaque + /// activation token to place in the launched child's environment. + fn on_activation_token( &mut self, _tag: String, _token: String ) -> Option { None } + /// Called once, immediately after the first buffer for the main surface /// has been rendered and committed to the compositor. Note: this is the /// client-side commit, not the compositor's present — under VT switching diff --git a/src/event_loop/handlers.rs b/src/event_loop/handlers.rs index 8c491ce..63bb900 100644 --- a/src/event_loop/handlers.rs +++ b/src/event_loop/handlers.rs @@ -468,16 +468,25 @@ smithay_client_toolkit::delegate_data_device!( @ AppData ); // --- `xdg-activation-v1` handler --- // -// We only honour inbound activation here: when a token issued for our -// own request gets delivered, we activate the main surface. Outbound -// requests (so the app can pass a token to another app) are out of -// scope for now — adding them only requires a new public method on -// `AppData` and an extra trait method on `App`. +// A token carrying a `RequestData::app_id` was requested by the app via +// [`App::take_activation_requests`]: echo the tag + token back through +// [`App::on_activation_token`] so the app can pass it to a child it is +// launching. A token with no tag was issued for our own surface, so we +// self-activate. impl smithay_client_toolkit::activation::ActivationHandler for AppData { type RequestData = smithay_client_toolkit::activation::RequestData; - fn new_token( &mut self, token: String, _data: &Self::RequestData ) + fn new_token( &mut self, token: String, data: &Self::RequestData ) { + use smithay_client_toolkit::activation::RequestDataExt; + if let Some( tag ) = data.app_id() + { + if let Some( msg ) = self.app.on_activation_token( tag.to_string(), token ) + { + self.pending_msgs.push( msg ); + } + return; + } if let ( Some( ref activation ), Some( wl ) ) = ( self.activation_state.as_ref(), self.main.surface.try_wl_surface() ) { activation.activate::( &wl, token ); diff --git a/src/event_loop/run.rs b/src/event_loop/run.rs index 52cdefb..28be50a 100644 --- a/src/event_loop/run.rs +++ b/src/event_loop/run.rs @@ -456,6 +456,34 @@ pub( crate ) fn try_run( app: A ) -> Result<(), RunError> let ext: Vec<_> = data.app.poll_external(); data.pending_msgs.extend( ext ); + // Issue any xdg-activation tokens the app asked for. The tag rides + // in `RequestData::app_id` and comes back through + // `ActivationHandler::new_token` (handlers.rs) as the token. When the + // compositor never advertised the global, answer with an empty token + // so the caller still proceeds (falling back to its own matching). + for tag in data.app.take_activation_requests() + { + match data.activation_state + { + Some( ref activation ) => + { + let req = smithay_client_toolkit::activation::RequestData { + app_id: Some( tag ), + seat_and_serial: None, + surface: data.main.surface.try_wl_surface().cloned(), + }; + activation.request_token::>( &data.qh, req ); + } + None => + { + if let Some( msg ) = data.app.on_activation_token( tag, String::new() ) + { + data.pending_msgs.push( msg ); + } + } + } + } + // Drain any inbound clipboard payloads delivered by the // data-device worker thread. Latest writer wins; the channel // is unbounded but selection payloads are small (capped at