@@ -9,22 +9,86 @@ use futures::StreamExt;
9
9
use pop_launcher:: * ;
10
10
use std:: borrow:: Cow ;
11
11
use std:: hash:: { Hash , Hasher } ;
12
- use std:: path :: PathBuf ;
12
+ use std:: process :: Command ;
13
13
use tokio:: io:: AsyncWrite ;
14
+ use tracing:: error;
15
+ use crate :: desktop_entries:: graphics:: is_switchable;
14
16
15
17
#[ derive( Debug , Eq ) ]
16
18
struct Item {
17
19
appid : String ,
18
20
description : String ,
21
+ context : Vec < ContextAction > ,
22
+ prefer_non_default_gpu : bool ,
19
23
exec : String ,
20
24
icon : Option < String > ,
21
25
keywords : Option < Vec < String > > ,
22
26
name : String ,
23
- path : PathBuf ,
24
- prefers_non_default_gpu : bool ,
25
27
src : PathSource ,
26
28
}
27
29
30
+ #[ derive( Debug , PartialEq , Eq ) ]
31
+ pub enum ContextAction {
32
+ Action ( Action ) ,
33
+ GpuPreference ( GpuPreference )
34
+ }
35
+
36
+ impl Item {
37
+ fn run ( & self , action_idx : Option < Indice > ) {
38
+ match action_idx {
39
+ // No action provided just run the desktop entry with the default gpu
40
+ None => run_exec_command ( & self . exec , self . prefer_non_default_gpu ) ,
41
+ // Run the provided action
42
+ Some ( idx) => {
43
+ match self . context . get ( idx as usize ) {
44
+ None => error ! ( "Could not find context action at index {idx}" ) ,
45
+ Some ( action) => match action {
46
+ ContextAction :: Action ( action) => run_exec_command ( & action. exec , self . prefer_non_default_gpu ) ,
47
+ ContextAction :: GpuPreference ( pref) => match pref {
48
+ GpuPreference :: Default => run_exec_command ( & self . exec , false ) ,
49
+ GpuPreference :: NonDefault => run_exec_command ( & self . exec , true ) ,
50
+ } ,
51
+ } ,
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ fn run_exec_command ( exec : & str , discrete_graphics : bool ) {
59
+ let cmd = shell_words:: split ( exec) ;
60
+ let cmd: Vec < String > = cmd. unwrap ( ) ;
61
+
62
+ let args = cmd
63
+ . iter ( )
64
+ // Filter desktop entries field code. Is this needed ?
65
+ // see: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
66
+ . filter ( |arg| !arg. starts_with ( '%' ) )
67
+ . collect :: < Vec < & String > > ( ) ;
68
+
69
+ let mut cmd = Command :: new ( & args[ 0 ] ) ;
70
+ let cmd = cmd. args ( & args[ 1 ..] ) ;
71
+
72
+ // FIXME: Will this work with Nvidia Gpu ?
73
+ // We probably want to look there : https://gitlab.freedesktop.org/hadess/switcheroo-control/-/blob/master/src/switcheroo-control.c
74
+ let cmd = if discrete_graphics {
75
+ cmd. env ( "DRI_PRIME" , "1" )
76
+ } else {
77
+ cmd
78
+ } ;
79
+
80
+ if let Err ( err) = cmd. spawn ( ) {
81
+ error ! ( "Failed to run desktop entry: {err}" ) ;
82
+ }
83
+ }
84
+
85
+ #[ derive( Debug , PartialEq , Eq ) ]
86
+ pub struct Action {
87
+ pub name : String ,
88
+ pub description : String ,
89
+ pub exec : String ,
90
+ }
91
+
28
92
impl Hash for Item {
29
93
fn hash < H : Hasher > ( & self , state : & mut H ) {
30
94
self . appid . hash ( state) ;
@@ -150,6 +214,44 @@ impl<W: AsyncWrite + Unpin> App<W> {
150
214
continue ;
151
215
}
152
216
217
+ let mut actions = vec ! [ ] ;
218
+
219
+ if let Some ( entries) = entry. actions ( ) {
220
+ for action in entries. split ( ';' ) {
221
+ let action =
222
+ entry. action_name ( action, locale) . and_then ( |name| {
223
+ entry. action_exec ( action) . map ( |exec| Action {
224
+ name : action. to_string ( ) ,
225
+ description : name. to_string ( ) ,
226
+ exec : exec. to_string ( ) ,
227
+ } )
228
+ } ) ;
229
+
230
+ if let Some ( action) = action {
231
+ actions. push ( action) ;
232
+ }
233
+ }
234
+ }
235
+
236
+ let actions = actions
237
+ . into_iter ( )
238
+ . map ( |action| ContextAction :: Action ( action) ) ;
239
+
240
+ let entry_prefers_non_default_gpu = entry. prefers_non_default_gpu ( ) ;
241
+ let prefer_non_default_gpu = entry_prefers_non_default_gpu && is_switchable ( ) ;
242
+ let prefer_default_gpu = !entry_prefers_non_default_gpu && is_switchable ( ) ;
243
+
244
+ let context: Vec < ContextAction > = if prefer_non_default_gpu {
245
+ vec ! [ ContextAction :: GpuPreference ( GpuPreference :: Default ) ]
246
+ } else if prefer_default_gpu {
247
+ vec ! [ ContextAction :: GpuPreference ( GpuPreference :: NonDefault ) ]
248
+ } else {
249
+ vec ! [ ]
250
+ }
251
+ . into_iter ( )
252
+ . chain ( actions)
253
+ . collect ( ) ;
254
+
153
255
let item = Item {
154
256
appid : entry. appid . to_owned ( ) ,
155
257
name : name. to_string ( ) ,
@@ -163,9 +265,9 @@ impl<W: AsyncWrite + Unpin> App<W> {
163
265
} ) ,
164
266
icon : entry. icon ( ) . map ( |x| x. to_owned ( ) ) ,
165
267
exec : exec. to_owned ( ) ,
166
- path : path. clone ( ) ,
167
- prefers_non_default_gpu : entry. prefers_non_default_gpu ( ) ,
168
268
src,
269
+ context,
270
+ prefer_non_default_gpu : entry_prefers_non_default_gpu
169
271
} ;
170
272
171
273
deduplicator. insert ( item) ;
@@ -179,57 +281,58 @@ impl<W: AsyncWrite + Unpin> App<W> {
179
281
}
180
282
181
283
async fn activate ( & mut self , id : u32 ) {
284
+ send ( & mut self . tx , PluginResponse :: Close ) . await ;
285
+
182
286
if let Some ( entry) = self . entries . get ( id as usize ) {
183
- let response = PluginResponse :: DesktopEntry {
184
- path : entry. path . clone ( ) ,
185
- gpu_preference : if entry. prefers_non_default_gpu {
186
- GpuPreference :: NonDefault
187
- } else {
188
- GpuPreference :: Default
189
- } ,
190
- } ;
191
-
192
- send ( & mut self . tx , response) . await ;
287
+ entry. run ( None ) ;
288
+ } else {
289
+ error ! ( "Desktop entry not found at index {id}" ) ;
193
290
}
291
+
292
+ std:: process:: exit ( 0 ) ;
194
293
}
195
294
196
295
async fn activate_context ( & mut self , id : u32 , context : u32 ) {
197
- if let Some ( entry) = self . entries . get ( id as usize ) {
198
- let response = match context {
199
- 0 => PluginResponse :: DesktopEntry {
200
- path : entry. path . clone ( ) ,
201
- gpu_preference : if !entry. prefers_non_default_gpu {
202
- GpuPreference :: NonDefault
203
- } else {
204
- GpuPreference :: Default
205
- } ,
206
- } ,
207
- _ => return ,
208
- } ;
296
+ send ( & mut self . tx , PluginResponse :: Close ) . await ;
209
297
210
- send ( & mut self . tx , response) . await ;
298
+ if let Some ( entry) = self . entries . get ( id as usize ) {
299
+ entry. run ( Some ( context) )
211
300
}
212
301
}
213
302
214
303
async fn context ( & mut self , id : u32 ) {
215
304
if let Some ( entry) = self . entries . get ( id as usize ) {
216
305
let mut options = Vec :: new ( ) ;
217
306
218
- if graphics:: is_switchable ( ) {
219
- options. push ( ContextOption {
220
- id : 0 ,
221
- name : ( if entry. prefers_non_default_gpu {
222
- "Launch Using Integrated Graphics Card"
223
- } else {
224
- "Launch Using Discrete Graphics Card"
225
- } )
226
- . to_owned ( ) ,
227
- } ) ;
307
+ for ( idx, action) in entry. context . iter ( ) . enumerate ( ) {
308
+ match action {
309
+ ContextAction :: Action ( action) => options. push ( ContextOption {
310
+ id : idx as u32 ,
311
+ name : action. name . to_owned ( ) ,
312
+ description : action. description . to_owned ( ) ,
313
+ exec : Some ( action. exec . to_string ( ) ) ,
314
+ } ) ,
315
+ ContextAction :: GpuPreference ( pref) => {
316
+ match pref {
317
+ GpuPreference :: Default => options. push ( ContextOption {
318
+ id : 0 ,
319
+ name : "Integrated Graphics" . to_owned ( ) ,
320
+ description : "Launch Using Integrated Graphics Card" . to_owned ( ) ,
321
+ exec : None ,
322
+ } ) ,
323
+ GpuPreference :: NonDefault => options. push ( ContextOption {
324
+ id : 0 ,
325
+ name : "Discrete Graphics" . to_owned ( ) ,
326
+ description : "Launch Using Discrete Graphics Card" . to_owned ( ) ,
327
+ exec : None ,
328
+ } ) ,
329
+ }
330
+ }
331
+ }
228
332
}
229
333
230
334
if !options. is_empty ( ) {
231
335
let response = PluginResponse :: Context { id, options } ;
232
-
233
336
send ( & mut self . tx , response) . await ;
234
337
}
235
338
}
0 commit comments