Skip to main content

nemo_extension/
info_provider.rs

1use glib_ffi::gpointer;
2use gobject_ffi::{GClosure, g_closure_ref};
3use nemo_ffi::{NemoFileInfo, NemoInfoProvider, NemoOperationHandle, NemoOperationResult};
4use nemo_ffi::{nemo_file_info_add_string_attribute, nemo_file_info_get_uri, nemo_file_info_get_uri_scheme};
5use nemo_ffi::nemo_file_info_invalidate_extension_info;
6use std::collections::HashMap;
7use std::ffi::{CStr, CString};
8use std::sync::{Arc, Mutex};
9use std::sync::atomic::{AtomicUsize, Ordering};
10
11pub trait InfoProvider : Send + Sync {
12    fn should_update_file_info(&self, &FileInfo) -> bool;
13    fn update_file_info(&self, &mut FileInfo);
14}
15
16#[derive(Clone)]
17pub struct FileInfo {
18    pub raw_file_info: *mut NemoFileInfo,
19    pub attributes: HashMap<String, String>,
20}
21
22unsafe impl Send for FileInfo {}
23
24impl FileInfo {
25    pub fn new(raw_file_info: *mut NemoFileInfo) -> FileInfo {
26        FileInfo {
27            raw_file_info: raw_file_info,
28            attributes: HashMap::new(),
29        }
30    }
31
32    pub fn get_uri(&self) -> String {
33        let raw_file_info = &self.raw_file_info;
34        unsafe {
35            CStr::from_ptr(nemo_file_info_get_uri(*raw_file_info)).to_string_lossy().into_owned()
36        }
37    }
38
39    pub fn get_uri_scheme(&self) -> String {
40        let raw_file_info = &self.raw_file_info;
41        unsafe {
42            CStr::from_ptr(nemo_file_info_get_uri_scheme(*raw_file_info)).to_string_lossy().into_owned()
43        }
44    }
45
46    pub fn invalidate_extension_info(&self) {
47        unsafe {
48            nemo_file_info_invalidate_extension_info(self.raw_file_info);
49        }
50    }
51
52    pub fn add_attribute(&mut self, name: &str, value: &str) -> &mut FileInfo {
53        self.attributes.insert(name.to_string(), value.to_string());
54        self
55    }
56}
57
58pub struct UpdateFileInfoOperationHandle {
59    pub skip_response: bool
60}
61
62macro_rules! info_provider_iface {
63    ($iface_init_fn:ident, $update_file_info_fn:ident, $update_file_info_bg_fn:ident, $cancel_update_fn:ident, $rust_provider:ident, $set_rust_provider:ident) => {
64        #[no_mangle]
65        pub unsafe extern "C" fn $iface_init_fn(iface: gpointer, _: gpointer) {
66            use nemo_ffi::NemoInfoProviderIface;
67
68            let iface_struct = iface as *mut NemoInfoProviderIface;
69            (*iface_struct).update_file_info = Some($update_file_info_fn);
70            (*iface_struct).cancel_update = Some($cancel_update_fn);
71        }
72
73        #[no_mangle]
74        pub extern "C" fn $update_file_info_fn(provider: *mut NemoInfoProvider,
75                                               file: *mut NemoFileInfo,
76                                               update_complete: *mut GClosure,
77                                               handle: *mut *mut NemoOperationHandle) -> NemoOperationResult {
78            use std::mem;
79            use std::sync::{Arc, Mutex};
80            use std::sync::mpsc::channel;
81            use std::thread;
82
83            let mut file_info = FileInfo::new(file);
84
85            let should_update_file_info = match *$rust_provider.lock().unwrap() {
86                Some(ref p) => p.should_update_file_info(&mut file_info),
87                None => false,
88            };
89
90            if !should_update_file_info {
91                return NemoOperationResult::NemoOperationComplete;
92            }
93
94            let my_handle = Arc::new(Mutex::new(UpdateFileInfoOperationHandle { skip_response: false }));
95            let my_handle_local = my_handle.clone();
96            let my_handle_thread = my_handle.clone();
97
98            let (tx, rx) = channel();
99            thread::spawn(move || {
100                let (file_info, provider, update_complete, handle_ref) = rx.recv().unwrap();
101                $update_file_info_bg_fn(file_info, provider, update_complete, my_handle_thread, handle_ref);
102            });
103
104            unsafe {
105                let closure_copy = g_closure_ref(update_complete);
106                *handle = mem::transmute(Box::into_raw(Box::new(my_handle_local)));
107                tx.send((Box::new(file_info), &mut *provider, &mut *closure_copy, &mut **handle)).unwrap();
108            }
109
110            return NemoOperationResult::NemoOperationInProgress;
111        }
112
113        #[no_mangle]
114        pub extern "C" fn $cancel_update_fn(_provider: *mut NemoInfoProvider, handle: *mut NemoOperationHandle) {
115            use std::sync::{Arc, Mutex};
116
117            unsafe {
118                let handle = handle as *mut Arc<Mutex<UpdateFileInfoOperationHandle>>;
119                let mut my_handle = (*handle).lock().unwrap();
120                my_handle.skip_response = true;
121            }
122        }
123
124        fn $update_file_info_bg_fn(mut file_info: Box<FileInfo>,
125                            provider: &mut NemoInfoProvider,
126                            update_complete: &mut GClosure,
127                            my_handle: Arc<Mutex<UpdateFileInfoOperationHandle>>,
128                            handle_ref: &mut NemoOperationHandle) {
129            use nemo_ffi::nemo_info_provider_update_complete_invoke;
130
131            match *$rust_provider.lock().unwrap() {
132                Some(ref p) => p.update_file_info(file_info.as_mut()),
133                None => (),
134            };
135
136            if !my_handle.lock().unwrap().skip_response {
137                let file_info = file_info.as_mut();
138                let attributes = &file_info.attributes;
139                unsafe {
140                    for (attr_name, attr_value) in attributes {
141                        let attr_name_c = CString::new(attr_name.as_str()).unwrap().into_raw();
142                        let attr_value_c = CString::new(attr_value.as_str()).unwrap().into_raw();
143
144                        nemo_file_info_add_string_attribute(file_info.raw_file_info, attr_name_c, attr_value_c);
145
146                        // deallocate CStrings
147                        CString::from_raw(attr_name_c);
148                        CString::from_raw(attr_value_c);
149                    }
150
151                    nemo_info_provider_update_complete_invoke(update_complete, provider, handle_ref, NemoOperationResult::NemoOperationComplete);
152                }
153            }
154        }
155
156        pub fn $set_rust_provider(info_provider: Box<dyn InfoProvider>) {
157            *$rust_provider.lock().unwrap() = Some(info_provider);
158        }
159
160        lazy_static! {
161            static ref $rust_provider: Mutex<Option<Box<dyn InfoProvider>>> = Mutex::new(None);
162        }
163    }
164}
165
166info_provider_iface!(info_provider_iface_init_0, info_provider_update_file_info_0, info_provider_update_file_info_bg_0, info_provider_cancel_update_0, INFO_PROVIDER_0, set_info_provider_0);
167info_provider_iface!(info_provider_iface_init_1, info_provider_update_file_info_1, info_provider_update_file_info_bg_1, info_provider_cancel_update_1, INFO_PROVIDER_1, set_info_provider_1);
168info_provider_iface!(info_provider_iface_init_2, info_provider_update_file_info_2, info_provider_update_file_info_bg_2, info_provider_cancel_update_2, INFO_PROVIDER_2, set_info_provider_2);
169info_provider_iface!(info_provider_iface_init_3, info_provider_update_file_info_3, info_provider_update_file_info_bg_3, info_provider_cancel_update_3, INFO_PROVIDER_3, set_info_provider_3);
170info_provider_iface!(info_provider_iface_init_4, info_provider_update_file_info_4, info_provider_update_file_info_bg_4, info_provider_cancel_update_4, INFO_PROVIDER_4, set_info_provider_4);
171info_provider_iface!(info_provider_iface_init_5, info_provider_update_file_info_5, info_provider_update_file_info_bg_5, info_provider_cancel_update_5, INFO_PROVIDER_5, set_info_provider_5);
172info_provider_iface!(info_provider_iface_init_6, info_provider_update_file_info_6, info_provider_update_file_info_bg_6, info_provider_cancel_update_6, INFO_PROVIDER_6, set_info_provider_6);
173info_provider_iface!(info_provider_iface_init_7, info_provider_update_file_info_7, info_provider_update_file_info_bg_7, info_provider_cancel_update_7, INFO_PROVIDER_7, set_info_provider_7);
174info_provider_iface!(info_provider_iface_init_8, info_provider_update_file_info_8, info_provider_update_file_info_bg_8, info_provider_cancel_update_8, INFO_PROVIDER_8, set_info_provider_8);
175info_provider_iface!(info_provider_iface_init_9, info_provider_update_file_info_9, info_provider_update_file_info_bg_9, info_provider_cancel_update_9, INFO_PROVIDER_9, set_info_provider_9);
176
177pub fn info_provider_iface_externs() -> Vec<unsafe extern "C" fn(gpointer, gpointer)> {
178    vec![
179        info_provider_iface_init_0,
180        info_provider_iface_init_1,
181        info_provider_iface_init_2,
182        info_provider_iface_init_3,
183        info_provider_iface_init_4,
184        info_provider_iface_init_5,
185        info_provider_iface_init_6,
186        info_provider_iface_init_7,
187        info_provider_iface_init_8,
188        info_provider_iface_init_9,
189    ]
190}
191
192pub fn rust_info_provider_setters() -> Vec<fn(Box<dyn InfoProvider>)> {
193    vec![
194        set_info_provider_0,
195        set_info_provider_1,
196        set_info_provider_2,
197        set_info_provider_3,
198        set_info_provider_4,
199        set_info_provider_5,
200        set_info_provider_6,
201        set_info_provider_7,
202        set_info_provider_8,
203        set_info_provider_9,
204    ]
205}
206
207static NEXT_INFO_PROVIDER_IFACE_INDEX: AtomicUsize = AtomicUsize::new(0);
208
209pub fn take_next_info_provider_iface_index() -> usize {
210    NEXT_INFO_PROVIDER_IFACE_INDEX.fetch_add(1, Ordering::SeqCst)
211}