#import "Basic"; // Build the C library first (only .so — Jai links dynamically, which pulls in libsystemd automatically): // cc -fPIC -shared -o linux/libmpris.so mpris.c -lsystemd libmpris :: #library "linux/libmpris"; /* ── Types ──────────────────────────────────────────────────────────────── */ Mpris_Player :: *void; // Callbacks must be #c_call. Pass your context pointer as userdata. Mpris_Callback :: #type (userdata: *void) -> void #c_call; Mpris_Seek_Callback :: #type (offset_us: s64, userdata: *void) -> void #c_call; Mpris_Set_Position_Callback :: #type (track_id: *u8, position_us: s64, userdata: *void) -> void #c_call; Mpris_Volume_Callback :: #type (volume: float64, userdata: *void) -> void #c_call; /* ── Raw C bindings ─────────────────────────────────────────────────────── */ _mpris_player_create :: (player_name: *u8, identity: *u8) -> Mpris_Player #foreign libmpris "mpris_player_create"; _mpris_player_destroy :: (p: Mpris_Player) #foreign libmpris "mpris_player_destroy"; _mpris_set_playback_status :: (p: Mpris_Player, status: *u8) #foreign libmpris "mpris_set_playback_status"; _mpris_set_metadata :: (p: Mpris_Player, track_id: *u8, title: *u8, artist: *u8, album: *u8, length_us: s64) #foreign libmpris "mpris_set_metadata"; _mpris_set_position :: (p: Mpris_Player, position_us: s64) #foreign libmpris "mpris_set_position"; _mpris_set_volume :: (p: Mpris_Player, volume: float64) #foreign libmpris "mpris_set_volume"; _mpris_set_can_go_next :: (p: Mpris_Player, value: s32) #foreign libmpris "mpris_set_can_go_next"; _mpris_set_can_go_previous :: (p: Mpris_Player, value: s32) #foreign libmpris "mpris_set_can_go_previous"; _mpris_set_can_play :: (p: Mpris_Player, value: s32) #foreign libmpris "mpris_set_can_play"; _mpris_set_can_pause :: (p: Mpris_Player, value: s32) #foreign libmpris "mpris_set_can_pause"; _mpris_set_can_seek :: (p: Mpris_Player, value: s32) #foreign libmpris "mpris_set_can_seek"; _mpris_emit_seeked :: (p: Mpris_Player, position_us: s64) #foreign libmpris "mpris_emit_seeked"; _mpris_on_play :: (p: Mpris_Player, cb: Mpris_Callback, userdata: *void) #foreign libmpris "mpris_on_play"; _mpris_on_pause :: (p: Mpris_Player, cb: Mpris_Callback, userdata: *void) #foreign libmpris "mpris_on_pause"; _mpris_on_play_pause :: (p: Mpris_Player, cb: Mpris_Callback, userdata: *void) #foreign libmpris "mpris_on_play_pause"; _mpris_on_stop :: (p: Mpris_Player, cb: Mpris_Callback, userdata: *void) #foreign libmpris "mpris_on_stop"; _mpris_on_next :: (p: Mpris_Player, cb: Mpris_Callback, userdata: *void) #foreign libmpris "mpris_on_next"; _mpris_on_previous :: (p: Mpris_Player, cb: Mpris_Callback, userdata: *void) #foreign libmpris "mpris_on_previous"; _mpris_on_seek :: (p: Mpris_Player, cb: Mpris_Seek_Callback, userdata: *void) #foreign libmpris "mpris_on_seek"; _mpris_on_set_position :: (p: Mpris_Player, cb: Mpris_Set_Position_Callback, userdata: *void) #foreign libmpris "mpris_on_set_position"; _mpris_on_volume :: (p: Mpris_Player, cb: Mpris_Volume_Callback, userdata: *void) #foreign libmpris "mpris_on_volume"; _mpris_process :: (p: Mpris_Player) -> s32 #foreign libmpris "mpris_process"; /* ── Jai-friendly wrappers (handle string conversion) ───────────────────── */ // player_name: D-Bus name component, no spaces (e.g. "MyPlayer") // identity: Human-readable name shown in media menus (e.g. "My Music Player") mpris_player_create :: (player_name: string, identity: string) -> Mpris_Player { n := temp_c_string(player_name); i := temp_c_string(identity); return _mpris_player_create(n, i); } mpris_player_destroy :: (p: Mpris_Player) { _mpris_player_destroy(p); } // status: "Playing" | "Paused" | "Stopped" mpris_set_playback_status :: (p: Mpris_Player, status: string) { _mpris_set_playback_status(p, temp_c_string(status)); } Mpris_Metadata :: struct { track_id : string; // D-Bus object path, e.g. "/myapp/track/42". Leave empty for auto. title : string; artist : string; album : string; length_us : s64; // Duration in microseconds. 0 = unknown. } mpris_set_metadata :: (p: Mpris_Player, meta: Mpris_Metadata) { tid := ifx meta.track_id then temp_c_string(meta.track_id) else null; t := ifx meta.title then temp_c_string(meta.title) else null; a := ifx meta.artist then temp_c_string(meta.artist) else null; al := ifx meta.album then temp_c_string(meta.album) else null; _mpris_set_metadata(p, tid, t, a, al, meta.length_us); } mpris_set_position :: (p: Mpris_Player, position_us: s64) { _mpris_set_position(p, position_us); } mpris_set_volume :: (p: Mpris_Player, volume: float64) { _mpris_set_volume(p, volume); } mpris_set_can_go_next :: (p: Mpris_Player, v: bool) { _mpris_set_can_go_next(p, cast(s32) ifx v then 1 else 0); } mpris_set_can_go_previous :: (p: Mpris_Player, v: bool) { _mpris_set_can_go_previous(p, cast(s32) ifx v then 1 else 0); } mpris_set_can_play :: (p: Mpris_Player, v: bool) { _mpris_set_can_play(p, cast(s32) ifx v then 1 else 0); } mpris_set_can_pause :: (p: Mpris_Player, v: bool) { _mpris_set_can_pause(p, cast(s32) ifx v then 1 else 0); } mpris_set_can_seek :: (p: Mpris_Player, v: bool) { _mpris_set_can_seek(p, cast(s32) ifx v then 1 else 0); } mpris_emit_seeked :: (p: Mpris_Player, position_us: s64) { _mpris_emit_seeked(p, position_us); } mpris_on_play :: (p: Mpris_Player, cb: Mpris_Callback, ud: *void) { _mpris_on_play(p, cb, ud); } mpris_on_pause :: (p: Mpris_Player, cb: Mpris_Callback, ud: *void) { _mpris_on_pause(p, cb, ud); } mpris_on_play_pause :: (p: Mpris_Player, cb: Mpris_Callback, ud: *void) { _mpris_on_play_pause(p, cb, ud); } mpris_on_stop :: (p: Mpris_Player, cb: Mpris_Callback, ud: *void) { _mpris_on_stop(p, cb, ud); } mpris_on_next :: (p: Mpris_Player, cb: Mpris_Callback, ud: *void) { _mpris_on_next(p, cb, ud); } mpris_on_previous :: (p: Mpris_Player, cb: Mpris_Callback, ud: *void) { _mpris_on_previous(p, cb, ud); } mpris_on_seek :: (p: Mpris_Player, cb: Mpris_Seek_Callback, ud: *void) { _mpris_on_seek(p, cb, ud); } mpris_on_set_position :: (p: Mpris_Player, cb: Mpris_Set_Position_Callback, ud: *void) { _mpris_on_set_position(p, cb, ud); } mpris_on_volume :: (p: Mpris_Player, cb: Mpris_Volume_Callback, ud: *void) { _mpris_on_volume(p, cb, ud); } // Returns true if messages were processed, false if idle, exits on error. mpris_process :: (p: Mpris_Player) -> bool { r := _mpris_process(p); if r < 0 { log_error("mpris_process failed: %", r); return false; } return r > 0; }