mpd-libui
Version, currently main branch1 version
- main branchlatestJul 6, 2026
github.com/mamantoha/mpd-libui
A desktop MPD client written in Crystal using the UIng (libui-ng) GUI library
Installation
# Add this to your shard.yml
dependencies:
mpd-libui:
github: mamantoha/mpd-libui
branch: mainmain is a branch, not a release, so this tracks it rather than pinning a version.
Then run:
shards installshard.yml
- Crystal
>= 1.19.1- License
- MIT
- Author
- mamantoha
- Target
mpd-libuifrom src/mpd_ui.cr
Dependencies
Runtime Dependencies
- uing*github: kojix2/uing
- stumpy_png*github: stumpycr/stumpy_png
- crystal_mpd*github: mamantoha/crystal_mpd
- stumpy_jpeg*github: stumpycr/stumpy_jpeg
README
Crystal MPD libui
A desktop MPD client written in Crystal using the UIng (libui-ng) GUI library.
Screenshots

Features
- Playback controls: play/pause, previous, next
- Shuffle and repeat toggle buttons
- Seek slider with elapsed / total time display
- Album cover art fetched from MPD (
readpicture/albumart) - Integrated playlist with Artist, Title, Duration columns
- Currently playing track highlighted and auto-scrolled into view
- Window title updates to reflect the current track
- Settings window for MPD host / port configuration
- About dialog with MPD server stats (version, artists, albums, songs, uptime)
Requirements
- Crystal >= 1.19.1
- libui-ng shared library installed
- GTK3 (required by libui-ng on Linux)
- A running MPD server
Installation
git clone https://github.com/mamantoha/mpd-libui
cd mpd-libui
shards install
crystal build src/mpd_ui.cr --release -o mpd-libui
./mpd-libui
Dependencies
| Shard | Purpose | |---|---| | kojix2/uing | libui-ng bindings — native desktop GUI | | mamantoha/crystal_mpd | MPD protocol client | | stumpycr/stumpy_png | Pure-Crystal PNG decoding for cover art | | stumpycr/stumpy_jpeg | Pure-Crystal JPEG decoding for cover art |
Platform support
| Platform | Status | |---|---| | Linux (GTK3) | ✅ Tested | | macOS | ❓ Untested | | Windows | ❓ Untested |
Custom components
ToggleButton (src/mpd_ui/toggle_button.cr)
UIng does not provide a toggle button widget. ToggleButton is a custom widget built on top of UIng::Area (a libui-ng raw drawing surface).
- Renders a filled rectangle with a centered emoji/text label using the
UIng::Area::DrawAPI (paths, brushes, text layouts) - Active state shown as a blue highlight; hover tracked for a subtle gray tint
- Minimum size enforced via
uiControlHandle+gtk_widget_set_size_requestbecauseuiNewAreahas no natural GTK size hint - Font loaded once via
UIng::FontDescriptor(uiLoadControlFont) and cached as an instance variable to avoid per-frame allocation and Pango errors AttributedStringandTextLayoutcreated and freed deterministically each draw using theopen { }block API to prevent libui leak warnings at shutdown
PlaylistView (src/mpd_ui/playlist_view.cr)
A UIng::Table-backed playlist widget embedded directly in the main window.
- Custom
UIng::Table::Model::Handlerbacked by a mutableArray(Song)that is updated in-place (clear+concat) so the model handler closure always sees the live array — replacing the array reference caused auiTableModel_get_value: stampassertion crash - Per-row background color column (type
Color) highlights the active track with a blue tint - Auto-scrolls to the playing row using raw GTK calls:
uiControlHandle→gtk_bin_get_child(GtkScrolledWindow → GtkTreeView) →gtk_tree_view_scroll_to_cell - Proper cleanup on window close:
table.destroybeforemodel.free, matching libui-ng's required teardown order
Architecture notes
- Dual MPD clients: a regular
MPD::Clientfor commands and a second client withwith_callbacks: truerunning in a backgroundThreadfor push events (song change, state, elapsed, random, repeat, playlist) - UI thread safety: all UI mutations from the callback thread go through
UIng.queue_main { } - Cover art: fetched in a third
Threadusing a dedicated MPD connection; decoded in pure Crystal (no subprocess) viaStumpyJPEG/StumpyPNGto avoidProcess.rundeadlock inside threads (Crystal'swaitpidsignal handler is occupied byUIng.main) - Stale result guard:
@current_fileis compared before applying a fetched cover image to discard results that arrived after the track changed
License
MIT
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This branch
- Branch
main- Seen
- Jul 6, 2026
- Crystal
>= 1.19.1- Indexed
- yes
Dependents
No indexed shard depends on this one yet.
Repository
github.com/mamantoha/mpd-libui
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 15, 2026
- Synced
- Aug 15, 2026
- Versions
- 1