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(),
+        }
+    }
+}