1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#![allow(non_camel_case_types)]
mod error;
use anyhow::anyhow;
use error::{clear_last_error, set_last_error};
use std::ffi::{CStr, CString};
use std::{mem, ptr, slice};

pub use crate::error::{
    linicon_have_last_error, linicon_last_error_length, linicon_last_error_message,
};

/// Opaque iterator over icon paths
pub type linicon_IconIter<'a> = linicon::IconIter<'a>;

// Do not reorder!
/// The type of icon returned i.e. file type
#[repr(C)]
pub enum linicon_IconType {
    PNG,
    SVG,
    XMP,
}

impl From<linicon::IconType> for linicon_IconType {
    fn from(icon_type: linicon::IconType) -> Self {
        match icon_type {
            linicon::IconType::PNG => linicon_IconType::PNG,
            linicon::IconType::SVG => linicon_IconType::SVG,
            linicon::IconType::XMP => linicon_IconType::XMP,
        }
    }
}
/// A path to an icon and meta data about that icon
#[repr(C)]
pub struct linicon_IconPath {
    /// The path to the icon
    path: *mut libc::c_char,
    /// The name of the theme to icon is from
    theme: *mut libc::c_char,
    /// The type of icon i.e. the file type
    icon_type: linicon_IconType,
}

impl From<linicon::IconPath> for linicon_IconPath {
    fn from(ip: linicon::IconPath) -> Self {
        let path_str = ip.path.into_os_string().into_string().unwrap();
        linicon_IconPath {
            path: CString::new(path_str)
                .expect("CString::new failed")
                .into_raw(),
            theme: CString::new(ip.theme)
                .expect("CString::new failed")
                .into_raw(),
            icon_type: ip.icon_type.into(),
        }
    }
}

/// Free an IconPath*
#[no_mangle]
pub unsafe extern "C" fn linicon_free_icon_path(ptr: *mut linicon_IconPath) {
    if ptr.is_null() {
        return;
    }
    let icon_path = Box::from_raw(ptr);
    mem::drop(CString::from_raw(icon_path.path));
    mem::drop(CString::from_raw(icon_path.theme));
}

/// Lookup and icons given a name, size, and scale and the name theme
/// you want to look the icon up from.
#[no_mangle]
pub unsafe extern "C" fn linicon_lookup_icon<'a>(
    theme_name: *mut libc::c_char,
    name: *mut libc::c_char,
    size: u16,
    scale: u16,
) -> *mut linicon_IconIter<'a> {
    if theme_name.is_null() {
        set_last_error(anyhow!(
            "theme_name pointer given to linicon_lookup_icon was null"
        ));
        return ptr::null_mut();
    }
    if name.is_null() {
        set_last_error(anyhow!(
            "name pointer given to linicon_lookup_icon was null"
        ));
        return ptr::null_mut();
    }
    let theme_name = CStr::from_ptr(theme_name).to_str().unwrap();
    let name = CStr::from_ptr(name).to_str().unwrap();
    let ret = linicon::lookup_icon(theme_name, name, size, scale);
    match ret {
        Ok(iter) => Box::into_raw(Box::new(iter)),
        Err(e) => {
            set_last_error(e);
            ptr::null_mut()
        }
    }
}

/// Works the same as lookup_icon expect
/// you can provide a list of additional paths to the default list
/// of paths in with to search for icon themes.
#[no_mangle]
pub unsafe extern "C" fn linicon_lookup_icon_with_extra_paths<'a>(
    theme_name: *mut libc::c_char,
    name: *mut libc::c_char,
    size: u16,
    scale: u16,
    extra_search_paths: *mut *mut libc::c_char,
    extra_search_paths_len: libc::c_int,
) -> *mut linicon_IconIter<'a> {
    if theme_name.is_null() {
        set_last_error(anyhow!(
            "theme_name pointer given to linicon_lookup_icon_with_extra_paths was null"
        ));
        return ptr::null_mut();
    }
    if name.is_null() {
        set_last_error(anyhow!(
            "name pointer given to linicon_lookup_icon_with_extra_paths was null"
        ));
        return ptr::null_mut();
    }
    if extra_search_paths.is_null() {
        set_last_error(anyhow!(
            "extra_search_paths pointer given to linicon_lookup_icon_with_extra_paths was null"
        ));
        return ptr::null_mut();
    }
    let theme_name = CStr::from_ptr(theme_name).to_str().unwrap();
    let name = CStr::from_ptr(name).to_str().unwrap();
    let esp_arr = slice::from_raw_parts(extra_search_paths, extra_search_paths_len as usize);
    let mut esp = Vec::new();
    for s in esp_arr {
        if s.is_null() {
            set_last_error(anyhow!(
                "one of the strings in the extra_search_paths array given to linicon_lookup_icon_with_extra_paths was null"
            ));
            return ptr::null_mut();
        }
        match CStr::from_ptr(*s).to_str() {
            Ok(s) => esp.push(s),
            Err(e) => {
                set_last_error(anyhow::Error::from(e));
                return ptr::null_mut();
            }
        }
    }
    let ret = linicon::lookup_icon_with_extra_paths(theme_name, name, size, scale, &esp);
    match ret {
        Ok(iter) => Box::into_raw(Box::new(iter)),
        Err(e) => {
            set_last_error(e);
            ptr::null_mut()
        }
    }
}

/// Get the next IconPath from an IconIter
#[no_mangle]
pub unsafe extern "C" fn linicon_iter_next<'a>(
    ptr: *mut linicon_IconIter<'a>,
) -> *mut linicon_IconPath {
    if ptr.is_null() {
        set_last_error(anyhow!("pointer given to linicon_iter_next was null"));
        return ptr::null_mut();
    }

    let iter = &mut *ptr;

    match iter.next() {
        None => {
            clear_last_error();
            ptr::null_mut()
        }
        Some(Ok(icon_path)) => Box::into_raw(Box::new(icon_path.into())),
        Some(Err(e)) => {
            set_last_error(e);
            ptr::null_mut()
        }
    }
}

/// Free an IconIter
#[no_mangle]
pub unsafe extern "C" fn linicon_free_icon_iter<'a>(ptr: *mut linicon_IconIter<'a>) {
    if ptr.is_null() {
        return;
    }
    mem::drop(Box::from_raw(ptr));
}