#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(feature = "doc", doc = document_features::document_features!())]
use ::windows::{
Win32::{
Foundation::{FALSE, HWND, LPARAM, TRUE, WPARAM},
System::Threading::GetCurrentThreadId,
UI::WindowsAndMessaging::{
EnumThreadWindows, FindWindowW, GetClassNameW, SendMessageW, WM_USER,
},
},
core::{BOOL, PCWSTR},
};
use tracing::debug;
use widestring::{U16Str, u16str};
use crate::wm::{
EVERYTHING_IPC_GET_MINOR_VERSION, EVERYTHING_IPC_IS_DB_LOADED,
EVERYTHING_IPC_IS_FILE_INFO_INDEXED,
};
#[cfg(feature = "folder")]
pub mod folder;
#[cfg(feature = "tokio")]
pub mod pipe;
pub mod search;
mod windows;
pub mod wm;
const IPC_CLASS_PREFIX: &U16Str = u16str!("EVERYTHING_TASKBAR_NOTIFICATION");
const EVERYTHING_WM_IPC: u32 = WM_USER;
struct EnumWindowsData {
result: Option<IpcWindow>,
}
unsafe extern "system" fn enum_windows_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
let data = unsafe { &mut *(lparam.0 as *mut EnumWindowsData) };
let mut buf = [0; 256];
let len = unsafe { GetClassNameW(hwnd, &mut buf) };
if len > 0 {
let class_name = U16Str::from_slice(&buf[..len as usize]);
if class_name
.as_slice()
.starts_with(IPC_CLASS_PREFIX.as_slice())
{
data.result = Some(IpcWindow {
hwnd,
class_name: class_name.to_string().unwrap(),
});
return FALSE;
}
}
TRUE
}
#[derive(Debug, Clone)]
pub struct IpcWindow {
hwnd: HWND,
class_name: String,
}
impl IpcWindow {
pub fn new() -> Option<Self> {
match Self::with_instance(None) {
Some(window) => Some(window),
None => Self::with_instance(Some("1.5a")),
}
}
pub fn with_instance(instance_name: Option<&str>) -> Option<Self> {
let mut class_name = Vec::<u16>::with_capacity(IPC_CLASS_PREFIX.len() + 7);
class_name.extend_from_slice(IPC_CLASS_PREFIX.as_slice());
if let Some(name) = instance_name {
class_name.push('(' as u16);
for ch in name.encode_utf16() {
class_name.push(ch);
}
class_name.push(')' as u16);
}
class_name.push(0);
let hwnd = unsafe { FindWindowW(PCWSTR(class_name.as_ptr()), None).ok() }?;
if hwnd.is_invalid() {
return None;
}
Some(IpcWindow {
hwnd,
class_name: String::from_utf16_lossy(&class_name[..class_name.len() - 1]),
})
}
pub fn from_current_thread() -> Option<Self> {
let mut data = EnumWindowsData { result: None };
let tid = unsafe { GetCurrentThreadId() };
debug!(?tid, "from_current_thread");
_ = unsafe {
EnumThreadWindows(
tid,
Some(enum_windows_proc),
LPARAM(&mut data as *mut _ as isize),
)
};
data.result
}
pub fn hwnd(&self) -> HWND {
self.hwnd
}
pub fn class_name(&self) -> &str {
&self.class_name
}
pub fn instance_name(&self) -> Option<&str> {
self.class_name
.strip_prefix("EVERYTHING_TASKBAR_NOTIFICATION_(")
.and_then(|s| s.strip_suffix(')'))
}
pub fn is_ipc_available(&self) -> bool {
unsafe {
SendMessageW(
self.hwnd,
EVERYTHING_WM_IPC,
Some(WPARAM(EVERYTHING_IPC_GET_MINOR_VERSION as usize)),
None,
)
}
.0 >= 4
}
pub fn get_version(&self) -> Version {
const EVERYTHING_IPC_GET_MAJOR_VERSION: u32 = 0;
const EVERYTHING_IPC_GET_MINOR_VERSION: u32 = 1;
const EVERYTHING_IPC_GET_REVISION: u32 = 2;
const EVERYTHING_IPC_GET_BUILD_NUMBER: u32 = 3;
let send_u32 = |command: u32| unsafe {
SendMessageW(
self.hwnd,
EVERYTHING_WM_IPC,
Some(WPARAM(command as usize)),
None,
)
.0 as u32
};
Version {
major: send_u32(EVERYTHING_IPC_GET_MAJOR_VERSION),
minor: send_u32(EVERYTHING_IPC_GET_MINOR_VERSION),
revision: send_u32(EVERYTHING_IPC_GET_REVISION),
build: send_u32(EVERYTHING_IPC_GET_BUILD_NUMBER),
}
}
pub fn is_db_loaded(&self) -> bool {
unsafe {
SendMessageW(
self.hwnd,
EVERYTHING_WM_IPC,
Some(WPARAM(EVERYTHING_IPC_IS_DB_LOADED as usize)),
Some(LPARAM(0)),
)
}
.0 != 0
}
pub fn is_file_info_indexed(&self, info: wm::FileInfo) -> bool {
unsafe {
SendMessageW(
self.hwnd,
EVERYTHING_WM_IPC,
Some(WPARAM(EVERYTHING_IPC_IS_FILE_INFO_INDEXED as usize)),
Some(LPARAM(info as isize)),
)
}
.0 != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Version {
pub major: u32,
pub minor: u32,
pub revision: u32,
pub build: u32,
}
impl Version {
pub fn new(major: u32, minor: u32, revision: u32, build: u32) -> Self {
Self {
major,
minor,
revision,
build,
}
}
}
#[cfg(test)]
mod tests {
use crate::wm::FileInfo;
use super::*;
#[test]
fn ipc_window_find_global() {
let window = IpcWindow::with_instance(None);
assert!(window.is_some(), "Should find Everything IPC window");
let window = window.unwrap();
let class_name = window.class_name();
println!("IPC Window class name: {}", class_name);
assert!(
class_name == "EVERYTHING_TASKBAR_NOTIFICATION"
|| class_name.starts_with("EVERYTHING_TASKBAR_NOTIFICATION_(")
);
let version = window.get_version();
println!(
"Everything version: {}.{}.{}.{}",
version.major, version.minor, version.revision, version.build
);
}
#[test]
fn get_version() {
let everything = IpcWindow::new().unwrap();
let version = everything.get_version();
assert!(version.major >= 1, "Expected Everything v1+");
println!(
"Version: {}.{}.{}.{}",
version.major, version.minor, version.revision, version.build
);
}
#[test]
fn is_db_loaded() {
let everything = IpcWindow::new().unwrap();
let loaded = everything.is_db_loaded();
assert!(loaded, "Everything DB should be loaded");
}
#[test]
fn is_file_info_indexed() {
let everything = IpcWindow::new().unwrap();
let _ = everything.is_file_info_indexed(FileInfo::FileSize);
let _ = everything.is_file_info_indexed(FileInfo::FolderSize);
let _ = everything.is_file_info_indexed(FileInfo::DateCreated);
let _ = everything.is_file_info_indexed(FileInfo::DateModified);
let _ = everything.is_file_info_indexed(FileInfo::DateAccessed);
let _ = everything.is_file_info_indexed(FileInfo::Attributes);
}
}