1#![cfg_attr(docsrs, feature(doc_cfg))]
61#![cfg_attr(feature = "doc", doc = document_features::document_features!())]
62
63use ::windows::{
64 Win32::{
65 Foundation::{FALSE, HWND, LPARAM, TRUE, WPARAM},
66 System::Threading::GetCurrentThreadId,
67 UI::WindowsAndMessaging::{
68 EnumThreadWindows, FindWindowW, GetClassNameW, SendMessageW, WM_USER,
69 },
70 },
71 core::{BOOL, PCWSTR},
72};
73use tracing::debug;
74use widestring::{U16Str, u16str};
75
76use crate::wm::{
77 EVERYTHING_IPC_GET_MINOR_VERSION, EVERYTHING_IPC_IS_DB_LOADED,
78 EVERYTHING_IPC_IS_FILE_INFO_INDEXED,
79};
80
81#[cfg(feature = "folder")]
82pub mod folder;
83#[cfg(feature = "tokio")]
84pub mod pipe;
85pub mod search;
86mod windows;
87pub mod wm;
88
89const IPC_CLASS_PREFIX: &U16Str = u16str!("EVERYTHING_TASKBAR_NOTIFICATION");
90
91const EVERYTHING_WM_IPC: u32 = WM_USER;
92
93struct EnumWindowsData {
94 result: Option<IpcWindow>,
95}
96
97unsafe extern "system" fn enum_windows_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
98 let data = unsafe { &mut *(lparam.0 as *mut EnumWindowsData) };
99
100 let mut buf = [0; 256];
101 let len = unsafe { GetClassNameW(hwnd, &mut buf) };
102 if len > 0 {
103 let class_name = U16Str::from_slice(&buf[..len as usize]);
104 if class_name
106 .as_slice()
107 .starts_with(IPC_CLASS_PREFIX.as_slice())
108 {
109 data.result = Some(IpcWindow {
110 hwnd,
111 class_name: class_name.to_string().unwrap(),
112 });
113 return FALSE;
114 }
115 }
116
117 TRUE
118}
119
120#[derive(Debug, Clone)]
130pub struct IpcWindow {
131 hwnd: HWND,
132 class_name: String,
133}
134
135impl IpcWindow {
136 pub fn new() -> Option<Self> {
138 match Self::with_instance(None) {
139 Some(window) => Some(window),
140 None => Self::with_instance(Some("1.5a")),
141 }
142 }
143
144 pub fn with_instance(instance_name: Option<&str>) -> Option<Self> {
146 let mut class_name = Vec::<u16>::with_capacity(IPC_CLASS_PREFIX.len() + 7);
147 class_name.extend_from_slice(IPC_CLASS_PREFIX.as_slice());
148
149 if let Some(name) = instance_name {
150 class_name.push('(' as u16);
151 for ch in name.encode_utf16() {
152 class_name.push(ch);
153 }
154 class_name.push(')' as u16);
155 }
156 class_name.push(0);
158
159 let hwnd = unsafe { FindWindowW(PCWSTR(class_name.as_ptr()), None).ok() }?;
160 if hwnd.is_invalid() {
161 return None;
162 }
163
164 Some(IpcWindow {
165 hwnd,
166 class_name: String::from_utf16_lossy(&class_name[..class_name.len() - 1]),
167 })
168 }
169
170 pub fn from_current_thread() -> Option<Self> {
172 let mut data = EnumWindowsData { result: None };
173
174 let tid = unsafe { GetCurrentThreadId() };
175 debug!(?tid, "from_current_thread");
176 _ = unsafe {
177 EnumThreadWindows(
178 tid,
179 Some(enum_windows_proc),
180 LPARAM(&mut data as *mut _ as isize),
181 )
182 };
183
184 data.result
185 }
186
187 pub fn hwnd(&self) -> HWND {
189 self.hwnd
190 }
191
192 pub fn class_name(&self) -> &str {
194 &self.class_name
195 }
196
197 pub fn instance_name(&self) -> Option<&str> {
198 self.class_name
200 .strip_prefix("EVERYTHING_TASKBAR_NOTIFICATION_(")
201 .and_then(|s| s.strip_suffix(')'))
202 }
203
204 pub fn is_ipc_available(&self) -> bool {
206 unsafe {
207 SendMessageW(
208 self.hwnd,
209 EVERYTHING_WM_IPC,
210 Some(WPARAM(EVERYTHING_IPC_GET_MINOR_VERSION as usize)),
211 None,
212 )
213 }
214 .0 >= 4
215 }
216
217 pub fn get_version(&self) -> Version {
219 const EVERYTHING_IPC_GET_MAJOR_VERSION: u32 = 0;
220 const EVERYTHING_IPC_GET_MINOR_VERSION: u32 = 1;
221 const EVERYTHING_IPC_GET_REVISION: u32 = 2;
222 const EVERYTHING_IPC_GET_BUILD_NUMBER: u32 = 3;
223 let send_u32 = |command: u32| unsafe {
226 SendMessageW(
227 self.hwnd,
228 EVERYTHING_WM_IPC,
229 Some(WPARAM(command as usize)),
230 None,
231 )
232 .0 as u32
233 };
234
235 Version {
236 major: send_u32(EVERYTHING_IPC_GET_MAJOR_VERSION),
237 minor: send_u32(EVERYTHING_IPC_GET_MINOR_VERSION),
238 revision: send_u32(EVERYTHING_IPC_GET_REVISION),
239 build: send_u32(EVERYTHING_IPC_GET_BUILD_NUMBER),
240 }
241 }
242
243 pub fn is_db_loaded(&self) -> bool {
245 unsafe {
246 SendMessageW(
247 self.hwnd,
248 EVERYTHING_WM_IPC,
249 Some(WPARAM(EVERYTHING_IPC_IS_DB_LOADED as usize)),
250 Some(LPARAM(0)),
251 )
252 }
253 .0 != 0
254 }
255
256 pub fn is_file_info_indexed(&self, info: wm::FileInfo) -> bool {
258 unsafe {
259 SendMessageW(
260 self.hwnd,
261 EVERYTHING_WM_IPC,
262 Some(WPARAM(EVERYTHING_IPC_IS_FILE_INFO_INDEXED as usize)),
263 Some(LPARAM(info as isize)),
264 )
265 }
266 .0 != 0
267 }
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
271pub struct Version {
272 pub major: u32,
273 pub minor: u32,
274 pub revision: u32,
275 pub build: u32,
276}
277
278impl Version {
279 pub fn new(major: u32, minor: u32, revision: u32, build: u32) -> Self {
280 Self {
281 major,
282 minor,
283 revision,
284 build,
285 }
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use crate::wm::FileInfo;
292
293 use super::*;
294
295 #[test]
296 fn ipc_window_find_global() {
297 let window = IpcWindow::with_instance(None);
298 assert!(window.is_some(), "Should find Everything IPC window");
299
300 let window = window.unwrap();
301 let class_name = window.class_name();
302 println!("IPC Window class name: {}", class_name);
303 assert!(
305 class_name == "EVERYTHING_TASKBAR_NOTIFICATION"
306 || class_name.starts_with("EVERYTHING_TASKBAR_NOTIFICATION_(")
307 );
308
309 let version = window.get_version();
310 println!(
311 "Everything version: {}.{}.{}.{}",
312 version.major, version.minor, version.revision, version.build
313 );
314 }
315
316 #[test]
317 fn get_version() {
318 let everything = IpcWindow::new().unwrap();
319 let version = everything.get_version();
320
321 assert!(version.major >= 1, "Expected Everything v1+");
323 println!(
324 "Version: {}.{}.{}.{}",
325 version.major, version.minor, version.revision, version.build
326 );
327 }
328
329 #[test]
330 fn is_db_loaded() {
331 let everything = IpcWindow::new().unwrap();
332 let loaded = everything.is_db_loaded();
333 assert!(loaded, "Everything DB should be loaded");
334 }
335
336 #[test]
337 fn is_file_info_indexed() {
338 let everything = IpcWindow::new().unwrap();
339
340 let _ = everything.is_file_info_indexed(FileInfo::FileSize);
342 let _ = everything.is_file_info_indexed(FileInfo::FolderSize);
343 let _ = everything.is_file_info_indexed(FileInfo::DateCreated);
344 let _ = everything.is_file_info_indexed(FileInfo::DateModified);
345 let _ = everything.is_file_info_indexed(FileInfo::DateAccessed);
346 let _ = everything.is_file_info_indexed(FileInfo::Attributes);
347 }
348}