11use crate :: commands:: Function ;
22use crate :: config:: Config ;
3- use crate :: macos:: { focus_this_app, transform_process_to_ui_element} ;
4- use crate :: { macos, utils:: get_installed_apps} ;
5-
3+ use crate :: utils:: get_config_file_path;
64use global_hotkey:: { GlobalHotKeyEvent , HotKeyState } ;
75use iced:: futures:: SinkExt ;
86use iced:: {
@@ -17,9 +15,22 @@ use iced::{
1715 } ,
1816 window:: { self , Id , Settings } ,
1917} ;
18+ use std:: path:: Path ;
2019
20+ #[ cfg( target_os = "macos" ) ]
21+ use crate :: macos:: { focus_this_app, transform_process_to_ui_element} ;
22+ #[ cfg( target_os = "macos" ) ]
23+ use crate :: { macos, utils:: get_installed_apps} ;
24+ #[ cfg( target_os = "macos" ) ]
2125use objc2:: rc:: Retained ;
26+ #[ cfg( target_os = "macos" ) ]
2227use objc2_app_kit:: NSRunningApplication ;
28+
29+ #[ cfg( target_os = "windows" ) ]
30+ use windows:: Win32 :: Foundation :: HWND ;
31+ #[ cfg( target_os = "windows" ) ]
32+ use windows:: Win32 :: UI :: WindowsAndMessaging :: { GetForegroundWindow , SetForegroundWindow } ;
33+
2334use rayon:: iter:: { IntoParallelRefIterator , ParallelIterator } ;
2435use rayon:: slice:: ParallelSliceMut ;
2536
@@ -135,6 +146,9 @@ pub fn default_settings() -> Settings {
135146 }
136147}
137148
149+ #[ derive( Debug , Clone ) ]
150+ pub struct Temp { }
151+
138152#[ derive( Debug , Clone ) ]
139153pub struct Tile {
140154 theme : iced:: Theme ,
@@ -145,38 +159,72 @@ pub struct Tile {
145159 options : Vec < App > ,
146160 visible : bool ,
147161 focused : bool ,
162+ #[ cfg( target_os = "macos" ) ]
148163 frontmost : Option < Retained < NSRunningApplication > > ,
164+ #[ cfg( target_os = "windows" ) ]
165+ frontmost : Option < HWND > ,
149166 config : Config ,
150167 open_hotkey_id : u32 ,
151168}
152169
153170impl Tile {
154171 /// A base window
155172 pub fn new ( keybind_id : u32 , config : & Config ) -> ( Self , Task < Message > ) {
156- let ( id, open) = window:: open ( default_settings ( ) ) ;
173+ let mut settings = default_settings ( ) ;
174+ #[ cfg( target_os = "windows" ) ]
175+ {
176+ // get normal settings and modify position
177+ use crate :: utils:: open_on_focused_monitor;
178+ use iced:: window:: Position :: Specific ;
179+ let pos = open_on_focused_monitor ( ) ;
180+ settings. position = Specific ( pos) ;
181+ }
157182
158- let open = open. discard ( ) . chain ( window:: run ( id, |handle| {
159- macos:: macos_window_config (
160- & handle. window_handle ( ) . expect ( "Unable to get window handle" ) ,
161- ) ;
162- // should work now that we have a window
163- transform_process_to_ui_element ( ) ;
164- } ) ) ;
183+ let ( id, open) = window:: open ( settings) ;
165184
166- let store_icons = config. theme . show_icons ;
167-
168- let user_local_path = std:: env:: var ( "HOME" ) . unwrap ( ) + "/Applications/" ;
185+ #[ cfg( target_os = "macos" ) ]
186+ {
187+ let open = open. discard ( ) . chain ( window:: run ( id, |handle| {
188+ {
189+ macos:: macos_window_config (
190+ & handle. window_handle ( ) . expect ( "Unable to get window handle" ) ,
191+ ) ;
192+ // should work now that we have a window
193+ transform_process_to_ui_element ( ) ;
194+ }
195+ } ) ) ;
196+ }
169197
170- let paths = vec ! [
171- "/Applications/" ,
172- user_local_path. as_str( ) ,
173- "/System/Applications/" ,
174- "/System/Applications/Utilities/" ,
175- ] ;
198+ let store_icons = config. theme . show_icons ;
199+ let paths;
200+
201+ #[ cfg( target_os = "macos" ) ]
202+ {
203+ let user_local_path = std:: env:: var ( "HOME" ) . unwrap ( ) + "/Applications/" ;
204+ paths = vec ! [
205+ "/Applications/" ,
206+ user_local_path. as_str( ) ,
207+ "/System/Applications/" ,
208+ "/System/Applications/Utilities/" ,
209+ ] ;
210+ }
211+ #[ cfg( target_os = "windows" ) ]
212+ {
213+ paths = vec ! [ "C:\\ Program Files\\ " , "C:\\ Program Files (x86)\\ " ] ;
214+ }
176215
177216 let mut options: Vec < App > = paths
178217 . par_iter ( )
179- . map ( |path| get_installed_apps ( path, store_icons) )
218+ . map ( |path| {
219+ #[ cfg( target_os = "macos" ) ]
220+ {
221+ get_installed_apps ( path, store_icons)
222+ }
223+ #[ cfg( target_os = "windows" ) ]
224+ {
225+ get_installed_windows_app ( Path :: new ( path) )
226+ }
227+ } )
180228 . flatten ( )
181229 . collect ( ) ;
182230
@@ -206,7 +254,11 @@ impl Tile {
206254 match message {
207255 Message :: OpenWindow => {
208256 self . capture_frontmost ( ) ;
209- focus_this_app ( ) ;
257+ #[ cfg( target_os = "macos" ) ]
258+ {
259+ focus_this_app ( ) ;
260+ }
261+
210262 self . focused = true ;
211263 Task :: none ( )
212264 }
@@ -272,16 +324,13 @@ impl Tile {
272324 Message :: ClearSearchQuery => {
273325 self . query_lc = String :: new ( ) ;
274326 self . query = String :: new ( ) ;
327+ self . prev_query_lc = String :: new ( ) ;
275328 Task :: none ( )
276329 }
277330
278331 Message :: ReloadConfig => {
279332 self . config = toml:: from_str (
280- & fs:: read_to_string (
281- std:: env:: var ( "HOME" ) . unwrap_or ( "" . to_owned ( ) )
282- + "/.config/rustcast/config.toml" ,
283- )
284- . unwrap_or ( "" . to_owned ( ) ) ,
333+ & fs:: read_to_string ( get_config_file_path ( ) ) . unwrap_or ( "" . to_owned ( ) ) ,
285334 )
286335 . unwrap ( ) ;
287336
@@ -292,12 +341,29 @@ impl Tile {
292341 if hk_id == self . open_hotkey_id {
293342 self . visible = !self . visible ;
294343 if self . visible {
295- Task :: chain (
296- window:: open ( default_settings ( ) )
297- . 1
298- . map ( |_| Message :: OpenWindow ) ,
299- operation:: focus ( "query" ) ,
300- )
344+ #[ cfg( target_os = "windows" ) ]
345+ {
346+ // get normal settings and modify position
347+ use crate :: utils:: open_on_focused_monitor;
348+ use iced:: window:: Position :: Specific ;
349+ let pos = open_on_focused_monitor ( ) ;
350+ let mut settings = default_settings ( ) ;
351+ settings. position = Specific ( pos) ;
352+ Task :: chain (
353+ window:: open ( settings) . 1 . map ( |_| Message :: OpenWindow ) ,
354+ operation:: focus ( "query" ) ,
355+ )
356+ }
357+
358+ #[ cfg( target_os = "macos" ) ]
359+ {
360+ Task :: chain (
361+ window:: open ( default_settings ( ) )
362+ . 1
363+ . map ( |_| Message :: OpenWindow ) ,
364+ operation:: focus ( "query" ) ,
365+ )
366+ }
301367 } else {
302368 let to_close = window:: latest ( ) . map ( |x| x. unwrap ( ) ) ;
303369 Task :: batch ( [
@@ -445,18 +511,38 @@ impl Tile {
445511 }
446512
447513 pub fn capture_frontmost ( & mut self ) {
448- use objc2_app_kit:: NSWorkspace ;
514+ #[ cfg( target_os = "macos" ) ]
515+ {
516+ use objc2_app_kit:: NSWorkspace ;
517+
518+ let ws = NSWorkspace :: sharedWorkspace ( ) ;
519+ self . frontmost = ws. frontmostApplication ( ) ;
520+ } ;
449521
450- let ws = NSWorkspace :: sharedWorkspace ( ) ;
451- self . frontmost = ws. frontmostApplication ( ) ;
522+ #[ cfg( target_os = "windows" ) ]
523+ {
524+ self . frontmost = Some ( unsafe { GetForegroundWindow ( ) } ) ;
525+ }
452526 }
453527
454528 #[ allow( deprecated) ]
455529 pub fn restore_frontmost ( & mut self ) {
456- use objc2_app_kit:: NSApplicationActivationOptions ;
530+ #[ cfg( target_os = "macos" ) ]
531+ {
532+ use objc2_app_kit:: NSApplicationActivationOptions ;
533+
534+ if let Some ( app) = self . frontmost . take ( ) {
535+ app. activateWithOptions ( NSApplicationActivationOptions :: ActivateIgnoringOtherApps ) ;
536+ }
537+ }
457538
458- if let Some ( app) = self . frontmost . take ( ) {
459- app. activateWithOptions ( NSApplicationActivationOptions :: ActivateIgnoringOtherApps ) ;
539+ #[ cfg( target_os = "windows" ) ]
540+ {
541+ if let Some ( handle) = self . frontmost {
542+ unsafe {
543+ let _ = SetForegroundWindow ( handle) ;
544+ }
545+ }
460546 }
461547 }
462548}
@@ -494,3 +580,54 @@ fn handle_hotkeys() -> impl futures::Stream<Item = Message> {
494580 }
495581 } )
496582}
583+
584+ fn get_installed_windows_app ( path : & Path ) -> Vec < App > {
585+ use std:: ffi:: OsString ;
586+
587+ let mut apps = Vec :: new ( ) ;
588+
589+ let hkey = winreg:: RegKey :: predef ( winreg:: enums:: HKEY_LOCAL_MACHINE ) ;
590+
591+ // where we can find installed applications
592+ // src: https://stackoverflow.com/questions/2864984/how-to-programatically-get-the-list-of-installed-programs/2892848#2892848
593+ let registers = [
594+ hkey. open_subkey ( "SOFTWARE\\ Microsoft\\ Windows\\ CurrentVersion\\ Uninstall" )
595+ . unwrap ( ) ,
596+ hkey. open_subkey ( "SOFTWARE\\ Wow6432Node\\ Microsoft\\ Windows\\ CurrentVersion\\ Uninstall" )
597+ . unwrap ( ) ,
598+ ] ;
599+
600+ registers. iter ( ) . for_each ( |reg| {
601+ reg. enum_keys ( ) . for_each ( |key| {
602+ // https://learn.microsoft.com/en-us/windows/win32/msi/uninstall-registry-key
603+ let name = key. unwrap ( ) ;
604+ let key = reg. open_subkey ( & name) . unwrap ( ) ;
605+ let display_name = key. get_value ( "DisplayName" ) . unwrap_or ( OsString :: new ( ) ) ;
606+
607+ // they might be useful one day ?
608+ // let publisher = key.get_value("Publisher").unwrap_or(OsString::new());
609+ // let version = key.get_value("DisplayVersion").unwrap_or(OsString::new());
610+
611+ // Trick, I saw on internet to point to the exe location..
612+ let exe_path = key. get_value ( "DisplayIcon" ) . unwrap_or ( OsString :: new ( ) ) ;
613+ if exe_path. is_empty ( ) {
614+ return ;
615+ }
616+ // if there is something, it will be in the form of
617+ // "C:\Program Files\Microsoft Office\Office16\WINWORD.EXE",0
618+ let exe_path = exe_path. to_string_lossy ( ) . to_string ( ) ;
619+ let exe = exe_path. split ( "," ) . next ( ) . unwrap ( ) . to_string ( ) ;
620+
621+ if !display_name. is_empty ( ) {
622+ apps. push ( App {
623+ open_command : Function :: OpenApp ( exe) ,
624+ name : display_name. clone ( ) . into_string ( ) . unwrap ( ) ,
625+ name_lc : display_name. clone ( ) . into_string ( ) . unwrap ( ) . to_lowercase ( ) ,
626+ icons : None ,
627+ } )
628+ }
629+ } ) ;
630+ } ) ;
631+
632+ apps
633+ }
0 commit comments