1#![allow(clippy::unused_self)] #[derive(Debug)]
8pub struct Api<'rofi> {
9 display_name: ptr::NonNull<*mut u8>,
10 display_name_len: usize,
12 display_name_capacity: usize,
13 lifetime: PhantomData<&'rofi ()>,
14}
15
16unsafe impl Send for Api<'_> {}
20unsafe impl Sync for Api<'_> {}
21
22impl Api<'_> {
23 pub(crate) unsafe fn new(display_name: ptr::NonNull<*mut u8>) -> Self {
24 Self {
25 display_name,
26 display_name_len: 0,
27 display_name_capacity: 0,
28 lifetime: PhantomData,
29 }
30 }
31
32 #[must_use]
39 pub fn display_name(&self) -> Option<&str> {
40 let ptr = *unsafe { self.display_name.as_ref() };
42
43 if ptr.is_null() {
44 return None;
45 }
46
47 let slice = unsafe { slice::from_raw_parts(ptr, self.display_name_len) };
48
49 Some(unsafe { str::from_utf8_unchecked(slice) })
50 }
51
52 fn change_display_name(&mut self, display_name: Option<String>) -> Option<String> {
53 let ptr = unsafe { self.display_name.as_mut() };
57
58 let old_len = self.display_name_len;
59 let old_capacity = self.display_name_capacity;
60 let old_ptr = *ptr;
61
62 if let Some(display_name) = &display_name {
63 self.display_name_len = display_name.len();
64 self.display_name_capacity = display_name.capacity();
65 }
66 *ptr = display_name.map_or_else(ptr::null_mut, String::into_raw);
67
68 if old_ptr.is_null() {
69 None
70 } else {
71 Some(unsafe { String::from_raw_parts(old_ptr, old_len, old_capacity) })
72 }
73 }
74
75 pub fn take_display_name(&mut self) -> Option<String> {
82 self.change_display_name(None)
83 }
84
85 pub fn replace_display_name(&mut self, display_name: String) -> Option<String> {
90 self.change_display_name(Some(display_name))
91 }
92
93 pub fn set_display_name<T: Display>(&mut self, display_name: T) {
99 let mut buf = self.take_display_name().unwrap_or_default();
100 buf.clear();
101 write!(buf, "{display_name}").unwrap();
102 self.replace_display_name(buf);
103 }
104
105 #[must_use]
108 pub fn supports_image<P: AsRef<Path>>(&self, path: P) -> bool {
109 let mut path = path.as_ref().as_os_str().as_bytes().to_owned();
110 path.push(b'\0');
111
112 let res = unsafe { ffi::icon_fetcher::file_is_image(path.as_ptr().cast()) };
113
114 res != 0
115 }
116
117 #[must_use]
125 pub fn query_icon(&mut self, name: &str, size: u32) -> IconRequest {
126 let name = CString::new(name).expect("name contained nul bytes");
127 self.query_icon_cstr(&name, size)
128 }
129
130 #[must_use]
134 pub fn query_icon_cstr(&mut self, name: &CStr, size: u32) -> IconRequest {
135 let uid = unsafe {
136 ffi::icon_fetcher::query(name.as_ptr(), size.try_into().unwrap_or(c_int::MAX))
137 };
138 IconRequest { uid }
139 }
140
141 #[must_use]
149 pub fn query_icon_wh(&mut self, name: &str, width: u32, height: u32) -> IconRequest {
150 let name = CString::new(name).expect("name contained nul bytes");
151 self.query_icon_wh_cstr(&name, width, height)
152 }
153
154 #[must_use]
158 pub fn query_icon_wh_cstr(&mut self, name: &CStr, width: u32, height: u32) -> IconRequest {
159 let uid = unsafe {
160 ffi::icon_fetcher::query_advanced(
161 name.as_ptr(),
162 width.try_into().unwrap_or(c_int::MAX),
163 height.try_into().unwrap_or(c_int::MAX),
164 )
165 };
166 IconRequest { uid }
167 }
168
169 #[allow(clippy::needless_pass_by_value)]
180 pub fn retrieve_icon(&mut self, request: IconRequest) -> Result<cairo::Surface, IconError> {
181 let ptr = unsafe { ffi::icon_fetcher::get(request.uid) };
182 if ptr.is_null() {
183 return Err(IconError::NotFound);
184 }
185 unsafe { cairo::Surface::from_raw_full(ptr) }.map_err(IconError::Surface)
186 }
187
188 pub fn hide(&mut self) {
193 unsafe { ffi::view::hide() };
194 }
195}
196
197#[derive(Debug)]
201pub struct IconRequest {
202 uid: u32,
203}
204
205impl IconRequest {
206 #[allow(clippy::missing_errors_doc)]
210 pub fn wait(self, api: &mut Api<'_>) -> Result<cairo::Surface, IconError> {
211 api.retrieve_icon(self)
212 }
213}
214
215#[derive(Debug)]
217pub enum IconError {
218 #[non_exhaustive]
220 NotFound,
221 #[non_exhaustive]
223 Surface(cairo::Error),
224}
225
226impl Display for IconError {
227 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
228 f.write_str("failed to retrieve icon")
229 }
230}
231
232impl Error for IconError {
233 fn source(&self) -> Option<&(dyn Error + 'static)> {
234 match self {
235 Self::NotFound => Some(&IconNotFound),
236 Self::Surface(e) => Some(e),
237 }
238 }
239}
240
241#[derive(Debug)]
242struct IconNotFound;
243
244impl Display for IconNotFound {
245 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
246 f.write_str("icon not found")
247 }
248}
249
250impl Error for IconNotFound {}
251
252use crate::ffi;
253use crate::String;
254use std::error::Error;
255use std::ffi::CStr;
256use std::ffi::CString;
257use std::fmt;
258use std::fmt::Display;
259use std::fmt::Formatter;
260use std::fmt::Write as _;
261use std::marker::PhantomData;
262use std::os::raw::c_int;
263use std::os::unix::ffi::OsStrExt;
264use std::path::Path;
265use std::ptr;
266use std::slice;
267use std::str;