|
@@ -1,23 +1,63 @@
|
|
|
use libc::{c_int, c_char};
|
|
|
-use std::ffi::{CStr, CString};
|
|
|
-use std::mem;
|
|
|
+use std::ffi::CStr;
|
|
|
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 eventual::{Async, AsyncResult, Future};
|
|
|
|
|
|
+use cstring_cache::CStringCache;
|
|
|
use types::sp_error;
|
|
|
use types::sp_error::*;
|
|
|
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)]
|
|
|
-pub type sp_session = Session;
|
|
|
+pub type sp_session = SpSession;
|
|
|
|
|
|
#[no_mangle]
|
|
|
pub unsafe extern "C" fn sp_session_create(c_config: *const sp_session_config,
|
|
|
c_session: *mut *mut sp_session) -> sp_error {
|
|
|
- assert_eq!(global_session, None);
|
|
|
+ assert!(global_session.is_none());
|
|
|
|
|
|
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,
|
|
|
};
|
|
|
|
|
|
- let session = Box::new(Session::new(config));
|
|
|
- let session = Box::into_raw(session);
|
|
|
+ let (tx, rx) = mpsc::channel();
|
|
|
+
|
|
|
+ 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;
|
|
|
|
|
|
SP_ERROR_OK
|
|
@@ -47,8 +97,6 @@ pub unsafe extern "C" fn sp_session_create(c_config: *const sp_session_config,
|
|
|
|
|
|
#[no_mangle]
|
|
|
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;
|
|
|
drop(Box::from_raw(c_session));
|
|
|
|
|
@@ -61,20 +109,25 @@ pub unsafe extern "C" fn sp_session_login(c_session: *mut sp_session,
|
|
|
c_password: *const c_char,
|
|
|
_remember_me: bool,
|
|
|
_blob: *const c_char) -> sp_error {
|
|
|
- assert_eq!(global_session, Some(c_session));
|
|
|
-
|
|
|
let session = &*c_session;
|
|
|
|
|
|
let username = CStr::from_ptr(c_username).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.clone();
|
|
|
- ::std::thread::spawn(move || {
|
|
|
- loop {
|
|
|
- session.poll();
|
|
|
+ let session = session.session.clone();
|
|
|
+ SpSession::receive(Future::spawn(move || {
|
|
|
+ session.login_password(username, password)
|
|
|
+ }), |session, result| {
|
|
|
+ result.unwrap();
|
|
|
+
|
|
|
+ {
|
|
|
+ let session = session.session.clone();
|
|
|
+ ::std::thread::spawn(move || {
|
|
|
+ loop {
|
|
|
+ session.poll();
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
});
|
|
|
}
|
|
@@ -84,27 +137,32 @@ pub unsafe extern "C" fn sp_session_login(c_session: *mut sp_session,
|
|
|
|
|
|
#[no_mangle]
|
|
|
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 = &*c_session;
|
|
|
-
|
|
|
- let username = CString::new(session.username()).unwrap();
|
|
|
- let c_username = username.as_ptr();
|
|
|
+ let session = &mut *c_session;
|
|
|
|
|
|
- // FIXME
|
|
|
- mem::forget(username);
|
|
|
-
|
|
|
- c_username
|
|
|
+ let username = session.session.username();
|
|
|
+ session.cache.intern(&username).as_ptr()
|
|
|
}
|
|
|
|
|
|
#[no_mangle]
|
|
|
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 country = session.username();
|
|
|
+ let country = session.session.country();
|
|
|
country.chars().fold(0, |acc, x| {
|
|
|
acc << 8 | (x as u32)
|
|
|
}) 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
|
|
|
+}
|