@@ -9,17 +9,35 @@ use gtk::prelude::*;
99use tracing:: { error, info, warn} ;
1010
1111use crate :: fakers:: { Command , CommandRunner , FdMode } ;
12+ use crate :: query:: Query ;
1213
1314use gtk:: subclass:: prelude:: * ;
1415
1516#[ derive( Clone , Debug , serde:: Serialize , serde:: Deserialize ) ]
1617pub struct Terminal {
1718 pub name : String ,
1819 pub program : String ,
20+ /// Arguments that come after the program but before the separator_arg.
21+ /// For example, for flatpak terminals: ["run", "org.gnome.Console"]
22+ #[ serde( default ) ]
23+ pub extra_args : Vec < String > ,
1924 pub separator_arg : String ,
2025 pub read_only : bool ,
2126}
2227
28+ impl Terminal {
29+ /// Returns a unique identifier for this terminal combining program and extra_args.
30+ /// This is used for deduplication since multiple terminals may use the same program
31+ /// (e.g., multiple flatpak terminals all use "flatpak" as the program).
32+ pub fn full_command_id ( & self ) -> String {
33+ if self . extra_args . is_empty ( ) {
34+ self . program . clone ( )
35+ } else {
36+ format ! ( "{} {}" , self . program, self . extra_args. join( " " ) )
37+ }
38+ }
39+ }
40+
2341static SUPPORTED_TERMINALS : LazyLock < Vec < Terminal > > = LazyLock :: new ( || {
2442 [
2543 ( "GNOME Console" , "kgx" , "--" ) ,
@@ -44,6 +62,7 @@ static SUPPORTED_TERMINALS: LazyLock<Vec<Terminal>> = LazyLock::new(|| {
4462 . map ( |( name, program, separator_arg) | Terminal {
4563 name : name. to_string ( ) ,
4664 program : program. to_string ( ) ,
65+ extra_args : vec ! [ ] ,
4766 separator_arg : separator_arg. to_string ( ) ,
4867 read_only : true ,
4968 } )
@@ -54,7 +73,7 @@ static FLATPAK_TERMINAL_CANDIDATES: LazyLock<Vec<Terminal>> = LazyLock::new(|| {
5473 let base_terminals = [
5574 ( "Ptyxis" , "app.devsuite.Ptyxis" , "--" ) ,
5675 ( "GNOME Console" , "org.gnome.Console" , "--" ) ,
57- ( "BlackBox" , "com.raggesilver.BlackBox" , "--" ) ,
76+ // ("BlackBox", "com.raggesilver.BlackBox", "--"), for some reason it doesn't work
5877 ( "WezTerm" , "org.wezfurlong.wezterm" , "start --" ) ,
5978 ( "Foot" , "page.codeberg.dnkl.foot" , "-e" ) ,
6079 ] ;
@@ -64,14 +83,16 @@ static FLATPAK_TERMINAL_CANDIDATES: LazyLock<Vec<Terminal>> = LazyLock::new(|| {
6483 // Stable
6584 candidates. push ( Terminal {
6685 name : format ! ( "{} (Flatpak)" , name) ,
67- program : format ! ( "flatpak run {}" , app_id) ,
86+ program : "flatpak" . to_string ( ) ,
87+ extra_args : vec ! [ "run" . to_string( ) , app_id. to_string( ) ] ,
6888 separator_arg : separator_arg. to_string ( ) ,
6989 read_only : true ,
7090 } ) ;
7191 // Devel
7292 candidates. push ( Terminal {
7393 name : format ! ( "{} Devel (Flatpak)" , name) ,
74- program : format ! ( "flatpak run {}.Devel" , app_id) ,
94+ program : "flatpak" . to_string ( ) ,
95+ extra_args : vec ! [ "run" . to_string( ) , format!( "{}.Devel" , app_id) ] ,
7596 separator_arg : separator_arg. to_string ( ) ,
7697 read_only : true ,
7798 } ) ;
@@ -88,6 +109,7 @@ mod imp {
88109 pub list : RefCell < Vec < Terminal > > ,
89110 pub custom_list_path : PathBuf ,
90111 pub command_runner : OnceCell < CommandRunner > ,
112+ pub flatpak_terminals_query : Query < Vec < Terminal > > ,
91113 }
92114
93115 impl Default for TerminalRepository {
@@ -97,6 +119,9 @@ mod imp {
97119 list : RefCell :: new ( vec ! [ ] ) ,
98120 custom_list_path,
99121 command_runner : OnceCell :: new ( ) ,
122+ flatpak_terminals_query : Query :: new ( "flatpak_terminals" . into ( ) , || async {
123+ Ok ( vec ! [ ] )
124+ } ) ,
100125 }
101126 }
102127 }
@@ -125,7 +150,7 @@ impl TerminalRepository {
125150 let this: Self = glib:: Object :: builder ( ) . build ( ) ;
126151 this. imp ( )
127152 . command_runner
128- . set ( command_runner)
153+ . set ( command_runner. clone ( ) )
129154 . map_err ( |_| "command runner already set" )
130155 . unwrap ( ) ;
131156
@@ -142,60 +167,71 @@ impl TerminalRepository {
142167 list. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
143168 this. imp ( ) . list . replace ( list) ;
144169
170+ // Set up the flatpak terminals query fetcher
171+ let runner = command_runner. clone ( ) ;
172+ this. imp ( )
173+ . flatpak_terminals_query
174+ . set_fetcher ( move || {
175+ let runner = runner. clone ( ) ;
176+ async move { Self :: fetch_flatpak_terminals ( & runner) . await }
177+ } ) ;
178+
179+ // Connect to query success to update the terminal list
145180 let this_clone = this. clone ( ) ;
146- glib :: MainContext :: default ( ) . spawn_local ( async move {
147- this_clone. discover_flatpak_terminals ( ) . await ;
181+ this . flatpak_terminals_query ( ) . connect_success ( move |terminals| {
182+ this_clone. merge_flatpak_terminals ( terminals . clone ( ) ) ;
148183 } ) ;
149184
150185 this
151186 }
152187
153- pub async fn discover_flatpak_terminals ( & self ) {
154- let Some ( runner) = self . imp ( ) . command_runner . get ( ) else {
155- return ;
156- } ;
157-
188+ async fn fetch_flatpak_terminals ( runner : & CommandRunner ) -> anyhow:: Result < Vec < Terminal > > {
158189 // Get list of installed flatpaks
159190 let mut cmd = Command :: new_with_args ( "flatpak" , [ "list" , "--app" , "--columns=application" ] ) ;
160191 cmd. stdout = FdMode :: Pipe ;
161192 cmd. stderr = FdMode :: Pipe ;
162193
163- let Ok ( output) = runner. output ( cmd) . await else {
164- return ;
165- } ;
166-
167- let stdout = String :: from_utf8_lossy ( & output. stdout ) ;
168- let installed_apps: HashSet < & str > = stdout. lines ( ) . collect ( ) ;
194+ let output = runner. output_string ( cmd) . await ?;
195+ let installed_apps: HashSet < & str > = output. lines ( ) . collect ( ) ;
169196
170197 let mut found_terminals = Vec :: new ( ) ;
171198 for terminal in FLATPAK_TERMINAL_CANDIDATES . iter ( ) {
172- // Extract app_id from program "flatpak run <app_id>"
173- if let Some ( app_id) = terminal. program . split_whitespace ( ) . nth ( 2 ) {
174- if installed_apps. contains ( app_id) {
199+ // Extract app_id from extra_args (e.g., [" run", "org.gnome.Console"])
200+ if let Some ( app_id) = terminal. extra_args . get ( 1 ) {
201+ if installed_apps. contains ( app_id. as_str ( ) ) {
175202 found_terminals. push ( terminal. clone ( ) ) ;
176203 }
177204 }
178205 }
179206
180- if !found_terminals. is_empty ( ) {
181- let mut list = self . imp ( ) . list . borrow_mut ( ) ;
182- // Build a set of existing programs to avoid duplicates
183- let existing_programs: HashSet < & str > =
184- list. iter ( ) . map ( |t| t. program . as_str ( ) ) . collect ( ) ;
185-
186- // Only add terminals that don't already exist
187- let new_terminals: Vec < Terminal > = found_terminals
188- . into_iter ( )
189- . filter ( |t| !existing_programs. contains ( t. program . as_str ( ) ) )
190- . collect ( ) ;
191-
192- if !new_terminals. is_empty ( ) {
193- list. extend ( new_terminals) ;
194- list. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
195- drop ( list) ;
196- self . emit_by_name :: < ( ) > ( "terminals-changed" , & [ ] ) ;
197- }
207+ Ok ( found_terminals)
208+ }
209+
210+ fn merge_flatpak_terminals ( & self , terminals : Vec < Terminal > ) {
211+ if terminals. is_empty ( ) {
212+ return ;
198213 }
214+
215+ let mut list = self . imp ( ) . list . borrow_mut ( ) ;
216+ // Build a set of existing terminal identifiers to avoid duplicates
217+ let existing_ids: HashSet < String > = list. iter ( ) . map ( |t| t. full_command_id ( ) ) . collect ( ) ;
218+
219+ // Only add terminals that don't already exist
220+ let new_terminals: Vec < Terminal > = terminals
221+ . into_iter ( )
222+ . filter ( |t| !existing_ids. contains ( & t. full_command_id ( ) ) )
223+ . collect ( ) ;
224+
225+ if !new_terminals. is_empty ( ) {
226+ list. extend ( new_terminals) ;
227+ list. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
228+ drop ( list) ;
229+ self . emit_by_name :: < ( ) > ( "terminals-changed" , & [ ] ) ;
230+ }
231+ }
232+
233+ pub fn flatpak_terminals_query ( & self ) -> Query < Vec < Terminal > > {
234+ self . imp ( ) . flatpak_terminals_query . clone ( )
199235 }
200236
201237 pub fn is_read_only ( & self , name : & str ) -> bool {
@@ -248,7 +284,7 @@ impl TerminalRepository {
248284 . list
249285 . borrow ( )
250286 . iter ( )
251- . find ( |x| x. program == program)
287+ . find ( |x| x. program == program || x . full_command_id ( ) == program )
252288 . cloned ( )
253289 }
254290
0 commit comments