libopenlipc_sys/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(dead_code)]
5include!("./bindings.rs");
6
7use std::ffi::CStr;
8use std::ffi::CString;
9use std::os::raw::{c_char, c_int, c_void};
10
11pub struct rLIPC {
12    conn: *mut LIPC,
13}
14
15macro_rules! code_to_result {
16    ($value:expr) => {
17        if $value == LIPCcode_LIPC_OK {
18            Ok(())
19        } else {
20            Err(format!(
21                "Failed to subscribe: {}",
22                rLIPC::code_to_string($value)
23            ))
24        }
25    };
26}
27
28#[derive(Debug)]
29pub enum LipcResult {
30    NUM(i32),
31    STR(String),
32}
33
34impl rLIPC {
35    /// Returns a new LIPC client if a connection was successful
36    /// Connects to the LIPC bus with no name.
37    pub fn new() -> Result<Self, String> {
38        let lipc;
39        unsafe {
40            lipc = LipcOpenNoName();
41        }
42        if lipc == (std::ptr::null_mut() as *mut c_void) {
43            return Err(String::from("Failed to open a connection!"));
44        }
45        Ok(Self { conn: lipc })
46    }
47
48    /// Register a callback for events broadcasted by `service`. Optionally,
49    /// you can filter to a single event by providing `name`.
50    ///
51    /// For callback, we pass (source, name, optional int param, optional str param).
52    /// an example callback payload would be
53    /// "com.lab126.appmgrd", "appActivating", Some(1), Some("com.lab126.booklet.reader")
54    ///
55    /// # Examples
56    ///
57    /// ```
58    /// use libopenlipc_sys::rLIPC;
59    /// let r = rLIPC::new().unwrap();
60    /// r.subscribe("com.lab126.powerd", Some("battLevelChanged"), |_, _, _, _| ());
61    /// // You will only get updates about battLevel in the callback
62    /// // battLevelChanged sends <int param> with the new battery value
63    /// ```
64    ///
65    /// ```
66    /// use libopenlipc_sys::rLIPC;
67    /// let r = rLIPC::new().unwrap();
68    /// r.subscribe("com.lab126.powerd", None, |_, _, _, _| ());
69    /// // You will get updates all power related events (screen on, off, etc)
70    /// ```
71    pub fn subscribe<F>(&self, service: &str, name: Option<&str>, callback: F) -> Result<(), String>
72    where
73        F: FnMut(&str, &str, Option<LipcResult>) + Send,
74    {
75        let _service = CString::new(service).unwrap();
76
77        let owned;
78        let c_name = match name {
79            None => std::ptr::null(),
80            Some(_name) => {
81                owned = CString::new(_name).unwrap();
82                owned.as_ptr()
83            }
84        };
85
86        let boxed_fn: Box<dyn FnMut(&str, &str, Option<LipcResult>) + Send> =
87            Box::new(callback) as _;
88        let double_box = Box::new(boxed_fn);
89        let ptr = Box::into_raw(double_box);
90        /*
91         * You can't pass a fn directly to C -- you can however pass a `Box::into_raw`
92         * This box however is of dynamic size and loses metadata -- so it's not easy to free later
93         * The other box (boxed_fn) is a fat pointer (which we can't pass to C) but it keeps
94         * metadata
95         * So we pass a thin pointer (into_raw) to a fat pointer (<dyn FnMut..>) to C
96         * then we have to undo this in the callback
97         */
98
99        let result;
100        unsafe {
101            /* We wait to cast to .as_ptr() here
102             * For a pointer to be valid, the thing it points to must still be around.
103             * For a value to exist past the expression it's introduced in, it must be bound to a variable.
104             * When the variable disappears, the value does too.
105             * We must store the CString for _service and c_name, then independently get pointers
106             * *to* them
107             */
108            result = code_to_result!(LipcSubscribeExt(
109                self.conn,
110                _service.as_ptr(),
111                c_name,
112                Some(ugly_callback),
113                ptr as *mut c_void,
114            ));
115        }
116        result
117    }
118
119    /// Get the current value of a string property
120    /// ```
121    /// use libopenlipc_sys::rLIPC;
122    /// let r = rLIPC::new().unwrap();
123    /// let reader_status = r.get_str_prop("com.lab126.acxreaderplugin", "allReaderData").unwrap();
124    /// // reader_status would be a string containing JSON
125    /// ```
126    pub fn get_str_prop(&self, service: &str, prop: &str) -> Result<String, String> {
127        let mut handle: *mut c_char = std::ptr::null_mut();
128        let handle_ptr: *mut *mut c_char = &mut handle;
129
130        let service = CString::new(service).unwrap();
131        let prop = CString::new(prop).unwrap();
132        unsafe {
133            code_to_result!(LipcGetStringProperty(
134                self.conn,
135                service.as_ptr(),
136                prop.as_ptr(),
137                handle_ptr
138            ))?;
139        };
140
141        let val;
142        unsafe {
143            val = CStr::from_ptr(handle).to_str().unwrap().into();
144            // Made a copy, we can now free() the string
145            LipcFreeString(handle);
146        }
147        Ok(val)
148    }
149
150    /// Get the current value of an int property
151    /// ```
152    /// use libopenlipc_sys::rLIPC;
153    /// let r = rLIPC::new().unwrap();
154    /// let reader_status = r.get_int_prop("com.lab126.powerd", "battLevel").unwrap();
155    /// // reader_status will contain the battery charge % (ie: 75).
156    /// ```
157    pub fn get_int_prop(&self, service: &str, prop: &str) -> Result<i32, String> {
158        let mut val: c_int = 0;
159        let service = CString::new(service).unwrap();
160        let prop = CString::new(prop).unwrap();
161        unsafe {
162            code_to_result!(LipcGetIntProperty(
163                self.conn,
164                service.as_ptr(),
165                prop.as_ptr(),
166                &mut val
167            ))?;
168        };
169
170        Ok(val)
171    }
172
173    fn code_to_string(code: u32) -> String {
174        unsafe {
175            let cstr = CStr::from_ptr(LipcGetErrorString(code));
176            return String::from(cstr.to_str().unwrap());
177        }
178    }
179}
180
181unsafe extern "C" fn ugly_callback(
182    _: *mut LIPC,
183    name: *const c_char,
184    event: *mut LIPCevent,
185    data: *mut c_void,
186) -> LIPCcode {
187    // Can't unwrap in this function
188    let source = LipcGetEventSource(event);
189    let _name = CStr::from_ptr(name).to_str().unwrap();
190    let _source = CStr::from_ptr(source).to_str().unwrap();
191
192    let _int_param: Option<i32>;
193    let _str_param: Option<String>;
194
195    {
196        let mut int_param: c_int = 0;
197        _int_param = match ReturnCodes::from_u32(LipcGetIntParam(event, &mut int_param)).unwrap() {
198            ReturnCodes::OK => Some(int_param),
199            ReturnCodes::ERROR_NO_SUCH_PARAM => None,
200            e => {
201                println!(
202                    "Error getting int param: {}",
203                    rLIPC::code_to_string(e as u32)
204                );
205                None
206            }
207        }
208    }
209
210    {
211        let mut handle: *mut c_char = std::ptr::null_mut();
212        let handle_ptr: *mut *mut c_char = &mut handle;
213        _str_param = match ReturnCodes::from_u32(LipcGetStringParam(event, handle_ptr)).unwrap() {
214            ReturnCodes::OK => {
215                let val = CStr::from_ptr(handle).to_str().unwrap().into();
216                Some(val)
217            }
218            ReturnCodes::ERROR_NO_SUCH_PARAM => None,
219            e => {
220                println!(
221                    "Error getting string param: {}",
222                    rLIPC::code_to_string(e as u32)
223                );
224                None
225            }
226        }
227    }
228
229    let f = data as *mut Box<dyn FnMut(&str, &str, Option<LipcResult>) + Send>;
230    let _res = if let Some(val) = _int_param {
231        Some(LipcResult::NUM(val))
232    } else {
233        _str_param.map(LipcResult::STR)
234    };
235
236    (*f)(_source, _name, _res);
237    0
238}
239
240impl Drop for rLIPC {
241    fn drop(&mut self) {
242        unsafe {
243            LipcClose(self.conn);
244        }
245        println!("Disconnected");
246    }
247}
248
249unsafe impl Sync for rLIPC {}