Bioe007 moc widget (scrolling text)

From awesome
Jump to: navigation, search

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)

Personal tools