diff --git a/configs/applications/sxmo-mpv-music.desktop b/configs/applications/sxmo-mpv-music.desktop new file mode 100644 index 0000000..febe0c3 --- /dev/null +++ b/configs/applications/sxmo-mpv-music.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=mpv Music Player +Exec=mpv -ao=alsa --vid=no -v +Terminal=true +Type=Application +MimeType=audio/wav;audio/opus;audio/m4a;audio/flac;audio/mp3 diff --git a/configs/applications/sxmo-sxiv.desktop b/configs/applications/sxmo-sxiv.desktop new file mode 100644 index 0000000..352bc2e --- /dev/null +++ b/configs/applications/sxmo-sxiv.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=sxiv Image Viewer +Exec=sxiv +Terminal=false +Type=Application +MimeType=image/jpeg;image/png;image/gif diff --git a/configs/applications/sxmo-zathura.desktop b/configs/applications/sxmo-zathura.desktop new file mode 100644 index 0000000..f7f425c --- /dev/null +++ b/configs/applications/sxmo-zathura.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Zathura +Exec=zathura +Terminal=false +Type=Application +MimeType=application/pdf;application/x-rar;application/epub+zip diff --git a/scripts/appscripts/sxmo_files.sh b/scripts/appscripts/sxmo_files.sh index 280f4b9..70df743 100755 --- a/scripts/appscripts/sxmo_files.sh +++ b/scripts/appscripts/sxmo_files.sh @@ -3,18 +3,6 @@ DIR="$1" [ -z "$DIR" ] && DIR="/home/$USER/" cd "$DIR" || exit 1 -handlefiles() { - if echo "$1" | grep -iE ".(webm|ogg|mp4|mov|avi|mkv)$"; then - st -e mpv -ao=alsa "$@" - elif echo "$1" | grep -iE ".(wav|opus|m4a|flac|mp3)$"; then - st -e mpv -ao=alsa --vid=no -v "$@" - elif echo "$1" | grep -iE ".(jpg|png|gif)$"; then - st -e sxiv "$@" - else - st -e sh -ic "$EDITOR $*" - fi -} - while true; do CHOICES="$(printf %b 'Close Menu\n../\n*\n'"$(ls -1p)")" DIR="$(basename "$(pwd)")" @@ -31,6 +19,6 @@ while true; do echo "$PICKED" | grep "Close Menu" && exit 0 [ -d "$PICKED" ] && cd "$PICKED" && continue - echo "$PICKED" | grep -E '^[*]$' && handlefiles ./* - [ -f "$PICKED" ] && handlefiles "$PICKED" + echo "$PICKED" | grep -E '^[*]$' && sxmo_open.sh -a ./* + [ -f "$PICKED" ] && sxmo_open.sh -a "$PICKED" done diff --git a/scripts/core/sxmo_open.sh b/scripts/core/sxmo_open.sh new file mode 100755 index 0000000..fe29a94 --- /dev/null +++ b/scripts/core/sxmo_open.sh @@ -0,0 +1,324 @@ +#!/usr/bin/env sh + +MIMEAPPS="${XDG_CONFIG_HOME:-$HOME/.config}/mimeapps.list" +DESKTOPS_CACHED_MIMEAPPS="${XDG_CONFIG_HOME:-$HOME/.config}/desktops.mimeapps.list" +DESKTOP_DIRS="/usr/share/sxmo/applications/|/usr/share/applications/|/usr/local/share/applications/|${XDG_DATA_HOME:-$HOME/.local/share}/applications/" +attached= +debug= +TERMCMD="${TERMCMD:-st -e}" + +# This will convert a mimeapps.list to a parsable mapping +# Lines with multiple mimetype will be splitted +# The mapping format is something as +# added;image/jpeg;bar.desktop +# added;image/jpeg;baz.desktop +# added;video/H264;bar.desktop +# default;image/jpeg;foo.desktop +# removed;video/H264;baz.desktop +mimeapps_to_desktop_mapping() { + [ ! -f "$1" ] && return + awk ' + /^\[Default Applications\]$/ { kind = "default" } + /^\[Added Associations\]$/ { kind = "added" } + /^\[Removed Associations\]$/ { kind = "removed" } + /=/ { + split($0, mime_desktops, "=") + split(mime_desktops[2], desktops, ";") + + for ( key in desktops ) { + print kind ";" mime_desktops[1] ";" desktops[key] + } + } + ' "$1" +} + +# This will generate the added mapping from all destkop entries in a dir +get_mimeapps_entries_from_desktop_dir() { + for desktop_path in "$1"/*.desktop; do + grep --include "*.desktop" '^MimeType=' "$desktop_path" \ + | tr ';' '\n' \ + | cut -d= -f2 \ + | xargs -i{} printf "%s=%s\n" "{}" "$(basename "$desktop_path")" + done +} + +# Build and save the desktop mimeapps mapping if necessary +prepare_desktop_mimeapps_mapping_cache() { + last_desktop_modif_date="$(printf %s "$DESKTOP_DIRS" | tr '|' '\n' | while read -r desktop_dir; do + for desktop_file in "$desktop_dir"/*.desktop; do + stat -c %Y "$desktop_file" + done + done | sort -r | head -n1)" + + if [ -r "$DESKTOPS_CACHED_MIMEAPPS" ]; then + desktop_cache_modif_date="$(stat -c %Y "$DESKTOPS_CACHED_MIMEAPPS")" + if [ "$last_desktop_modif_date" -gt "$desktop_cache_modif_date" ]; then + rm "$DESKTOPS_CACHED_MIMEAPPS" + fi + fi + + if [ ! -r "$DESKTOPS_CACHED_MIMEAPPS" ]; then + IFS='|' + echo "[Added Associations]" > "$DESKTOPS_CACHED_MIMEAPPS" + for desktop_dir in $DESKTOP_DIRS; do + get_mimeapps_entries_from_desktop_dir "$desktop_dir" + done >> "$DESKTOPS_CACHED_MIMEAPPS" + fi +} + +find_desktop_path() { + desktop_name="$1" + + IFS='|' + for desktop_dir in $DESKTOP_DIRS; do + if [ -r "$desktop_dir/$desktop_name" ]; then + realpath "$desktop_dir/$desktop_name" + return + fi + done +} + +# This will take the full maping and simplify it which means +# * strip added entries with removed ones +# * put default entries first +# * remove duplicates lines +# following this format +# image/jpeg;foo.desktop +# image/jpeg;bar.desktop +# image/jpeg;baz.desktop +simplify_mapping() { + awk ' + function contains(array, value) { + for (key in array) { + if (array[key] == value) + return 1 + } + return 0 + } + + /^$/ {next} + + /^default;/ { kind = "default" } + /^added;/ { kind = "added" } + /^removed;/ { kind = "removed" } + + { gsub(kind ";", "", $0) } + + kind == "default" { default_entries[NR]=$0 } + kind == "added" { added_entries[NR]=$0 } + kind == "removed" { removed_entries[NR]=$0 } + + END { + for (key in default_entries) { + print default_entries[key] + } + for (key in added_entries) { + entry = added_entries[key] + if (contains(removed_entries, entry)) { continue } + if (contains(default_entries, entry)) { continue } + print entry + } + } + ' /dev/stdin +} + +get_mimeapps_mapping() { + prepare_desktop_mimeapps_mapping_cache + + printf '%s\n%s' \ + "$(mimeapps_to_desktop_mapping "$MIMEAPPS")" \ + "$(mimeapps_to_desktop_mapping "$DESKTOPS_CACHED_MIMEAPPS")" \ + | simplify_mapping +} + +filter_matching_desktops() { + mime_type="$1" + + grep "^$mime_type;" /dev/stdin \ + | cut -d";" -f2 +} + +curl_mime_type() { + curl --head -fsw '%{content_type}' "$1" | tail -n1 | sed 's|;.*||' +} + +extension_mime_type() { + [ ! -r "/etc/mime.types" ] && return # require mailcap + + ext="${1##*.}" + grep "\t.*$ext" /etc/mime.types | awk '{print $1}' +} + +get_mime_type() { + # Seems like an x-scheme + if echo "$1" | grep -q '^.\+:'; then + if echo "$1" | grep -q '^https\?:'; then + mime_type="$(curl_mime_type "$1")" + fi + + [ -z "$mime_type" ] && mime_type="x-scheme-handler/$(echo "$1" | grep -o '^\w\+')" + else + # is readable + if [ -r "$1" ]; then + mime_type="$(file -b --mime-type "$1")" + else + echo "This file does not exists?" > /dev/stderr + exit 1 + fi + fi + + # we try to be more precise then + if [ "application/octet-stream" = "$mime_type" ]; then + new_mime_type="$(extension_mime_type "$1")" + [ -n "$new_mime_type" ] && mime_type="$new_mime_type" + elif [ "inode/symlink" = "$mime_type" ]; then + real_path="$(realpath "$1")" + mime_type="$(get_mime_type "$real_path")" + fi + + echo "$mime_type" +} + +execute() { + if [ -n "$debug" ]; then + echo "The final command to execute is:" > /dev/stderr + echo "$@" > /dev/stderr + exit 0 + fi + + eval "$@" +} + +fetch_file() { + if [ -n "$debug" ]; then + echo "The file would be downloaded with '$*'" > /dev/stderr + return + fi + + eval "$@" +} + +build_command() { + exec="$1" + shift + + IFS=" +" + file_paths="$*" + unset IFS + + if echo "$exec" | grep -q '%f\|%F'; then # We must retrieve distant files + file_paths="$( + echo "$file_paths" | while read -r file_path; do + local_file_path="$(mktemp --suffix=".$(basename "$file_path")")" + if echo "$file_path" | grep -q '^https\?://'; then + fetch_file curl -so "$local_file_path" "$file_path" + else + echo "$file_path" + continue + fi + echo "$local_file_path" + done + )" + exec="$(echo "$exec" | sed -e 's|%f|%u|' -e 's|%F|%U|')" + fi + + file_paths="$(echo "$file_paths" | awk '{print "\"" $0 "\""}')" + + command= + if echo "$exec" | grep -q '%u'; then # handle url file one by one + exec="$(echo "$exec" | sed 's|%u|"%s"|')" + if [ "true" = "$terminal" ]; then + command="$(echo "$file_paths" | xargs printf "$TERMCMD $exec & " | sed 's| $||')" + else + command="$(echo "$file_paths" | xargs printf "$exec & " | sed -e 's|^ ||' -e 's| $||')" + fi + elif echo "$exec" | grep -q '%U'; then # handle url file grouped + exec_before="$(echo "$exec" | awk -F' %U' '{print $1}')" + exec_after="$(echo "$exec" | awk -F'%U ' '{$1=""; print $0}')" + command="$exec_before $(echo "$file_paths" | xargs printf '"%s" ')${exec_after:+ $exec_after }&" + if [ "true" = "$terminal" ]; then + command="$TERMCMD $command" + fi + else + command="$exec $(echo "$file_paths" | xargs printf '"%s" ')&" + if [ "true" = "$terminal" ]; then + command="$TERMCMD $command" + fi + fi + + echo "$command" +} + +run_desktop() { + desktop="$1" + shift + + desktop_path="$(find_desktop_path "$desktop")" + if [ -z "$desktop_path" ]; then + echo "We can't find the desktop file \"$desktop\"" > /dev/stderr + return + fi + + desktop_content="$(cat "$desktop_path")" + exec="$(echo "$desktop_content" | grep '^Exec=' | sed 's|^Exec=||' | head -n1)" + terminal="$(echo "$desktop_content" | grep '^Terminal=' | cut -d= -f2)" + [ -z "$terminal" ] && terminal="false" + + command="$(build_command "$exec" "$@")" + + if [ -n "$attached" ]; then + command="$(echo "$command" | sed 's| &|;|g')" + fi + + if [ -n "$debug" ]; then + echo "Exec is \"$exec\"" > /dev/stderr + echo "Terminal is \"$terminal\"" > /dev/stderr + echo "Command is \"$command\"" > /dev/stderr + exit 0 + fi + + eval "$command" + + exit 0 +} + +run() { + mime_type="$(get_mime_type "$1")" + + if [ -z "$mime_type" ]; then + echo "We failed to find the mime_type for \"$1\"" > /dev/stderr + exit 1 + fi + + desktops="$(get_mimeapps_mapping | filter_matching_desktops "$mime_type")" + + if [ -n "$debug" ]; then + echo "The mime type is \"$mime_type\"" > /dev/stderr + echo "The matching desktops are:" > /dev/stderr + echo "$desktops" > /dev/stderr + fi + + if [ -z "$desktops" ]; then + echo "There is no matching desktop to open \"$mime_type\"" > /dev/stderr + exit 1 + fi + + echo "$desktops" | while read -r desktop; do + run_desktop "$desktop" "$@" + done +} + +if [ "-d" = "$1" ]; then + debug="1" + shift +fi + +if [ "-a" = "$1" ]; then + attached="1" + shift +fi + +if [ $# -gt 0 ]; then + run "$@" +fi