mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Intern returned C strings to avoid leaking them.
This commit is contained in:
parent
526c54702b
commit
5a9b139a7f
9 changed files with 282 additions and 112 deletions
|
@ -10,7 +10,6 @@ crate-type = ["staticlib"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
eventual = "~0.1.5"
|
eventual = "~0.1.5"
|
||||||
owning_ref = "0.1.*"
|
|
||||||
|
|
||||||
[dependencies.librespot]
|
[dependencies.librespot]
|
||||||
path = "../"
|
path = "../"
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use libc::c_char;
|
use libc::c_char;
|
||||||
use std::ffi::CString;
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
use librespot::metadata::Artist;
|
use librespot::metadata::Artist;
|
||||||
|
|
||||||
|
@ -17,17 +15,11 @@ pub unsafe extern "C" fn sp_artist_is_loaded(c_artist: *mut sp_artist) -> bool {
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_artist_name(c_artist: *mut sp_artist) -> *const c_char {
|
pub unsafe extern "C" fn sp_artist_name(c_artist: *mut sp_artist) -> *const c_char {
|
||||||
let artist = &*c_artist;
|
let artist = &mut *c_artist;
|
||||||
|
|
||||||
let name = artist.get()
|
let name = artist.get()
|
||||||
.map(|metadata| metadata.name.clone())
|
.map(|metadata| &metadata.name as &str)
|
||||||
.unwrap_or("".to_owned());
|
.unwrap_or("");
|
||||||
|
|
||||||
let name = CString::new(name).unwrap();
|
artist.intern(name).as_ptr()
|
||||||
let c_name = name.as_ptr();
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
mem::forget(name);
|
|
||||||
|
|
||||||
c_name
|
|
||||||
}
|
}
|
||||||
|
|
21
capi/src/cstring_cache.rs
Normal file
21
capi/src/cstring_cache.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::{CString, CStr};
|
||||||
|
|
||||||
|
pub struct CStringCache {
|
||||||
|
cache: HashMap<String, CString>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CStringCache {
|
||||||
|
pub fn new() -> CStringCache {
|
||||||
|
CStringCache {
|
||||||
|
cache: HashMap::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intern(&mut self, string: &str) -> &CStr {
|
||||||
|
self.cache.entry(string.to_owned()).or_insert_with(|| {
|
||||||
|
CString::new(string).unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
#![feature(fnbox)]
|
||||||
|
|
||||||
extern crate librespot;
|
extern crate librespot;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate eventual;
|
extern crate eventual;
|
||||||
extern crate owning_ref;
|
|
||||||
|
|
||||||
pub mod artist;
|
pub mod artist;
|
||||||
pub mod link;
|
pub mod link;
|
||||||
|
@ -9,9 +10,5 @@ pub mod metadata;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod track;
|
pub mod track;
|
||||||
mod types;
|
mod types;
|
||||||
|
mod cstring_cache;
|
||||||
pub use types::sp_session_config;
|
|
||||||
pub use types::sp_error;
|
|
||||||
pub use types::sp_error::*;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use metadata::SpMetadata;
|
use metadata::SpMetadata;
|
||||||
use session::global_session;
|
use session::SpSession;
|
||||||
use track::sp_track;
|
use track::sp_track;
|
||||||
use types::sp_error;
|
use types::sp_error;
|
||||||
use types::sp_error::*;
|
use types::sp_error::*;
|
||||||
|
@ -29,8 +29,8 @@ pub unsafe extern "C" fn sp_link_release(c_link: *mut sp_link) -> sp_error {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_link_as_track(c_link: *mut sp_link) -> *mut sp_track {
|
pub unsafe extern "C" fn sp_link_as_track(c_link: *mut sp_link) -> *mut sp_track {
|
||||||
let link = &*c_link;
|
let link = &*c_link;
|
||||||
let session = &*global_session.unwrap();
|
let session = SpSession::global();
|
||||||
|
|
||||||
let track = SpMetadata::from_future(link.as_track(session).unwrap());
|
let track = SpMetadata::from_future(link.as_track(&session.session).unwrap());
|
||||||
Box::into_raw(Box::new(track))
|
Box::into_raw(Box::new(track))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +1,87 @@
|
||||||
use eventual::Async;
|
use eventual::Async;
|
||||||
use owning_ref::MutexGuardRef;
|
use std::sync::Arc;
|
||||||
use std::sync::{Mutex, Arc};
|
use std::ffi::CStr;
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
|
|
||||||
use librespot::metadata::{MetadataTrait, MetadataRef};
|
use librespot::metadata::{MetadataTrait, MetadataRef};
|
||||||
|
|
||||||
pub enum SpMetadataInner<T: MetadataTrait> {
|
use cstring_cache::CStringCache;
|
||||||
|
|
||||||
|
use session::SpSession;
|
||||||
|
|
||||||
|
pub struct UnsafeSyncCell<T> {
|
||||||
|
cell: UnsafeCell<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T> UnsafeSyncCell<T> {
|
||||||
|
fn new(value: T) -> UnsafeSyncCell<T> {
|
||||||
|
UnsafeSyncCell { cell: UnsafeCell::new(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> *mut T {
|
||||||
|
self.cell.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T> Sync for UnsafeSyncCell<T> {}
|
||||||
|
|
||||||
|
pub enum SpMetadataState<T: MetadataTrait> {
|
||||||
Loading,
|
Loading,
|
||||||
Error,
|
Error,
|
||||||
Loaded(T),
|
Loaded(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpMetadata<T: MetadataTrait>(Arc<Mutex<SpMetadataInner<T>>>);
|
pub struct SpMetadata<T: MetadataTrait> {
|
||||||
|
state: Arc<UnsafeSyncCell<SpMetadataState<T>>>,
|
||||||
|
cache: CStringCache,
|
||||||
|
}
|
||||||
|
|
||||||
impl <T: MetadataTrait> SpMetadata<T> {
|
impl <T: MetadataTrait> SpMetadata<T> {
|
||||||
pub fn from_future(future: MetadataRef<T>) -> SpMetadata<T> {
|
pub fn from_future(future: MetadataRef<T>) -> SpMetadata<T> {
|
||||||
let metadata = Arc::new(Mutex::new(SpMetadataInner::Loading));
|
let state = Arc::new(UnsafeSyncCell::new(SpMetadataState::Loading));
|
||||||
|
|
||||||
{
|
{
|
||||||
let metadata = metadata.clone();
|
let state = state.clone();
|
||||||
future.receive(move |result| {
|
SpSession::receive(future, move |session, result| {
|
||||||
//let metadata = metadata.upgrade().unwrap();
|
let state = unsafe {
|
||||||
let mut metadata = metadata.lock().unwrap();
|
&mut *state.get()
|
||||||
|
|
||||||
*metadata = match result {
|
|
||||||
Ok(data) => SpMetadataInner::Loaded(data),
|
|
||||||
Err(_) => SpMetadataInner::Error,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
*state = match result {
|
||||||
|
Ok(data) => SpMetadataState::Loaded(data),
|
||||||
|
Err(_) => SpMetadataState::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if let Some(f) = session.callbacks.metadata_updated {
|
||||||
|
f(session as *mut _)
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SpMetadata(metadata)
|
SpMetadata {
|
||||||
|
state: state,
|
||||||
|
cache: CStringCache::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_loaded(&self) -> bool {
|
pub fn is_loaded(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
self.get().is_some()
|
self.get().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self) -> Option<MutexGuardRef<SpMetadataInner<T>, T>> {
|
|
||||||
let inner = self.0.lock().unwrap();
|
|
||||||
|
|
||||||
match *inner {
|
|
||||||
SpMetadataInner::Loaded(_) => {
|
|
||||||
Some(MutexGuardRef::new(inner).map(|inner| {
|
|
||||||
match *inner {
|
|
||||||
SpMetadataInner::Loaded(ref metadata) => metadata,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get(&self) -> Option<&'static T> {
|
||||||
|
let state = &*self.state.get();
|
||||||
|
|
||||||
|
match *state {
|
||||||
|
SpMetadataState::Loaded(ref metadata) => Some(metadata),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn intern(&mut self, string: &str) -> &CStr {
|
||||||
|
self.cache.intern(string)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,63 @@
|
||||||
use libc::{c_int, c_char};
|
use libc::{c_int, c_char};
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::CStr;
|
||||||
use std::mem;
|
|
||||||
use std::slice::from_raw_parts;
|
use std::slice::from_raw_parts;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::boxed::FnBox;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use librespot::session::{Session, Config, Bitrate};
|
use librespot::session::{Session, Config, Bitrate};
|
||||||
|
use eventual::{Async, AsyncResult, Future};
|
||||||
|
|
||||||
|
use cstring_cache::CStringCache;
|
||||||
use types::sp_error;
|
use types::sp_error;
|
||||||
use types::sp_error::*;
|
use types::sp_error::*;
|
||||||
use types::sp_session_config;
|
use types::sp_session_config;
|
||||||
|
use types::sp_session_callbacks;
|
||||||
|
|
||||||
pub static mut global_session: Option<*mut Session> = None;
|
static mut global_session: Option<(*const sp_session, *const Mutex<mpsc::Sender<SpSessionEvent>>)> = None;
|
||||||
|
|
||||||
|
pub type SpSessionEvent = Box<FnBox(&mut SpSession) -> ()>;
|
||||||
|
|
||||||
|
pub struct SpSession {
|
||||||
|
pub session: Session,
|
||||||
|
cache: CStringCache,
|
||||||
|
rx: mpsc::Receiver<SpSessionEvent>,
|
||||||
|
|
||||||
|
pub callbacks: &'static sp_session_callbacks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpSession {
|
||||||
|
pub unsafe fn global() -> &'static SpSession {
|
||||||
|
&*global_session.unwrap().0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run<F: FnOnce(&mut SpSession) -> () + 'static>(event: F) {
|
||||||
|
let tx = unsafe {
|
||||||
|
&*global_session.unwrap().1
|
||||||
|
};
|
||||||
|
|
||||||
|
tx.lock().unwrap().send(Box::new(event)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receive<T, E, F>(future: Future<T, E>, handler: F)
|
||||||
|
where T : Send, E: Send,
|
||||||
|
F : FnOnce(&mut SpSession, AsyncResult<T, E>) -> () + Send + 'static {
|
||||||
|
|
||||||
|
future.receive(move |result| {
|
||||||
|
SpSession::run(move |session| {
|
||||||
|
handler(session, result);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub type sp_session = Session;
|
pub type sp_session = SpSession;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_session_create(c_config: *const sp_session_config,
|
pub unsafe extern "C" fn sp_session_create(c_config: *const sp_session_config,
|
||||||
c_session: *mut *mut sp_session) -> sp_error {
|
c_session: *mut *mut sp_session) -> sp_error {
|
||||||
assert_eq!(global_session, None);
|
assert!(global_session.is_none());
|
||||||
|
|
||||||
let c_config = &*c_config;
|
let c_config = &*c_config;
|
||||||
|
|
||||||
|
@ -36,10 +76,20 @@ pub unsafe extern "C" fn sp_session_create(c_config: *const sp_session_config,
|
||||||
bitrate: Bitrate::Bitrate160,
|
bitrate: Bitrate::Bitrate160,
|
||||||
};
|
};
|
||||||
|
|
||||||
let session = Box::new(Session::new(config));
|
let (tx, rx) = mpsc::channel();
|
||||||
let session = Box::into_raw(session);
|
|
||||||
|
let session = SpSession {
|
||||||
|
session: Session::new(config),
|
||||||
|
cache: CStringCache::new(),
|
||||||
|
rx: rx,
|
||||||
|
callbacks: &*c_config.callbacks,
|
||||||
|
};
|
||||||
|
|
||||||
|
let session = Box::into_raw(Box::new(session));
|
||||||
|
let tx = Box::into_raw(Box::new(Mutex::new(tx)));
|
||||||
|
|
||||||
|
global_session = Some((session, tx));
|
||||||
|
|
||||||
global_session = Some(session);
|
|
||||||
*c_session = session;
|
*c_session = session;
|
||||||
|
|
||||||
SP_ERROR_OK
|
SP_ERROR_OK
|
||||||
|
@ -47,8 +97,6 @@ pub unsafe extern "C" fn sp_session_create(c_config: *const sp_session_config,
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_session_release(c_session: *mut sp_session) -> sp_error {
|
pub unsafe extern "C" fn sp_session_release(c_session: *mut sp_session) -> sp_error {
|
||||||
assert_eq!(global_session, Some(c_session));
|
|
||||||
|
|
||||||
global_session = None;
|
global_session = None;
|
||||||
drop(Box::from_raw(c_session));
|
drop(Box::from_raw(c_session));
|
||||||
|
|
||||||
|
@ -61,50 +109,60 @@ pub unsafe extern "C" fn sp_session_login(c_session: *mut sp_session,
|
||||||
c_password: *const c_char,
|
c_password: *const c_char,
|
||||||
_remember_me: bool,
|
_remember_me: bool,
|
||||||
_blob: *const c_char) -> sp_error {
|
_blob: *const c_char) -> sp_error {
|
||||||
assert_eq!(global_session, Some(c_session));
|
|
||||||
|
|
||||||
let session = &*c_session;
|
let session = &*c_session;
|
||||||
|
|
||||||
let username = CStr::from_ptr(c_username).to_string_lossy().into_owned();
|
let username = CStr::from_ptr(c_username).to_string_lossy().into_owned();
|
||||||
let password = CStr::from_ptr(c_password).to_string_lossy().into_owned();
|
let password = CStr::from_ptr(c_password).to_string_lossy().into_owned();
|
||||||
|
|
||||||
session.login_password(username, password).unwrap();
|
{
|
||||||
|
let session = session.session.clone();
|
||||||
|
SpSession::receive(Future::spawn(move || {
|
||||||
|
session.login_password(username, password)
|
||||||
|
}), |session, result| {
|
||||||
|
result.unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
let session = session.clone();
|
let session = session.session.clone();
|
||||||
::std::thread::spawn(move || {
|
::std::thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
session.poll();
|
session.poll();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
SP_ERROR_OK
|
SP_ERROR_OK
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_session_user_name(c_session: *mut sp_session) -> *const c_char {
|
pub unsafe extern "C" fn sp_session_user_name(c_session: *mut sp_session) -> *const c_char {
|
||||||
assert_eq!(global_session, Some(c_session));
|
let session = &mut *c_session;
|
||||||
|
|
||||||
let session = &*c_session;
|
let username = session.session.username();
|
||||||
|
session.cache.intern(&username).as_ptr()
|
||||||
let username = CString::new(session.username()).unwrap();
|
|
||||||
let c_username = username.as_ptr();
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
mem::forget(username);
|
|
||||||
|
|
||||||
c_username
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_session_user_country(c_session: *mut sp_session) -> c_int {
|
pub unsafe extern "C" fn sp_session_user_country(c_session: *mut sp_session) -> c_int {
|
||||||
assert_eq!(global_session, Some(c_session));
|
|
||||||
|
|
||||||
let session = &*c_session;
|
let session = &*c_session;
|
||||||
|
|
||||||
let country = session.username();
|
let country = session.session.country();
|
||||||
country.chars().fold(0, |acc, x| {
|
country.chars().fold(0, |acc, x| {
|
||||||
acc << 8 | (x as u32)
|
acc << 8 | (x as u32)
|
||||||
}) as c_int
|
}) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_session_process_events(c_session: *mut sp_session, next_timeout: *mut c_int) -> sp_error {
|
||||||
|
let session = &mut *c_session;
|
||||||
|
|
||||||
|
if !next_timeout.is_null() {
|
||||||
|
*next_timeout = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
let event = session.rx.recv().unwrap();
|
||||||
|
event.call_box((session,));
|
||||||
|
|
||||||
|
SP_ERROR_OK
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use libc::{c_int, c_char};
|
use libc::{c_int, c_char};
|
||||||
use std::ffi::CString;
|
|
||||||
use std::mem;
|
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
use artist::sp_artist;
|
use artist::sp_artist;
|
||||||
use metadata::SpMetadata;
|
use metadata::SpMetadata;
|
||||||
use session::global_session;
|
use session::SpSession;
|
||||||
|
|
||||||
use librespot::metadata::{Track, Artist};
|
use librespot::metadata::{Track, Artist};
|
||||||
|
|
||||||
|
@ -20,19 +18,13 @@ pub unsafe extern "C" fn sp_track_is_loaded(c_track: *mut sp_track) -> bool {
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_track_name(c_track: *mut sp_track) -> *const c_char {
|
pub unsafe extern "C" fn sp_track_name(c_track: *mut sp_track) -> *const c_char {
|
||||||
let track = &*c_track;
|
let track = &mut *c_track;
|
||||||
|
|
||||||
let name = track.get()
|
let name = track.get()
|
||||||
.map(|metadata| metadata.name.clone())
|
.map(|metadata| &metadata.name as &str)
|
||||||
.unwrap_or("".to_owned());
|
.unwrap_or("");
|
||||||
|
|
||||||
let name = CString::new(name).unwrap();
|
track.intern(name).as_ptr()
|
||||||
let c_name = name.as_ptr();
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
mem::forget(name);
|
|
||||||
|
|
||||||
c_name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -47,14 +39,12 @@ pub unsafe extern "C" fn sp_track_num_artists(c_track: *mut sp_track) -> c_int {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_track_artist(c_track: *mut sp_track, index: c_int) -> *mut sp_artist {
|
pub unsafe extern "C" fn sp_track_artist(c_track: *mut sp_track, index: c_int) -> *mut sp_artist {
|
||||||
let track = &*c_track;
|
let track = &*c_track;
|
||||||
let session = &*global_session.unwrap();
|
let session = SpSession::global();
|
||||||
|
|
||||||
track.get()
|
track.get()
|
||||||
.and_then(|metadata| metadata.artists.get(index as usize).map(|x| *x))
|
.and_then(|metadata| metadata.artists.get(index as usize).map(|x| *x))
|
||||||
.map(|artist_id| {
|
.map(|artist_id| session.session.metadata::<Artist>(artist_id))
|
||||||
let artist = SpMetadata::from_future(session.metadata::<Artist>(artist_id));
|
.map(|artist| Box::into_raw(Box::new(SpMetadata::from_future(artist))))
|
||||||
Box::into_raw(Box::new(artist))
|
|
||||||
})
|
|
||||||
.unwrap_or(null_mut())
|
.unwrap_or(null_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types, dead_code)]
|
||||||
|
|
||||||
use libc::size_t;
|
use libc::{size_t, c_int, c_char, c_void};
|
||||||
|
use session::sp_session;
|
||||||
pub enum sp_session_callbacks {}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
|
@ -48,21 +47,102 @@ pub enum sp_error {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy,Clone)]
|
#[derive(Copy,Clone)]
|
||||||
pub struct sp_session_config {
|
pub struct sp_session_config {
|
||||||
pub api_version: ::std::os::raw::c_int,
|
pub api_version: c_int,
|
||||||
pub cache_location: *const ::std::os::raw::c_char,
|
pub cache_location: *const c_char,
|
||||||
pub settings_location: *const ::std::os::raw::c_char,
|
pub settings_location: *const c_char,
|
||||||
pub application_key: *const ::std::os::raw::c_void,
|
pub application_key: *const c_void,
|
||||||
pub application_key_size: size_t,
|
pub application_key_size: size_t,
|
||||||
pub user_agent: *const ::std::os::raw::c_char,
|
pub user_agent: *const c_char,
|
||||||
pub callbacks: *const sp_session_callbacks,
|
pub callbacks: *const sp_session_callbacks,
|
||||||
pub userdata: *mut ::std::os::raw::c_void,
|
pub userdata: *mut c_void,
|
||||||
pub compress_playlists: bool,
|
pub compress_playlists: bool,
|
||||||
pub dont_save_metadata_for_playlists: bool,
|
pub dont_save_metadata_for_playlists: bool,
|
||||||
pub initially_unload_playlists: bool,
|
pub initially_unload_playlists: bool,
|
||||||
pub device_id: *const ::std::os::raw::c_char,
|
pub device_id: *const c_char,
|
||||||
pub proxy: *const ::std::os::raw::c_char,
|
pub proxy: *const c_char,
|
||||||
pub proxy_username: *const ::std::os::raw::c_char,
|
pub proxy_username: *const c_char,
|
||||||
pub proxy_password: *const ::std::os::raw::c_char,
|
pub proxy_password: *const c_char,
|
||||||
pub tracefile: *const ::std::os::raw::c_char,
|
pub tracefile: *const c_char,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct sp_session_callbacks {
|
||||||
|
pub logged_in: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
error: sp_error)>,
|
||||||
|
|
||||||
|
pub logged_out: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub metadata_updated: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub connection_error: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
error: sp_error)>,
|
||||||
|
|
||||||
|
pub message_to_user: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
message: *const c_char)>,
|
||||||
|
|
||||||
|
pub notify_main_thread: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub music_delivery: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
format: *const sp_audioformat,
|
||||||
|
frames: *const c_void,
|
||||||
|
num_frames: c_int)
|
||||||
|
-> c_int>,
|
||||||
|
|
||||||
|
pub play_token_lost: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub log_message: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
data: *const c_char)>,
|
||||||
|
|
||||||
|
pub end_of_track: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub streaming_error: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
error: sp_error)>,
|
||||||
|
|
||||||
|
pub userinfo_updated: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub start_playback: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub stop_playback: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub get_audio_buffer_stats: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
stats: *mut sp_audio_buffer_stats)>,
|
||||||
|
|
||||||
|
pub offline_status_updated: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub offline_error: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
error: sp_error)>,
|
||||||
|
|
||||||
|
pub credentials_blob_updated: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
blob: *const c_char)>,
|
||||||
|
|
||||||
|
pub connectionstate_updated: Option<unsafe extern "C" fn(session: *mut sp_session)>,
|
||||||
|
|
||||||
|
pub scrobble_error: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
error: sp_error)>,
|
||||||
|
|
||||||
|
pub private_session_mode_changed: Option<unsafe extern "C" fn(session: *mut sp_session,
|
||||||
|
is_private: bool)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct sp_audioformat {
|
||||||
|
pub sample_type: sp_sampletype,
|
||||||
|
pub sample_rate: c_int,
|
||||||
|
pub channels: c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum sp_sampletype {
|
||||||
|
SP_SAMPLETYPE_INT16_NATIVE_ENDIAN = 0,
|
||||||
|
_Dummy // rust #10292
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct sp_audio_buffer_stats {
|
||||||
|
pub samples: c_int,
|
||||||
|
pub stutter: c_int,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue