Bioe007 moc widget (scrolling text)
With all the great widgets on my statusbar, I'm running out of room. And its aggravating that I can not see all of the artist plus track title for whats currently playing in moc.
So I decided to write a widget that would 'scroll' text to show the entire thing.
Note throughout this page files are linked to my github account, where you can always find the latest version.
This new (2/17/09) version makes use of mocp's ability to execute a script when the song changes, here is the script i use in ~/.bin/mochg:
#!/bin/bash
# a helper script to update my awesome mocp widget
#
# this script accepts key value pairs and passes them to the mocp module via awesome-client
echo time >> ~/mocdbg
if [ "$#" -lt "2" ] ; then
echo 'mocp.update("state","STOP")' | awesome-client
else
while [ "$#" -ge "2" ] ; do
echo 'mocp.update("'$1'","'$2'")' | awesome-client
shift; shift
done
fi
This is the actual mocp module code, I put it in ~/.config/awesome/mocp.lua:
local io = io
local string = string
local awful = require("awful")
local beautiful = require("beautiful")
local naughty = require("naughty")
local markup = require("markup")
module("mocp")
-- public settings
settings = {}
settings.iScroller = 1
settings.MAXCH = 15
settings.interval = 0.75
local mocbox = nil
local trackinfo = {}
trackinfo.artist = ""
trackinfo.songtitle = ""
trackinfo.album = ""
trackinfo.state = ""
---PAUSE
---{{{ local setTitle
-- call to force update of trackinfo variables
local function setTitle()
local fd = {}
if state() then
fd = io.popen('mocp -i')
-- read to end of mocp -i
tmp = fd:read()
while tmp ~= nil do
key = string.match(tmp,"^%w+")
if trackinfo[key:lower()] ~= nil then
trackinfo[key:lower()]=awful.util.escape(string.gsub(string.gsub(tmp,key..":%s*",""),"%b()",""))
end
tmp = fd:read()
end
end
end
---}}}
---{{{ local title(delim)
local function title(delim)
local eol = delim or " "
local np = {}
if trackinfo.artist == "" and state() then setTitle() end
np.song =string.gsub( string.gsub(trackinfo.songtitle,"^%d*",""),"%(.*","") .. eol
-- return for widget text
return trackinfo.artist.." : "..np.song
end
---}}}
---{{{ local function notdestroy()
local function notdestroy()
if mocbox ~= nil then
naughty.destroy(mocbox)
mocbox = nil
end
end
---}}}
---{{{ local getTime() gets ct and tt of track for popup
--@return string containig formatted times
local function getTime()
local fd = {}
local ttable = {}
fd = io.popen('mocp -i')
local tmp = fd:read()
while tmp ~= nil do
key = string.match(tmp,"^%w+")
if key == "TotalTime" then
tstring = " [ "..markup.fg(beautiful.fg_normal,awful.util.escape(string.gsub(string.gsub(tmp,key..":%s*",""),"%b()",""))).." ]"
elseif key == "CurrentTime" then
tstring = markup.fg(beautiful.fg_focus,"Time: ")..
markup.fg(beautiful.fg_normal,awful.util.escape(string.gsub(string.gsub(tmp,key..":%s*",""),"%b()","")))..tstring
end
tmp = fd:read()
end
fd:close()
return tstring
end
---}}}
---{{{ popup
-- displays a naughty notificaiton of the current track
function popup()
setTitle()
notdestroy()
local np = {}
np.state = nil
np.strng = ""
if not state() then
return
else
np.strng = "Artist: "..markup.fg(beautiful.fg_normal,trackinfo.artist).."\n"..
"Song: "..markup.fg(beautiful.fg_normal,trackinfo.songtitle).."\n"..
"Album: "..markup.fg(beautiful.fg_normal,trackinfo.album).."\n"
np.strng = np.strng..markup.fg(beautiful.fg_normal,getTime())
end
np.strng = markup.fg( beautiful.fg_focus, markup.font("monospace", np.strng.." "))
mocbox = naughty.notify({
title = markup.font("monospace","Now Playing:"),
text = np.strng, hover_timeout = ( settings.hovertime or 3 ), timeout = 0,
-- icon = "/usr/share/icons/gnome/24x24/actions/edia-playback-start.png", icon_size = 24,
run = function() play(); popup() end
})
end
---}}}
---run mocp
function play()
if trackinfo.state == "STOP" then
awful.util.spawn('mocp --play')
elseif trackinfo.state == "PLAY" then
awful.util.spawn('mocp --next')
else
awful.util.spawn('mocp --toggle-pause')
end
end
---
function setwidget(w)
settings.widget = w
awful.hooks.timer.register (settings.interval,scroller)
state()
end
---{{{ function update ( k, v)
-- called by any kind of external script to trigger widget text update
function update ( k, v )
if #k == 0 or #v == 0 then return end
if trackinfo[k] ~= nil then
trackinfo[k] = v
end
state()
end
---}}}
-- mocp widget, scrolls text
function scroller(tb)
local np = {}
-- if mocp is not running, then simply return here
if trackinfo.state == "OFF" then
settings.iScroller = 1
state()
return
else
-- this sets the symbolic prefix based on where moc is playing or stopped or paused
if trackinfo.state == "PAUSE" then
prefix = "|| "
settings.interval = 2
elseif trackinfo.state == "STOP" then
settings.iScroller = 1
settings.widget.width = 20
settings.widget.text = "[]"
return
else
prefix = ">> "
settings.interval = 0.75
end
-- extract a substring, putting it after the
np.strng = title()
np.rtn = string.sub(np.strng,settings.iScroller,settings.MAXCH+settings.iScroller-1)
-- if our index and settings.MAXCH count are bigger than the string, wrap around to the beginning and
-- add enough to make it look circular
if settings.MAXCH+settings.iScroller > (np.strng):len() then
np.rtn = np.rtn .. string.sub(np.strng,1,(settings.MAXCH+settings.iScroller-1)-np.strng:len())
end
np.rtn = awful.util.escape(np.rtn)
settings.widget.text = markup.fg(beautiful.fg_normal,prefix) .. markup.fg(beautiful.fg_sb_hi,np.rtn)
if settings.iScroller <= np.strng:len() then
settings.iScroller = settings.iScroller +1
else
settings.iScroller = 1
end
end
end
-- }}}
Now in ~/.config/awesome/rc.lua, require("mocp") near the top, like any other 'included' script. Then setup mocpwidget similar to this:
mocpwidget = widget({ type = 'textbox', name = 'mocpwidget', align = 'right'})
mocp.setwidget(mocpwidget)
mocpwidget:buttons({
button({ }, 1, function () mocp.play(); mocp.popup() end ),
button({ }, 2, function () awful.util.spawn('mocp --toggle-pause') end),
button({ }, 4, function () awful.util.spawn('mocp --toggle-pause') end),
button({ }, 3, function () awful.util.spawn('mocp --previous'); mocp.popup() end),
button({ }, 5, function () awful.util.spawn('mocp --previous'); mocp.popup() end)
})
mocpwidget.mouse_enter = function() mocp.popup() end
awful.hooks.timer.register (mocp.settings.interval,mocp.scroller)
Also make sure to update your ~/.moc/config OnSongChange and OnStop value to point to the script that will call mocp.lua.
OnSongChange = "/home/perry/.bin/mochg artist %a songtitle %t album %r" OnStop = "/home/perry/.bin/mochg"
Add to your statusbar wibox the mocpwidget and you should be good.
I tried to liberally comment the above but if its confused you might find me in #awesome (but for that matter there are many helpful people in #awesome anyway).
Thanks:
I'd like to thank farhaven for helping me debug this and get the width proper.
Notes: 2/17/08: Rewrote this widget to rely on OnSongChange of mocp's config, dramatically reduces resource use.
I use a markup module that can be found on my github so I don't need any ugly formatting inline with my code, you can either remove all the 'markup' references or grab my markup module from github. (also most current version of this widget is on github)