local GLib = lgi.GLib
local Gio = lgi.Gio
+local trace_mpc = false
+
local mpc = {}
local function parse_password(host)
_error_handler = error_handler or function() end,
_connected = false,
_try_reconnect = false,
- _idle_commands = { ... }
+ _idle_subsystems = { ... }
}, { __index = mpc })
- self:_connect()
+ self:connect()
return self
end
+function mpc:_write(command)
+ if trace_mpc then
+ print("write: " .. command)
+ end
+ self._output:write(command .. "\n")
+end
+
function mpc:_error(err)
self._connected = false
self._error_handler(err)
- self._try_reconnect = not self._try_reconnect
- if self._try_reconnect then
- self:_connect()
- end
end
-function mpc:_connect()
+function mpc:connect()
if self._connected then return end
-- Reset all of our state
self._reply_handlers = {}
if type(err) ~= "number" then
self._output, self._input = nil, nil
self:_error(err)
- else
- line = tostring(line)
- if line == "OK" or line:match("^ACK ") then
- local success = line == "OK"
- local arg
- if success then
- arg = self._pending_reply
- else
- arg = { line }
- end
- local handler = self._reply_handlers[1]
- table.remove(self._reply_handlers, 1)
- self._pending_reply = {}
- handler(success, arg)
+ return
+ end
+
+ line = tostring(line)
+ if line == "OK" or line:match("^ACK ") then
+ local success = line == "OK"
+ local arg
+ if success then
+ arg = self._pending_reply
else
- local _, _, key, value = string.find(line, "([^:]+):%s(.+)")
- if key then
- if key == "binary" then
- value = tonumber(value)
- local data = {}
- while value > 0 do
- local b = assert(obj:read_bytes(value))
- table.insert(data, b.data)
- value = value - #b
- end
- local w = obj:read_bytes(1) -- read newline at end of binary
- self._pending_reply[string.lower(key)] = table.concat(data)
- else
- self._pending_reply[string.lower(key)] = value
+ arg = { line }
+ end
+ local handler = self._reply_handlers[1]
+ table.remove(self._reply_handlers, 1)
+ self._pending_reply = {}
+ handler(success, arg)
+
+ if next(self._reply_handlers) == nil then
+ self:_start_idle()
+ end
+ else
+ local _, _, key, value = string.find(line, "([^:]+):%s(.+)")
+ if key then
+ local k = string.lower(key)
+ if k == "binary" then
+ value = tonumber(value)
+ local data = {}
+ while value > 0 do
+ local b = assert(obj:read_bytes(value))
+ table.insert(data, b.data)
+ value = value - #b
end
+ local w = obj:read_bytes(1) -- read newline at end of binary
+ self._pending_reply[k] = table.concat(data)
+ elseif k == "changed" then
+ if not self._pending_reply[k] then
+ self._pending_reply[k] = {}
+ end
+
+ self._pending_reply[k][value] = true
+ else
+ self._pending_reply[k] = value
end
end
- do_read()
end
+ do_read()
end)
end
do_read()
- -- To synchronize the state on startup, send the idle commands now. As a
- -- side effect, this will enable idle state.
- self:_send_idle_commands(true)
-
- return self
-end
-
-function mpc:_send_idle_commands(skip_stop_idle)
- -- We use a ping to unset this to make sure we never get into a busy
- -- loop sending idle / unidle commands. Next call to
- -- _send_idle_commands() might be ignored!
- if self._idle_commands_pending then
- return
- end
- if not skip_stop_idle then
- self:_stop_idle()
+ -- To synchronize the state on startup, send the idle commands now.
+ for i = 1, #self._idle_subsystems, 2 do
+ self._idle_subsystems[i+1](self)
end
- self._idle_commands_pending = true
- for i = 1, #self._idle_commands, 2 do
- self:_send(self._idle_commands[i], self._idle_commands[i+1])
- end
- self:_send("ping", function()
- self._idle_commands_pending = false
- end)
- self:_start_idle()
+ return self
end
function mpc:_start_idle()
if self._idle then
- error("Still idle?!")
+ error("start_idle but already idle")
end
self:_send("idle", function(success, reply)
+ self._idle = false
if reply.changed then
-- idle mode was disabled by mpd
- self:_send_idle_commands()
+ for i = 1, #self._idle_subsystems, 2 do
+ local subsys = self._idle_subsystems[i]
+ if reply.changed[subsys] then
+ self._idle_subsystems[i+1](self)
+ end
+ end
end
end)
self._idle = true
function mpc:_stop_idle()
if not self._idle then
- error("Not idle?!")
+ error("stop_idle but not idle")
end
- self._output:write("noidle\n")
+ self:_write("noidle")
self._idle = false
end
if self._idle then
error("Still idle in send()?!")
end
- self._output:write(command .. "\n")
+ self:_write(command)
table.insert(self._reply_handlers, callback or function() end)
end
function mpc:send(...)
- self:_connect()
+ self:connect()
if not self._connected then
return
end
- local args = { ... }
- if not self._idle then
- error("Something is messed up, we should be idle here...")
+ if self._idle then
+ self:_stop_idle()
end
- self:_stop_idle()
+ local args = { ... }
for i = 1, #args, 2 do
self:_send(args[i], args[i+1])
end
- self:_start_idle()
end
function mpc:toggle_play()
end)
end
+function mpc:play()
+ self:send("play")
+end
+
+function mpc:pause()
+ self:send("pause")
+end
+
function clamp(x, min, max)
return math.min(math.max(x, min), max)
end