11use std:: fs;
22
3- use crate :: models:: { DesktopEnvironment , ShortcutBackend } ;
3+ use crate :: models:: { DesktopEnvironment , ShortcutApplyCapability , ShortcutBackend } ;
44use wayscriber:: shortcut_hint:: {
55 GNOME_MEDIA_KEYS_KEY , GNOME_MEDIA_KEYS_SCHEMA , GNOME_WAYSCRIBER_KEYBINDING_PATH ,
6- gnome_effective_shortcut, gnome_shortcut_schema_with_path, normalize_shortcut_hint,
7- parse_gsettings_path_list,
6+ PORTAL_APP_ID_ENV , PORTAL_SHORTCUT_ENV , PORTAL_SHORTCUT_OPT_IN_ENV , PortalShortcutDropInState ,
7+ gnome_effective_shortcut, gnome_shortcut_schema_with_path, parse_gsettings_path_list,
8+ parse_portal_shortcut_dropin_state,
9+ parse_portal_shortcut_from_dropin as shared_parse_portal_shortcut_from_dropin,
810} ;
911
1012use super :: command:: { command_available, run_command, run_command_checked} ;
1113use super :: service:: {
1214 escape_systemd_env_value, portal_shortcut_dropin_path, query_service_active,
13- require_systemctl_available, run_systemctl_user,
15+ remove_portal_shortcut_dropin_if_gnome , require_systemctl_available, run_systemctl_user,
1416} ;
1517
1618const PORTAL_APP_ID : & str = "wayscriber" ;
@@ -25,21 +27,38 @@ pub(super) fn read_configured_shortcut(backend: ShortcutBackend) -> Option<Strin
2527 }
2628}
2729
30+ pub ( super ) fn read_portal_shortcut_dropin_state ( ) -> PortalShortcutDropInState {
31+ let Some ( path) = portal_shortcut_dropin_path ( ) else {
32+ return PortalShortcutDropInState :: default ( ) ;
33+ } ;
34+ let Ok ( content) = fs:: read_to_string ( path) else {
35+ return PortalShortcutDropInState :: default ( ) ;
36+ } ;
37+ parse_portal_shortcut_dropin_state ( & content)
38+ }
39+
2840pub ( super ) fn apply_shortcut ( shortcut_input : & str ) -> Result < String , String > {
2941 let desktop = DesktopEnvironment :: detect_current ( ) ;
30- let backend = ShortcutBackend :: from_environment (
42+ let apply_capability = ShortcutApplyCapability :: from_environment (
3143 desktop,
3244 command_available ( "gsettings" ) ,
3345 command_available ( "systemctl" ) ,
3446 ) ;
3547
36- match backend {
37- ShortcutBackend :: GnomeCustomShortcut => {
48+ match apply_capability {
49+ ShortcutApplyCapability :: GnomeCustomShortcut => {
3850 let normalized = normalize_shortcut_for_gnome ( shortcut_input) ?;
3951 apply_gnome_custom_shortcut ( & normalized) ?;
52+ let removed_dropin = remove_portal_shortcut_dropin_if_gnome ( desktop) ?;
53+ if command_available ( "systemctl" ) {
54+ run_systemctl_user ( & [ "daemon-reload" ] ) ?;
55+ if removed_dropin && query_service_active ( ) {
56+ run_systemctl_user ( & [ "restart" , "wayscriber.service" ] ) ?;
57+ }
58+ }
4059 Ok ( format ! ( "Configured GNOME shortcut: {normalized}" ) )
4160 }
42- ShortcutBackend :: PortalServiceDropIn => {
61+ ShortcutApplyCapability :: PortalServiceDropIn => {
4362 require_systemctl_available ( ) ?;
4463 let normalized = normalize_shortcut_for_portal ( shortcut_input) ?;
4564 let dropin_path = write_portal_shortcut_dropin ( & normalized) ?;
@@ -52,7 +71,7 @@ pub(super) fn apply_shortcut(shortcut_input: &str) -> Result<String, String> {
5271 dropin_path. display( )
5372 ) )
5473 }
55- ShortcutBackend :: Manual => Err (
74+ ShortcutApplyCapability :: Manual => Err (
5675 "Automatic shortcut setup is not available in this desktop session; bind `pkill -SIGUSR1 wayscriber` manually."
5776 . to_string ( ) ,
5877 ) ,
@@ -160,9 +179,7 @@ fn write_portal_shortcut_dropin(shortcut: &str) -> Result<std::path::PathBuf, St
160179
161180 let escaped_shortcut = escape_systemd_env_value ( shortcut) ;
162181 let escaped_app_id = escape_systemd_env_value ( PORTAL_APP_ID ) ;
163- let contents = format ! (
164- "[Service]\n Environment=\" WAYSCRIBER_PORTAL_SHORTCUT={escaped_shortcut}\" \n Environment=\" WAYSCRIBER_PORTAL_APP_ID={escaped_app_id}\" \n "
165- ) ;
182+ let contents = render_portal_shortcut_dropin ( & escaped_shortcut, & escaped_app_id) ;
166183 fs:: write ( & dropin_path, contents) . map_err ( |err| {
167184 format ! (
168185 "Failed to write portal shortcut drop-in {}: {}" ,
@@ -173,23 +190,20 @@ fn write_portal_shortcut_dropin(shortcut: &str) -> Result<std::path::PathBuf, St
173190 Ok ( dropin_path)
174191}
175192
193+ fn render_portal_shortcut_dropin ( escaped_shortcut : & str , escaped_app_id : & str ) -> String {
194+ format ! (
195+ "[Service]\n Environment=\" {PORTAL_SHORTCUT_OPT_IN_ENV}=1\" \n Environment=\" {PORTAL_SHORTCUT_ENV}={escaped_shortcut}\" \n Environment=\" {PORTAL_APP_ID_ENV}={escaped_app_id}\" \n "
196+ )
197+ }
198+
176199fn read_portal_shortcut_from_dropin ( ) -> Option < String > {
177200 let path = portal_shortcut_dropin_path ( ) ?;
178201 let content = fs:: read_to_string ( path) . ok ( ) ?;
179202 parse_portal_shortcut_from_dropin ( & content)
180203}
181204
182205fn parse_portal_shortcut_from_dropin ( content : & str ) -> Option < String > {
183- content. lines ( ) . find_map ( |line| {
184- let trimmed = line. trim ( ) ;
185- let prefix = "Environment=\" WAYSCRIBER_PORTAL_SHORTCUT=" ;
186- if !trimmed. starts_with ( prefix) || !trimmed. ends_with ( '"' ) {
187- return None ;
188- }
189- let inner = & trimmed[ prefix. len ( ) ..trimmed. len ( ) - 1 ] ;
190- let unescaped = inner. replace ( "\\ \" " , "\" " ) . replace ( "\\ \\ " , "\\ " ) ;
191- normalize_shortcut_hint ( Some ( & unescaped) )
192- } )
206+ shared_parse_portal_shortcut_from_dropin ( content)
193207}
194208
195209fn serialize_gsettings_path_list ( paths : & [ String ] ) -> String {
@@ -343,6 +357,14 @@ mod tests {
343357 assert_eq ! ( parse_portal_shortcut_from_dropin( content) , None ) ;
344358 }
345359
360+ #[ test]
361+ fn render_portal_shortcut_dropin_includes_explicit_opt_in_marker ( ) {
362+ let rendered = render_portal_shortcut_dropin ( "<Ctrl><Shift>g" , PORTAL_APP_ID ) ;
363+ assert ! ( rendered. contains( "Environment=\" WAYSCRIBER_ENABLE_PORTAL_SHORTCUTS=1\" " ) ) ;
364+ assert ! ( rendered. contains( "Environment=\" WAYSCRIBER_PORTAL_SHORTCUT=<Ctrl><Shift>g\" " ) ) ;
365+ assert ! ( rendered. contains( "Environment=\" WAYSCRIBER_PORTAL_APP_ID=wayscriber\" " ) ) ;
366+ }
367+
346368 #[ test]
347369 fn resolve_gnome_shortcut_requires_registered_path ( ) {
348370 let custom_keybindings =
0 commit comments