A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

try to call browse function

work in progress

work in progress

work in progress

+343 -21
+14
apps/tree.c
··· 621 621 /* gets the directory's name and put it into tc.currdir */ 622 622 filename = strrchr(path+1,'/'); 623 623 size_t endpos = filename - path; 624 + printf("%s | %s | %d\n", filename, path, endpos); 624 625 if (filename && endpos < MAX_PATH - 1) 625 626 { 626 627 strmemccpy(tc.currdir, path, endpos + 1); ··· 955 956 #endif 956 957 957 958 default: 959 + // return GO_TO_ROOT; 958 960 if (default_event_handler(button) == SYS_USB_CONNECTED) 959 961 { 960 962 if(*tc.dirfilter > NUM_FILTER_MODES) ··· 1123 1125 tc = backups[backup_count]; 1124 1126 1125 1127 return ret_val; 1128 + } 1129 + 1130 + int rockbox_browse_at(const char* path) 1131 + { 1132 + struct browse_context browse = { 1133 + .dirfilter = SHOW_SUPPORTED, 1134 + .icon = Icon_NOICON, 1135 + .root = path, 1136 + }; 1137 + strcpy(tc.currdir, path); 1138 + 1139 + return rockbox_browse(&browse); 1126 1140 } 1127 1141 1128 1142 static int move_callback(int handle, void* current, void* new)
+1
apps/tree.h
··· 114 114 void set_dirfilter(int l_dirfilter); 115 115 void set_current_file(const char *path); 116 116 int rockbox_browse(struct browse_context *browse); 117 + int rockbox_browse_root(); 117 118 int create_playlist(void); 118 119 void resume_directory(const char *dir); 119 120
+9
crates/server/src/lib.rs
··· 202 202 stream.write_all(response.as_bytes()).unwrap(); 203 203 return; 204 204 } 205 + "/tree_context" => { 206 + let context = rb::browse::tree_get_context(); 207 + let response = format!( 208 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", 209 + serde_json::to_string(&context).unwrap() 210 + ); 211 + stream.write_all(response.as_bytes()).unwrap(); 212 + return; 213 + } 205 214 _ => { 206 215 if path.starts_with("/play?") { 207 216 let params: Vec<_> = path.split('?').collect();
+19 -8
crates/sys/src/browse.rs
··· 1 1 use std::ffi::CString; 2 2 3 - use crate::{AddToPlCallback, BrowseContext, Entry, Mp3Entry, PlaylistInsertCb, Tm, TreeContext}; 3 + use crate::{ 4 + types::tree::{Entry, TreeContext}, 5 + AddToPlCallback, Mp3Entry, PlaylistInsertCb, Tm, 6 + }; 4 7 5 - pub fn rockbox_browse(ctx: *mut BrowseContext) -> i32 { 6 - unsafe { crate::rockbox_browse(ctx) } 8 + pub fn rockbox_browse_at(path: &str) -> i32 { 9 + let path = CString::new(path).unwrap(); 10 + unsafe { crate::rockbox_browse_at(path.as_ptr()) } 11 + } 12 + 13 + pub fn rockbox_browse() -> i32 { 14 + unsafe { crate::rb_rockbox_browse() } 7 15 } 8 16 9 17 pub fn tree_get_context() -> TreeContext { 10 - unsafe { crate::tree_get_context() } 18 + let tc = unsafe { crate::rb_tree_get_context() }; 19 + tc.into() 11 20 } 12 21 13 - pub fn tree_get_entries(ctx: *mut TreeContext) -> Entry { 14 - unsafe { crate::tree_get_entries(ctx) } 22 + pub fn tree_get_entries() -> Entry { 23 + let entry = unsafe { crate::rb_tree_get_entries() }; 24 + entry.into() 15 25 } 16 26 17 - pub fn tree_get_entry_at(ctx: *mut TreeContext, index: i32) -> Entry { 18 - unsafe { crate::tree_get_entry_at(ctx, index) } 27 + pub fn tree_get_entry_at(index: i32) -> Entry { 28 + let entry = unsafe { crate::rb_tree_get_entry_at(index) }; 29 + entry.into() 19 30 } 20 31 21 32 pub fn set_current_file(path: &str) {
+32 -7
crates/sys/src/lib.rs
··· 51 51 }; 52 52 } 53 53 54 + #[macro_export] 55 + macro_rules! convert_ptr_to_vec { 56 + ($ptr:expr, $len:expr) => {{ 57 + if $ptr.is_null() { 58 + Vec::new() 59 + } else { 60 + // Safety: Ensure that the pointer is valid for $len elements, 61 + // and that the memory was allocated in a way that is compatible with Rust's Vec. 62 + unsafe { Vec::from_raw_parts($ptr, $len, $len) } 63 + } 64 + }}; 65 + } 66 + 67 + #[macro_export] 68 + macro_rules! ptr_to_option { 69 + ($ptr:expr) => { 70 + if $ptr.is_null() { 71 + None 72 + } else { 73 + unsafe { Some(*$ptr) } 74 + } 75 + }; 76 + } 77 + 54 78 #[repr(C)] 55 79 #[derive(Debug, Copy, Clone)] 56 80 pub struct Mp3Entry { ··· 224 248 } 225 249 226 250 #[repr(C)] 227 - #[derive(Debug)] 251 + #[derive(Debug, Copy, Clone)] 228 252 pub struct BrowseContext { 229 253 pub dirfilter: c_int, // int dirfilter 230 254 pub flags: c_uint, // unsigned flags ··· 1042 1066 // Playlist control 1043 1067 fn playlist_get_current() -> PlaylistInfo; 1044 1068 fn playlist_get_resume_info(resume_index: *mut c_int) -> c_int; 1045 - fn _get_track_info_from_current_playlist(index: i32) -> PlaylistTrackInfo; 1069 + fn rb_get_track_info_from_current_playlist(index: i32) -> PlaylistTrackInfo; 1046 1070 fn playlist_get_first_index(playlist: *mut PlaylistInfo) -> c_int; 1047 1071 fn playlist_get_display_index() -> c_int; 1048 1072 fn playlist_amount() -> c_int; ··· 1132 1156 fn keyclick_click(rawbutton: c_uchar, action: c_int); 1133 1157 1134 1158 // Browsing 1135 - fn rockbox_browse(browse: *mut BrowseContext) -> c_int; 1136 - fn tree_get_context() -> TreeContext; 1137 - fn tree_get_entries(t: *mut TreeContext) -> Entry; 1138 - fn tree_get_entry_at(t: *mut TreeContext, index: c_int) -> Entry; 1159 + fn rockbox_browse_at(path: *const c_char) -> c_int; 1160 + fn rb_rockbox_browse() -> c_int; 1161 + fn rb_tree_get_context() -> TreeContext; 1162 + fn rb_tree_get_entries() -> Entry; 1163 + fn rb_tree_get_entry_at(index: c_int) -> Entry; 1139 1164 fn set_current_file(path: *const c_char); 1140 1165 fn set_dirfilter(l_dirfilter: c_int); 1141 1166 fn onplay_show_playlist_menu( ··· 1187 1212 fn filetype_get_plugin(); 1188 1213 1189 1214 // Metadata 1190 - fn _get_metadata(fd: i32, trackname: *const c_char) -> Mp3Entry; 1215 + fn rb_get_metadata(fd: i32, trackname: *const c_char) -> Mp3Entry; 1191 1216 fn get_codec_string(codectype: c_int) -> *const c_char; 1192 1217 fn count_mp3_frames( 1193 1218 fd: c_int,
+1 -1
crates/sys/src/metadata.rs
··· 4 4 5 5 pub fn get_metadata(fd: i32, trackname: &str) -> Mp3Entry { 6 6 let trackname = CString::new(trackname).unwrap(); 7 - let id3 = unsafe { crate::_get_metadata(fd, trackname.as_ptr()) }; 7 + let id3 = unsafe { crate::rb_get_metadata(fd, trackname.as_ptr()) }; 8 8 id3.into() 9 9 } 10 10
+1 -1
crates/sys/src/playlist.rs
··· 11 11 } 12 12 13 13 pub fn get_track_info(index: i32) -> PlaylistTrackInfo { 14 - let track_info = unsafe { crate::_get_track_info_from_current_playlist(index) }; 14 + let track_info = unsafe { crate::rb_get_track_info_from_current_playlist(index) }; 15 15 track_info.into() 16 16 } 17 17
+1
crates/sys/src/types/mod.rs
··· 8 8 pub mod playlist_track_info; 9 9 pub mod system_status; 10 10 pub mod user_settings; 11 + pub mod tree; 11 12 12 13 #[derive(Serialize, Deserialize)] 13 14 pub struct RockboxVersion {
+113
crates/sys/src/types/tree.rs
··· 1 + use serde::{Deserialize, Serialize}; 2 + 3 + use crate::{cast_ptr, convert_ptr_to_vec, get_string_from_ptr, ptr_to_option}; 4 + 5 + #[derive(Debug, Serialize, Deserialize)] 6 + pub struct TreeCache { 7 + pub entries_handle: i32, // int entries_handle 8 + pub name_buffer_handle: i32, // int name_buffer_handle 9 + pub max_entries: i32, // int max_entries 10 + pub name_buffer_size: i32, // int name_buffer_size (in bytes) 11 + } 12 + 13 + impl From<crate::TreeCache> for TreeCache { 14 + fn from(cache: crate::TreeCache) -> Self { 15 + Self { 16 + entries_handle: cache.entries_handle, 17 + name_buffer_handle: cache.name_buffer_handle, 18 + max_entries: cache.max_entries, 19 + name_buffer_size: cache.name_buffer_size, 20 + } 21 + } 22 + } 23 + 24 + #[derive(Debug, Serialize, Deserialize)] 25 + pub struct TreeContext { 26 + pub currdir: String, // char currdir[MAX_PATH] 27 + pub dirlevel: i32, // int dirlevel 28 + pub selected_item: i32, // int selected_item 29 + pub selected_item_history: Vec<i32>, // int selected_item_history[MAX_DIR_LEVELS] 30 + pub filesindir: i32, // int filesindir 31 + pub dirsindir: i32, // int dirsindir 32 + pub dirlength: i32, // int dirlength 33 + pub currtable: i32, // int currtable (db use) 34 + pub currextra: i32, // int currextra (db use) 35 + pub sort_dir: i32, // int sort_dir 36 + pub out_of_tree: i32, // int out_of_tree 37 + pub cache: TreeCache, // struct tree_cache cache 38 + pub dirfull: bool, // bool dirfull 39 + pub is_browsing: bool, // bool is_browsing 40 + pub browse: Option<BrowseContext>, // struct browse_context* browse 41 + } 42 + 43 + impl From<crate::TreeContext> for TreeContext { 44 + fn from(context: crate::TreeContext) -> Self { 45 + Self { 46 + currdir: unsafe { 47 + std::ffi::CStr::from_ptr(cast_ptr!(context.currdir.as_ptr())) 48 + .to_string_lossy() 49 + .into_owned() 50 + }, 51 + dirlevel: context.dirlevel, 52 + selected_item: context.selected_item, 53 + selected_item_history: context.selected_item_history.to_vec(), 54 + filesindir: context.filesindir, 55 + dirsindir: context.dirsindir, 56 + dirlength: context.dirlength, 57 + currtable: context.currtable, 58 + currextra: context.currextra, 59 + sort_dir: context.sort_dir, 60 + out_of_tree: context.out_of_tree, 61 + cache: context.cache.into(), 62 + dirfull: context.dirfull, 63 + is_browsing: context.is_browsing, 64 + browse: None, 65 + // browse: ptr_to_option!(context.browse).map(|browse| browse.into()), 66 + } 67 + } 68 + } 69 + 70 + #[derive(Debug, Serialize, Deserialize)] 71 + pub struct BrowseContext { 72 + pub dirfilter: i32, // int dirfilter 73 + pub flags: u32, // unsigned flags 74 + pub title: String, // char* title 75 + // pub icon: ThemableIcons, // enum themable_icons icon 76 + pub root: String, // const char* root 77 + pub selected: String, // const char* selected 78 + pub buf: Vec<i8>, // char* buf 79 + pub bufsize: usize, // size_t bufsize 80 + } 81 + 82 + impl From<crate::BrowseContext> for BrowseContext { 83 + fn from(context: crate::BrowseContext) -> Self { 84 + Self { 85 + dirfilter: context.dirfilter, 86 + flags: context.flags, 87 + title: get_string_from_ptr!(context.title), 88 + root: get_string_from_ptr!(context.root), 89 + selected: get_string_from_ptr!(context.selected), 90 + buf: convert_ptr_to_vec!(context.buf, context.bufsize), 91 + bufsize: context.bufsize, 92 + } 93 + } 94 + } 95 + 96 + #[derive(Debug, Serialize, Deserialize)] 97 + pub struct Entry { 98 + pub name: String, // char* name 99 + pub attr: i32, // int attr (FAT attributes + file type flags) 100 + pub time_write: u32, // unsigned time_write (Last write time) 101 + pub customaction: i32, // int customaction (db use) 102 + } 103 + 104 + impl From<crate::Entry> for Entry { 105 + fn from(entry: crate::Entry) -> Self { 106 + Self { 107 + name: get_string_from_ptr!(entry.name), 108 + attr: entry.attr, 109 + time_write: entry.time_write, 110 + customaction: entry.customaction, 111 + } 112 + } 113 + }
+22 -2
src/main.zig
··· 1 1 const std = @import("std"); 2 2 const playlist = @import("rockbox/playlist.zig"); 3 3 const metadata = @import("rockbox/metadata.zig"); 4 + const tree = @import("rockbox/tree.zig"); 4 5 5 6 extern fn main_c() c_int; 6 7 extern fn parse_args(argc: usize, argv: [*]const [*]const u8) c_int; ··· 27 28 _ = main_c(); 28 29 } 29 30 30 - export fn _get_track_info_from_current_playlist(index: c_int) playlist.PlaylistTrackInfo { 31 + // playlist functions 32 + export fn rb_get_track_info_from_current_playlist(index: c_int) playlist.PlaylistTrackInfo { 31 33 return playlist._get_track_info_from_current_playlist(index); 32 34 } 33 35 34 - export fn _get_metadata(fd: c_int, trackname: [*]const u8) metadata.mp3entry { 36 + // metadata functions 37 + export fn rb_get_metadata(fd: c_int, trackname: [*]const u8) metadata.mp3entry { 35 38 return metadata._get_metadata(fd, trackname); 36 39 } 40 + 41 + // browsing functions 42 + export fn rb_rockbox_browse() c_int { 43 + return tree._rockbox_browse(); 44 + } 45 + 46 + export fn rb_tree_get_context() tree.tree_context { 47 + return tree._tree_get_context(); 48 + } 49 + 50 + export fn rb_tree_get_entries() *tree.entry { 51 + return tree._tree_get_entries(); 52 + } 53 + 54 + export fn rb_tree_get_entry_at(index: c_int) *tree.entry { 55 + return tree._tree_get_entry_at(index); 56 + }
-2
src/rockbox/playlist.zig
··· 1 - const std = @import("std"); 2 - 3 1 const MAX_PATH = 260; 4 2 const PLAYLIST_CONTROL_FILE_SIZE = 256; 5 3
+130
src/rockbox/tree.zig
··· 1 + const std = @import("std"); 2 + 3 + pub const MAX_PATH = 260; 4 + pub const MAX_DIR_LEVELS = 10; 5 + 6 + const themable_icons = enum(c_int) { 7 + NOICON = -2, 8 + Icon_NOICON = -1, 9 + Icon_Audio, 10 + Icon_Folder, 11 + Icon_Playlist, 12 + Icon_Cursor, 13 + Icon_Wps, 14 + Icon_Firmware, 15 + Icon_Font, 16 + Icon_Language, 17 + Icon_Config, 18 + Icon_Plugin, 19 + Icon_Bookmark, 20 + Icon_Preset, 21 + Icon_Queued, 22 + Icon_Moving, 23 + Icon_Keyboard, 24 + Icon_Reverse_Cursor, 25 + Icon_Questionmark, 26 + Icon_Menu_setting, 27 + Icon_Menu_functioncall, 28 + Icon_Submenu, 29 + Icon_Submenu_Entered, 30 + Icon_Recording, 31 + Icon_Voice, 32 + Icon_General_settings_menu, 33 + Icon_System_menu, 34 + Icon_Playback_menu, 35 + Icon_Display_menu, 36 + Icon_Remote_Display_menu, 37 + Icon_Radio_screen, 38 + Icon_file_view_menu, 39 + Icon_EQ, 40 + Icon_Rockbox, 41 + Icon_Last_Themeable, 42 + }; 43 + 44 + pub const browse_context = extern struct { 45 + dirfilter: c_int, 46 + flags: c_uint, // 'unsigned' is translated to 'c_uint' 47 + title: [*c]const u8, // Nullable C string for the title 48 + icon: themable_icons, // Enum 'themable_icons', assumed to be defined elsewhere 49 + root: [*c]const u8, // Const C string for root directory path 50 + selected: [*c]const u8, // Const C string for selected file name 51 + buf: [*c]u8, // Buffer for the selected file 52 + bufsize: usize, // Size of the buffer (translated to 'usize' for portability) 53 + }; 54 + 55 + const tree_cache = extern struct { 56 + entries_handle: c_int, // Handle to the entry cache 57 + name_buffer_handle: c_int, // Handle to the name cache 58 + max_entries: c_int, // Maximum number of entries in the cache 59 + name_buffer_size: c_int, // Size of the name buffer (in bytes) 60 + }; 61 + 62 + pub const tree_context = extern struct { 63 + currdir: [MAX_PATH]u8, // Fixed-size array for the current directory 64 + dirlevel: i32, // int in C is c_int in Zig 65 + selected_item: i32, // Selected file/id3dbitem index 66 + selected_item_history: [MAX_DIR_LEVELS]c_int, // History of selected items, fixed-size array 67 + 68 + dirfilter: ?*c_int, // Nullable pointer to an int for file use 69 + filesindir: i32, // Number of files in the directory cache 70 + dirsindir: i32, // Directory use 71 + dirlength: i32, // Total number of entries in directory 72 + 73 + currtable: i32, 74 + currextra: i32, 75 + 76 + sort_dir: i32, // Directory sort order 77 + out_of_tree: i32, // Shortcut from elsewhere 78 + cache: tree_cache, // Struct tree_cache, defined elsewhere 79 + 80 + dirfull: bool, 81 + is_browsing: bool, 82 + 83 + browse: ?*browse_context, // Pointer to browse_context, nullable 84 + }; 85 + pub const entry = extern struct { 86 + name: ?*c_char, // Pointer to the name (nullable) 87 + attr: c_int, // FAT attributes + file type flags 88 + time_write: c_uint, // Last write time (unsigned) 89 + customaction: c_int, // Custom action (for database use) 90 + }; 91 + 92 + extern fn tree_init() void; 93 + extern fn rockbox_browse(browse: ?*browse_context) c_int; 94 + extern fn rockbox_browse_at(path: [*]const u8) c_int; 95 + extern fn tree_get_context() *tree_context; 96 + extern fn tree_get_entries(t: *tree_context) *entry; 97 + extern fn tree_get_entry_at(t: *tree_context, index: c_int) *entry; 98 + 99 + pub fn _rockbox_browse() c_int { 100 + var browse: browse_context = .{ 101 + .dirfilter = 0, 102 + .flags = 0, 103 + .title = "demo", 104 + .icon = themable_icons.Icon_NOICON, 105 + .root = "/", 106 + .selected = null, 107 + .buf = null, 108 + .bufsize = 0, 109 + }; 110 + return rockbox_browse(&browse); 111 + } 112 + 113 + pub fn _tree_get_context() tree_context { 114 + const ret = rockbox_browse_at("/home"); 115 + std.debug.print("rockbox_browse_root: {}\n", .{ret}); 116 + const tc = tree_get_context(); 117 + const e = tree_get_entries(tc); 118 + std.debug.print("tree_get_context: {}\n{}\n", .{ tc, e }); 119 + return tc.*; 120 + } 121 + 122 + pub fn _tree_get_entries() *entry { 123 + const tc = tree_get_context(); 124 + return tree_get_entries(tc); 125 + } 126 + 127 + pub fn _tree_get_entry_at(index: c_int) *entry { 128 + const tc = tree_get_context(); 129 + return tree_get_entry_at(tc, index); 130 + }