11use accessibility:: AXUIElement ;
2+ use accessibility:: AXUIElementAttributes ;
23use accessibility_sys:: AXError ;
34use accessibility_sys:: AXObserverGetTypeID ;
45use accessibility_sys:: AXObserverRef ;
56use accessibility_sys:: AXUIElementRef ;
67use accessibility_sys:: kAXErrorSuccess;
8+ use accessibility_sys:: kAXWindowRole;
79use accessibility_sys:: pid_t;
810use anyhow:: Context ;
911use anyhow:: anyhow;
12+ use core_foundation:: base:: CFIndexConvertible ;
1013use core_foundation:: base:: OSStatus ;
1114use core_foundation:: base:: TCFType ;
15+ use core_foundation:: data:: CFData ;
16+ use core_foundation:: data:: CFDataCreate ;
17+ use core_foundation:: data:: CFDataRef ;
1218use core_foundation:: declare_TCFType;
1319use core_foundation:: impl_CFTypeDescription;
1420use core_foundation:: impl_TCFType;
15- use objc2_core_graphics:: CGError ;
16- use objc2_core_graphics:: CGWindowID ;
21+ use core_graphics:: base:: kCGErrorSuccess;
22+ use core_graphics:: display:: CGError ;
23+ use core_graphics:: window:: CGWindowID ;
1724
1825declare_TCFType ! ( AXObserver , AXObserverRef ) ;
1926impl_TCFType ! ( AXObserver , AXObserverRef , AXObserverGetTypeID ) ;
@@ -25,6 +32,13 @@ unsafe extern "C" {
2532 fn GetProcessForPID ( pid : pid_t , psn : * mut ProcessSerialNumber ) -> OSStatus ;
2633}
2734
35+ #[ link( name = "SkyLight" , kind = "framework" ) ] // PrivateFrameworks included in build.rs
36+ unsafe extern "C" {
37+ fn _SLPSSetFrontProcessWithOptions ( psn : * const ProcessSerialNumber , wid : CGWindowID , mode : u32 ) -> CGError ;
38+ fn SLPSPostEventRecordTo ( psn : * const ProcessSerialNumber , bytes : * const u8 ) -> CGError ;
39+ pub fn _AXUIElementCreateWithRemoteToken ( data : CFDataRef ) -> AXUIElementRef ;
40+ }
41+
2842#[ repr( C ) ]
2943#[ derive( Default ) ]
3044struct ProcessSerialNumber {
@@ -47,11 +61,9 @@ pub fn make_key_window(pid: pid_t, window: &AXUIElement) -> anyhow::Result<()> {
4761 // See https://github.com/Hammerspoon/hammerspoon/issues/370#issuecomment-545545468.
4862 // god bless all the wizards in that thread that worked on it, thank you
4963
50- let mut window_id = 0 ;
51- let res = unsafe { _AXUIElementGetWindow ( window. as_concrete_TypeRef ( ) , & mut window_id) } ;
52- if res != kAXErrorSuccess {
53- return Err ( accessibility:: Error :: Ax ( res) ) . context ( format ! ( "Failed to get window id for element {:?}" , window) ) ;
54- }
64+ let window_id = ax_window_id ( window) ?;
65+
66+ println ! ( "window id: {}" , window_id) ;
5567
5668 #[ allow( non_upper_case_globals) ]
5769 const kCPSUserGenerated: u32 = 0x200 ;
@@ -69,7 +81,7 @@ pub fn make_key_window(pid: pid_t, window: &AXUIElement) -> anyhow::Result<()> {
6981 let psn = ProcessSerialNumber :: for_pid ( pid) ?;
7082
7183 let check = |err| {
72- if err == CGError :: Success {
84+ if err == kCGErrorSuccess {
7385 Ok ( ( ) )
7486 } else {
7587 Err ( anyhow:: anyhow!( "Cursed api failed: {:?}" , err) )
@@ -83,8 +95,46 @@ pub fn make_key_window(pid: pid_t, window: &AXUIElement) -> anyhow::Result<()> {
8395 Ok ( ( ) )
8496}
8597
86- #[ link( name = "SkyLight" , kind = "framework" ) ] // PrivateFrameworks included in build.rs
87- unsafe extern "C" {
88- fn _SLPSSetFrontProcessWithOptions ( psn : * const ProcessSerialNumber , wid : u32 , mode : u32 ) -> CGError ;
89- fn SLPSPostEventRecordTo ( psn : * const ProcessSerialNumber , bytes : * const u8 ) -> CGError ;
98+ pub fn bruteforce_windows_for_app ( app_pid : pid_t ) -> Vec < AXUIElement > {
99+ unsafe {
100+ let mut result = vec ! [ ] ;
101+ let mut data = [ 0 ; 0x14 ] ;
102+
103+ let app_pid = u32:: to_ne_bytes ( app_pid as u32 ) ;
104+ data[ 0 ..0x4 ] . copy_from_slice ( & app_pid) ;
105+
106+ let magic = u32:: to_ne_bytes ( 0x636f636f ) ;
107+ data[ 0x8 ..0xC ] . copy_from_slice ( & magic) ;
108+
109+ for element_id in 0 ..0x7fffu64 {
110+ let mut data = data. clone ( ) ;
111+
112+ let element_id = element_id. to_ne_bytes ( ) ;
113+ data[ 0xC ..0x14 ] . copy_from_slice ( & element_id) ;
114+
115+ let data_ref = CFDataCreate ( std:: ptr:: null ( ) , data. as_ptr ( ) , data. len ( ) . to_CFIndex ( ) ) ;
116+ let data_ref = CFData :: wrap_under_create_rule ( data_ref) ;
117+ let data_ref = data_ref. as_concrete_TypeRef ( ) ;
118+
119+ let window = AXUIElement :: wrap_under_create_rule ( _AXUIElementCreateWithRemoteToken ( data_ref) ) ;
120+
121+ let role = window. role ( ) . ok ( ) . map ( |role| role. to_string ( ) ) ;
122+ if role. as_deref ( ) != Some ( kAXWindowRole) {
123+ continue ;
124+ }
125+
126+ result. push ( window) ;
127+ }
128+
129+ result
130+ }
131+ }
132+
133+ pub fn ax_window_id ( window : & AXUIElement ) -> anyhow:: Result < CGWindowID > {
134+ let mut window_id = 0 ;
135+ let res = unsafe { _AXUIElementGetWindow ( window. as_concrete_TypeRef ( ) , & mut window_id) } ;
136+ if res != kAXErrorSuccess {
137+ return Err ( accessibility:: Error :: Ax ( res) ) . context ( format ! ( "Failed to get window id for element {:?}" , window) ) ;
138+ }
139+ Ok ( window_id)
90140}
0 commit comments