diff --git a/Cargo.toml b/Cargo.toml index 4c2e354..3d524c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "Underlayer" -version = "0.1.0" +version = "0.1.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/lib.rs b/src/lib.rs index 607d47c..258cc71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,10 @@ pub enum UnderlayEvent { X11FullscreenEvent{ is_fullscreen: bool } } +pub fn focus_target() { + platform_impl::focus_target(); +} + pub fn register>(window_title: T) -> Receiver { let (tx,rx) = std::sync::mpsc::channel(); diff --git a/src/linux/mod.rs b/src/linux/mod.rs index ae05930..e8ad806 100644 --- a/src/linux/mod.rs +++ b/src/linux/mod.rs @@ -1,8 +1,10 @@ +use std::sync::{atomic::AtomicPtr, Arc, Mutex}; + use x11rb::{ connect, connection::Connection, protocol::{ - xproto::{Atom, AtomEnum, ChangeWindowAttributesAux, ConnectionExt, EventMask, Window}, + xproto::{Atom, AtomEnum, ChangeWindowAttributesAux, ConnectionExt, EventMask, Window, InputFocus}, Event, }, rust_connection::RustConnection, @@ -12,6 +14,10 @@ use crate::{Bounds, UnderlayEvent}; const WM_NAME_BUFSIZE: u32 = 512; + +static TARGET: AtomicPtr>> = + AtomicPtr::new(std::ptr::null_mut()); + struct Target { title: String, wid: Window, @@ -53,6 +59,20 @@ impl AtomCollection { } } +pub fn focus_target() { + unsafe { + if let Ok(target) = + (*TARGET.load(std::sync::atomic::Ordering::Relaxed)).lock() + { + if *target != x11rb::NONE { + if let Ok((conn, _)) = connect(None) { + conn.set_input_focus(InputFocus::PARENT, *target, x11rb::CURRENT_TIME).ok(); + } + } + } + } +} + struct Runtime { conn: RustConnection, root: Window, @@ -64,7 +84,8 @@ struct Runtime { impl Runtime { fn get_active_window(&self) -> Option { - let cookie = self.conn + let cookie = self + .conn .get_property( false, self.root, @@ -74,14 +95,26 @@ impl Runtime { 1, ) .ok()?; - + let reply = cookie.reply().ok()?; - + let mut value32_iter = reply.value32()?; - + value32_iter.next() } + ///Using this instead to allow setting the globally accessible target too for focus_target. + fn set_target(&mut self, wid: Window) { + self.target.wid = wid; + unsafe { + if let Ok(mut target) = + (*TARGET.load(std::sync::atomic::Ordering::Relaxed)).lock() + { + *target = wid; + } + } + } + fn get_title(&self, wid: Window) -> Option { if wid == x11rb::NONE { return None; @@ -187,16 +220,21 @@ impl Runtime { } if self.target.is_destroyed { - self.target.wid = x11rb::NONE; + self.set_target(x11rb::NONE); self.target.is_destroyed = false; self.underlay_tx.send(UnderlayEvent::Detach).ok(); } } - log::trace!("got title: {}", self.get_title(wid).unwrap_or("ERROR".into())); + log::trace!( + "got title: {}", + self.get_title(wid).unwrap_or("ERROR".into()) + ); match self.get_title(wid) { Some(title) => { - if title != self.target.title {return} + if title != self.target.title { + return; + } } _ => return, } @@ -212,7 +250,7 @@ impl Runtime { .ok(); } - self.target.wid = wid; + self.set_target(wid); self.conn .change_window_attributes( @@ -241,7 +279,7 @@ impl Runtime { } } - self.target.wid = x11rb::NONE; + self.set_target(x11rb::NONE); } fn handle_event(&mut self, event: Event) { @@ -340,14 +378,19 @@ pub fn init( runtime.active = runtime.get_active_window().unwrap_or(x11rb::NONE); if runtime.active != x11rb::NONE { - runtime.conn.change_window_attributes(runtime.active, &ChangeWindowAttributesAux::new().event_mask(EventMask::PROPERTY_CHANGE)).ok(); + runtime + .conn + .change_window_attributes( + runtime.active, + &ChangeWindowAttributesAux::new().event_mask(EventMask::PROPERTY_CHANGE), + ) + .ok(); runtime.check_and_handle_window(runtime.active); } runtime.conn.flush().ok(); log::trace!("Initialized, listning for events"); - while let Ok(event) = runtime.conn.wait_for_event() { log::trace!("Got event"); runtime.handle_event(event); diff --git a/src/windows/mod.rs b/src/windows/mod.rs index b93199e..6dadac2 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -1,9 +1,7 @@ use windows::Win32::Graphics::Gdi::ClientToScreen; use windows::Win32::System::Com::VT_I4; use windows::Win32::System::Ole::{VariantClear, VariantInit}; -use windows::Win32::UI::Accessibility::{ - AccessibleObjectFromEvent, IAccessible, -}; +use windows::Win32::UI::Accessibility::{AccessibleObjectFromEvent, IAccessible}; use std::sync::atomic::AtomicPtr; use std::sync::{Arc, Mutex}; @@ -14,9 +12,10 @@ use windows::Win32::Foundation::{ use windows::Win32::UI::WindowsAndMessaging::{ DispatchMessageW, GetClientRect, GetForegroundWindow, GetMessageW, GetWindowTextW, GetWindowThreadProcessId, IsHungAppWindow, PostMessageW, RegisterWindowMessageW, - TranslateMessage, CHILDID_SELF, EVENT_OBJECT_DESTROY, EVENT_OBJECT_LOCATIONCHANGE, - EVENT_OBJECT_NAMECHANGE, EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG, - OBJECT_IDENTIFIER, OBJID_WINDOW, STATE_SYSTEM_FOCUSED, WINEVENT_OUTOFCONTEXT, SetTimer, + SetForegroundWindow, SetTimer, TranslateMessage, CHILDID_SELF, EVENT_OBJECT_DESTROY, + EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_NAMECHANGE, EVENT_SYSTEM_FOREGROUND, + EVENT_SYSTEM_MINIMIZEEND, MSG, OBJECT_IDENTIFIER, OBJID_WINDOW, STATE_SYSTEM_FOCUSED, + WINEVENT_OUTOFCONTEXT, }; use windows::Win32::{ Foundation::HWND, @@ -25,9 +24,21 @@ use windows::Win32::{ use crate::{Bounds, UnderlayEvent}; -static GLOBAL_CALLBACK_SENDER: AtomicPtr>> = +static GLOBAL_RUNTIME: AtomicPtr>> = AtomicPtr::new(std::ptr::null_mut()); +pub fn focus_target() { + unsafe { + if let Ok(runtime) = + (*GLOBAL_RUNTIME.load(std::sync::atomic::Ordering::Relaxed)).lock() + { + if runtime.target.hwnd.is_valid() { + SetForegroundWindow(runtime.target.hwnd); + } + } + } +} + fn constant_name(value: u32) -> String { match value { EVENT_OBJECT_DESTROY => "EVENT_OBJECT_DESTROY".into(), @@ -171,7 +182,7 @@ unsafe extern "system" fn global_hook( _dwmseventtime: u32, ) { if let Ok(mut runtime) = - (*GLOBAL_CALLBACK_SENDER.load(std::sync::atomic::Ordering::Relaxed)).lock() + (*GLOBAL_RUNTIME.load(std::sync::atomic::Ordering::Relaxed)).lock() { runtime.on_event(Event::from_data( event, @@ -189,7 +200,7 @@ unsafe extern "system" fn global_timer_hook( _dwms_event_time: u32, ) { if let Ok(mut runtime) = - (*GLOBAL_CALLBACK_SENDER.load(std::sync::atomic::Ordering::Relaxed)).lock() + (*GLOBAL_RUNTIME.load(std::sync::atomic::Ordering::Relaxed)).lock() { let system_foreground_window = GetForegroundWindow(); if runtime.foreground_window != system_foreground_window @@ -373,7 +384,11 @@ impl Runtime { if let Some(bounds) = self.get_content_bounds() { self.underlay_tx - .send(UnderlayEvent::Attach { bounds, has_access: Some(has_access), is_fullscreen: None }) + .send(UnderlayEvent::Attach { + bounds, + has_access: Some(has_access), + is_fullscreen: None, + }) .ok(); self.target.is_focused = true; @@ -381,7 +396,6 @@ impl Runtime { } else { self.target.hwnd = Default::default(); } - } fn msaa_check_window_focused_state(&mut self, hwnd: HWND) -> bool { @@ -497,9 +511,16 @@ pub fn init( }; runtime.foreground_change.bind(); runtime.unminimize_change.bind(); - unsafe { SetTimer(HWND::default(), 0, OW_FOREGROUND_TIMER_MS, Some(global_timer_hook)) }; + unsafe { + SetTimer( + HWND::default(), + 0, + OW_FOREGROUND_TIMER_MS, + Some(global_timer_hook), + ) + }; - GLOBAL_CALLBACK_SENDER.store( + GLOBAL_RUNTIME.store( Box::into_raw(Box::new(Arc::new(Mutex::new(runtime)))), std::sync::atomic::Ordering::Relaxed, ); @@ -522,4 +543,4 @@ fn register_message>(message: T) -> u32 { let message_wide = windows::core::HSTRING::from(message.into()); let result = unsafe { RegisterWindowMessageW(PCWSTR(message_wide.as_wide().as_ptr())) }; result -} \ No newline at end of file +}