diff --git a/src/rust/liblektor-rs/lektor_c_compat/src/c_types.rs b/src/rust/liblektor-rs/lektor_c_compat/src/c_types.rs index acc5975dbe8e258c6f248145b2fc55880a3c3f9e..8e49a097b9d0d5d0c79aaa8397424d9744e5cba3 100644 --- a/src/rust/liblektor-rs/lektor_c_compat/src/c_types.rs +++ b/src/rust/liblektor-rs/lektor_c_compat/src/c_types.rs @@ -9,8 +9,8 @@ pub type LktUriPtr = *mut c_void; #[repr(C)] pub struct LktEvent { - ty: c_uint, - attr: *const c_void, + pub ty: c_uint, + pub attr: *const c_void, } #[repr(C)] @@ -32,6 +32,7 @@ pub enum LktUriValueType { Integer = 2, } +#[derive(Debug)] #[repr(C)] pub enum LktPlaybackEvent { Stop = 0, @@ -40,12 +41,14 @@ pub enum LktPlaybackEvent { Toggle = 3, } +#[derive(Debug)] #[repr(C)] pub enum LktDbUpdateEvent { Finished = 0, Progress = 1, } +#[derive(Debug, PartialEq, Eq)] #[repr(C)] pub enum LktEventType { Null = 0, @@ -64,6 +67,59 @@ pub enum LktEventType { TouchKara = (1 << 13), } +impl TryFrom<usize> for LktEventType { + type Error = (); + + fn try_from(value: usize) -> Result<Self, Self::Error> { + use LktEventType::*; + match value { + 0 => Ok(Null), + x if x == (1 << 1) => Ok(PlayPos), + x if x == (1 << 2) => Ok(PlayFile), + x if x == (1 << 3) => Ok(PlayNext), + x if x == (1 << 4) => Ok(PlayPrev), + x if x == (1 << 5) => Ok(PlayToggle), + x if x == (1 << 6) => Ok(PropVol), + x if x == (1 << 7) => Ok(PropDur), + x if x == (1 << 8) => Ok(PropTime), + x if x == (1 << 9) => Ok(SkipCurrent), + x if x == (1 << 10) => Ok(DbUpdating), + x if x == (1 << 11) => Ok(DbUpdateTotal), + x if x == (1 << 12) => Ok(DbUpdateTick), + x if x == (1 << 13) => Ok(TouchKara), + _ => Err(()), + } + } +} + +impl TryFrom<usize> for LktDbUpdateEvent { + type Error = (); + + fn try_from(value: usize) -> Result<Self, Self::Error> { + use LktDbUpdateEvent::*; + match value { + 0 => Ok(Finished), + 1 => Ok(Progress), + _ => Err(()), + } + } +} + +impl TryFrom<usize> for LktPlaybackEvent { + type Error = (); + + fn try_from(value: usize) -> Result<Self, Self::Error> { + use LktPlaybackEvent::*; + match value { + 0 => Ok(Stop), + 1 => Ok(Play), + 2 => Ok(Pause), + 3 => Ok(Toggle), + _ => Err(()), + } + } +} + impl std::ops::BitOr<u32> for LktEventType { type Output = u32; fn bitor(self, rhs: u32) -> Self::Output { @@ -89,9 +145,9 @@ impl std::ops::BitOr<LktEventType> for LktEventType { pub mod LktMacroEvent { use super::LktEventType::*; lazy_static::lazy_static! { - pub static ref EVENT_PLAY: u32 = PlayPos | PlayFile | PlayNext | PlayPrev | PlayToggle | SkipCurrent; - pub static ref EVENT_PROP: u32 = PropVol | PropDur | PropTime; - pub static ref LKT_MACRO_EVENT_UPDATE: u32 = DbUpdating | DbUpdateTick | DbUpdateTotal | TouchKara; + pub static ref PLAY: u32 = PlayPos | PlayFile | PlayNext | PlayPrev | PlayToggle | SkipCurrent; + pub static ref PROP: u32 = PropVol | PropDur | PropTime; + pub static ref UPDATE: u32 = DbUpdating | DbUpdateTick | DbUpdateTotal | TouchKara; } } @@ -107,7 +163,7 @@ extern "C" { pub(crate) fn lkt_queue_has_event(_: LktQueuePtr, _: LktEventType) -> bool; pub(crate) fn lkt_queue_send(_: LktQueuePtr, _: LktEventType, _: *const c_void); pub(crate) fn lkt_queue_handle(_: LktQueuePtr) -> LktEvent; - pub(crate) fn lkt_queue_make_available(_: LktQueuePtr, _: LktEventType); + pub(crate) fn lkt_queue_make_available(_: LktQueuePtr, _: c_int); } unsafe fn str_len(str: *const c_char) -> usize { diff --git a/src/rust/liblektor-rs/lektor_c_compat/src/rs_types.rs b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types.rs deleted file mode 100644 index 7361a85908a18a61941456f5fe80ef5f8d33c570..0000000000000000000000000000000000000000 --- a/src/rust/liblektor-rs/lektor_c_compat/src/rs_types.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::c_types::*; - -pub struct LktCQueue { - ptr: LktQueuePtr, -} - -pub enum LktEventValue<'a> { - Null, - Integer(usize), - String(&'a str), -} - -pub struct LktCUri { - ptr: LktUriPtr, -} - -impl From<LktQueuePtr> for LktCQueue { - fn from(ptr: LktQueuePtr) -> Self { - Self { ptr } - } -} - -impl From<LktUriPtr> for LktCUri { - fn from(ptr: LktUriPtr) -> Self { - Self { ptr } - } -} - -impl LktCUri { - pub fn get_type(&self) -> LktUriType { - unsafe { lkt_uri_get_type(self.ptr) } - } - - pub fn get_value_type(&self) -> LktUriValueType { - unsafe { lkt_uri_get_value_type(self.ptr) } - } - - pub fn get_value_as_str(&self) -> Option<&str> { - use LktUriValueType::*; - match self.get_value_type() { - Null | Integer => None, - String => Some(unsafe { ptr_to_str(lkt_uri_get_value_as_str(self.ptr)) }), - } - } - - pub fn get_value_as_int(&self) -> Option<i32> { - use LktUriValueType::*; - match self.get_value_type() { - Null | String => None, - Integer => Some(unsafe { lkt_uri_get_value_as_int(self.ptr) }), - } - } - - fn get_column_name(&self) -> &str { - unsafe { ptr_to_str(lkt_uri_get_column_name(self.ptr)) } - } -} - -impl From<LktCUri> for String { - fn from(uri: LktCUri) -> Self { - match uri.get_value_type() { - LktUriValueType::Null => "".to_string(), - LktUriValueType::String => uri.get_value_as_str().unwrap_or_default().to_string(), - LktUriValueType::Integer => uri.get_value_as_int().unwrap_or_default().to_string(), - } - } -} - -impl LktCQueue { - pub fn has_event(&self, ty: LktEventType) -> bool { - unsafe { lkt_queue_has_event(self.ptr, ty) } - } - - pub fn send(&self, ty: LktEventType, val: LktEventValue) { - use LktEventValue::*; - let val = match val { - Null => std::ptr::null(), - Integer(x) => x as *const _, - String(str) => str.as_ptr() as *const _, - }; - unsafe { lkt_queue_send(self.ptr, ty, val) } - todo!("verify validity of (ty, val)") - } - - pub fn handle(&mut self) -> Option<(LktEventType, LktEventValue)> { - todo!("verify validity of (ty, val)") - } - - pub fn make_available(&mut self, ty: LktEventType) { - unsafe { lkt_queue_make_available(self.ptr, ty) } - } -} diff --git a/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/mod.rs b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..60b32d5ef8150fd83729900b1df2a8d6bf34ef1e --- /dev/null +++ b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/mod.rs @@ -0,0 +1,6 @@ +mod queue; +mod uri; + +pub use self::{queue::*, uri::*}; + +pub(self) use crate::c_types::*; diff --git a/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/queue.rs b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/queue.rs new file mode 100644 index 0000000000000000000000000000000000000000..72ff51947549774b0aaa8904c6f88d7a191d907a --- /dev/null +++ b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/queue.rs @@ -0,0 +1,156 @@ +use super::*; + +// Re-export some types, here enums because they are re-used in the safe part of +// the implementation. +pub use crate::c_types::{LktDbUpdateEvent, LktEventType, LktPlaybackEvent}; + +/// The safe wrapper around the lektord's queue implemented in the C part of the +/// code-base. +pub struct LktCQueue { + ptr: LktQueuePtr, +} + +#[derive(Debug)] +pub enum LktEventValue { + /// No event / null event. + Null, + + /// Action to play a kara by its position in the queue. + PlayPos(usize), + + /// Action to play next kara. + PlayNext, + + /// Action to play previous kara. + PlayPrev, + + /// Playback state changed. + PlayToggle(LktPlaybackEvent), + + /// The volume changed. This is buggy because MPV don't always notify us + /// when the volume is changed externally. + PropVol(usize), + + /// The total playtime of the current file changed, only when changing the + /// playing file. + PropDur(usize), + + /// Got the playback time of the currently played file. + PropTime(usize), + + /// Shall skip the current kara. + SkipCurrent, + + /// The status of the update process may have changed. + DbUpdating(LktDbUpdateEvent), + + /// Add a specified quantity to the update total. + DbUpdateTotal(usize), + + /// Mark a specified quantity as updated. + DbUpdateTick(usize), + + /// The specified kara was present during the update, don't delete it. + TouchKara(u64), +} + +impl LktEventValue { + pub fn get_type(&self) -> LktEventType { + match self { + LktEventValue::Null => LktEventType::Null, + LktEventValue::PlayPos(_) => LktEventType::PlayPos, + LktEventValue::PlayNext => LktEventType::PlayNext, + LktEventValue::PlayPrev => LktEventType::PlayPrev, + LktEventValue::PlayToggle(_) => LktEventType::PlayToggle, + LktEventValue::PropVol(_) => LktEventType::PropVol, + LktEventValue::PropDur(_) => LktEventType::PropDur, + LktEventValue::PropTime(_) => LktEventType::PropTime, + LktEventValue::SkipCurrent => LktEventType::SkipCurrent, + LktEventValue::DbUpdating(_) => LktEventType::DbUpdating, + LktEventValue::DbUpdateTotal(_) => LktEventType::DbUpdateTotal, + LktEventValue::DbUpdateTick(_) => LktEventType::DbUpdateTick, + LktEventValue::TouchKara(_) => LktEventType::TouchKara, + } + } +} + +impl From<LktQueuePtr> for LktCQueue { + fn from(ptr: LktQueuePtr) -> Self { + Self { ptr } + } +} + +impl LktCQueue { + pub fn has_event(&self, ty: LktEventType) -> bool { + unsafe { lkt_queue_has_event(self.ptr, ty) } + } + + pub fn send(&self, val: LktEventValue) { + use LktEventValue::*; + let ty = val.get_type(); + if ty == LktEventType::Null { + return; + } + + let val = match val { + PlayNext | PlayPrev | SkipCurrent | Null => std::ptr::null(), + + PlayToggle(x) => (x as usize) as *const _, + DbUpdating(x) => (x as usize) as *const _, + + PlayPos(x) | PropVol(x) | PropDur(x) | PropTime(x) | DbUpdateTotal(x) + | DbUpdateTick(x) => x as *const _, + + TouchKara(x) => (x as usize) as *const _, + }; + + unsafe { lkt_queue_send(self.ptr, ty, val) } + } + + pub fn handle(&mut self) -> Option<LktEventValue> { + use LktEventValue::*; + let LktEvent { ty, attr } = unsafe { lkt_queue_handle(self.ptr) }; + Some(match LktEventType::try_from(ty as usize).ok()? { + LktEventType::PlayToggle => PlayToggle(LktPlaybackEvent::try_from(attr as usize).ok()?), + LktEventType::DbUpdating => DbUpdating(LktDbUpdateEvent::try_from(attr as usize).ok()?), + LktEventType::TouchKara => TouchKara(u64::try_from(attr as usize).ok()?), + + LktEventType::PlayPos => PlayPos(attr as usize), + LktEventType::PropVol => PropVol(attr as usize), + LktEventType::PropDur => PropDur(attr as usize), + LktEventType::PropTime => PropTime(attr as usize), + LktEventType::DbUpdateTotal => DbUpdateTotal(attr as usize), + LktEventType::DbUpdateTick => DbUpdateTick(attr as usize), + + LktEventType::PlayNext => PlayNext, + LktEventType::PlayPrev => PlayPrev, + LktEventType::SkipCurrent => SkipCurrent, + + LktEventType::Null | LktEventType::PlayFile => return None, + }) + } + + /// Make an event available. + pub fn make_available(&mut self, ty: LktEventType) { + use crate::c_int; + unsafe { lkt_queue_make_available(self.ptr, (ty as usize) as c_int) } + } + + /// Make play event available. + pub fn make_available_playback_events(&mut self) { + use crate::{c_int, c_types::LktMacroEvent::PLAY}; + unsafe { lkt_queue_make_available(self.ptr, (*PLAY) as c_int) } + } + + /// Make properties event available. + pub fn make_available_property_events(&mut self) { + use crate::{c_int, c_types::LktMacroEvent::PROP}; + unsafe { lkt_queue_make_available(self.ptr, (*PROP) as c_int) } + } + + /// Make update event available. + pub fn make_available_update_events(&mut self) { + use crate::{c_int, c_types::LktMacroEvent::UPDATE}; + unsafe { lkt_queue_make_available(self.ptr, (*UPDATE) as c_int) } + } +} diff --git a/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/uri.rs b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/uri.rs new file mode 100644 index 0000000000000000000000000000000000000000..e8184010730e6be879d828afb98d345d81939786 --- /dev/null +++ b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/uri.rs @@ -0,0 +1,106 @@ +use super::*; + +/// The safe wrapper around the lektord's URI implemented in the C part of the +/// code-base. +pub struct LktCUri { + ptr: LktUriPtr, +} + +pub enum LktUriField { + /// The id of the kara must match the URI. + Id, + + /// One of the kara makers must match the value of the URI. + KaraMaker, + + /// The origin of the kara must match the URI. + Origin, + + /// The title of the kara must match the query. + Title, + + /// The source of the kara must match the query. + Source, + + /// The type of the kara must match the query. + Type, + + /// The language of the kara must match the value in the URI. + Language, + + /// Do a fuzzy search with the value if the URI. This is the default action + /// for an URI. + FuzzySearch, +} + +impl Default for LktUriField { + fn default() -> Self { + LktUriField::FuzzySearch + } +} + +impl From<LktUriPtr> for LktCUri { + fn from(ptr: LktUriPtr) -> Self { + Self { ptr } + } +} + +impl LktCUri { + pub fn get_type(&self) -> LktUriType { + unsafe { lkt_uri_get_type(self.ptr) } + } + + pub fn get_value_type(&self) -> LktUriValueType { + unsafe { lkt_uri_get_value_type(self.ptr) } + } + + /// Get the URI as a string slice, if the contained thing is not a string + /// slice, get None. Note that if the string is not valid using this + /// function may crash the system. If everything is alright in the C part of + /// the code, there should not be any issue. + fn get_value_as_str(&self) -> Option<&str> { + use LktUriValueType::*; + match self.get_value_type() { + Null | Integer => None, + String => Some(unsafe { ptr_to_str(lkt_uri_get_value_as_str(self.ptr)) }), + } + } + + /// Get the URI value as an int. If the contained thing is not an integer + /// returns None. + fn get_value_as_int(&self) -> Option<i32> { + use LktUriValueType::*; + match self.get_value_type() { + Null | String => None, + Integer => Some(unsafe { lkt_uri_get_value_as_int(self.ptr) }), + } + } + + /// Get the field that query the uri. See the `inc/lektor/common.h`, + /// `LKT_DB_*` defines. If those defines change, the implementation of this + /// function must also change. + pub fn get_query_field(&self) -> Option<LktUriField> { + use LktUriField::*; + match unsafe { ptr_to_str(lkt_uri_get_column_name(self.ptr)) } { + "id" => Some(Id), + "source_name" => Some(Source), + "song_title" => Some(Title), + "category" => Some(Origin), + "song_type" => Some(Type), + "author_name" => Some(KaraMaker), + "language" => Some(Language), + "string" => Some(FuzzySearch), + _ => None, + } + } +} + +impl From<LktCUri> for String { + fn from(uri: LktCUri) -> Self { + match uri.get_value_type() { + LktUriValueType::Null => "".to_string(), + LktUriValueType::String => uri.get_value_as_str().unwrap_or_default().to_string(), + LktUriValueType::Integer => uri.get_value_as_int().unwrap_or_default().to_string(), + } + } +}