X-Git-Url: https://git.madduck.net/etc/awesome.git/blobdiff_plain/e00ee3436e0a01827eeda3b5360304eea2a6fb92..98bb2dc5ef25ce2b2ad330bca7e78d77125e7c72:/asyncshell.lua diff --git a/asyncshell.lua b/asyncshell.lua index 51885e8..61336db 100644 --- a/asyncshell.lua +++ b/asyncshell.lua @@ -2,81 +2,78 @@ --[[ Licensed under GNU General Public License v2 + * (c) 2015, worron * (c) 2013, Alexander Yakushev --]] -- Asynchronous io.popen for Awesome WM. --- How to use... --- ...asynchronously: --- asyncshell.request('wscript -Kiev', function(f) wwidget.text = f:read("*l") end) --- ...synchronously --- wwidget.text = asyncshell.demand('wscript -Kiev', 5):read("*l") or "Error" +-- How to use: +-- asyncshell.request('wscript -Kiev', function(output) wwidget.text = output end) --- This makes things faster, but puts weight on sysload and is more cpu demanding. +-- Grab environment +local awful = require('awful') -local spawn = require('awful.util').spawn +-- Avoid discrepancies across multiple shells +awful.util.shell = '/bin/sh' -asyncshell = {} -asyncshell.request_table = {} -asyncshell.id_counter = 0 -asyncshell.folder = "/tmp/asyncshell" -asyncshell.file_template = asyncshell.folder .. '/req' +-- Initialize tables for module +asyncshell = { request_table = {}, id_counter = 0 } --- Create a directory for asynchell response files -os.execute("mkdir -p " .. asyncshell.folder) - --- Returns next tag - unique identifier of the request +-- Request counter local function next_id() - asyncshell.id_counter = (asyncshell.id_counter + 1) % 100000 - return asyncshell.id_counter + asyncshell.id_counter = (asyncshell.id_counter + 1) % 10000 + return asyncshell.id_counter +end + +-- Remove given request +function asyncshell.clear(id) + if asyncshell.request_table[id] then + if asyncshell.request_table[id].timer then + asyncshell.request_table[id].timer:stop() + asyncshell.request_table[id].timer = nil + end + asyncshell.request_table[id] = nil + end end --- Sends an asynchronous request for an output of the shell command. +-- Sends an asynchronous request for an output of the shell command -- @param command Command to be executed and taken output from -- @param callback Function to be called when the command finishes --- @return Request ID -function asyncshell.request(command, callback) - local id = next_id() - local tmpfname = asyncshell.file_template .. id - asyncshell.request_table[id] = {callback = callback} - local req = - string.format("sh -c '%s > %s; " .. - 'echo "asyncshell.deliver(%s)" | ' .. - "awesome-client' 2> /dev/null", - string.gsub(command, "'", "'\\''"), tmpfname, - id, tmpfname) - spawn(req, false) - return id -end +-- @param timeout Maximum amount of time to wait for the result (optional) +function asyncshell.request(command, callback, timeout) + local id = next_id() + asyncshell.request_table[id] = { callback = callback } --- Calls the remembered callback function on the output of the shell --- command. --- @param id Request ID --- @param output The output file of the shell command to be delievered -function asyncshell.deliver(id) - if asyncshell.request_table[id] and - asyncshell.request_table[id].callback then - local output = io.open(asyncshell.file_template .. id, 'r') - asyncshell.request_table[id].callback(output) - end + local formatted_command = string.gsub(command, '"','\"') + + local req = string.format( + "echo \"asyncshell.deliver(%s, [[\\\"$(%s)\\\"]])\" | awesome-client &", + id, formatted_command + ) + + if type(awful.spawn) == 'table' and awful.spawn.with_shell then + awful.spawn.with_shell(req) + else + awful.util.spawn_with_shell(req) + end + + if timeout then + asyncshell.request_table[id].timer = timer({ timeout = timeout }) + asyncshell.request_table[id].timer:connect_signal("timeout", function() asyncshell.clear(id) end) + asyncshell.request_table[id].timer:start() + end end --- Sends a synchronous request for an output of the command. Waits for --- the output, but if the given timeout expires returns nil. --- @param command Command to be executed and taken output from --- @param timeout Maximum amount of time to wait for the result --- @return File handler on success, nil otherwise -function asyncshell.demand(command, timeout) - local id = next_id() - local tmpfname = asyncshell.file_template .. id - local f = io.popen(string.format("(%s > %s; echo asyncshell_done) & " .. - "(sleep %s; echo asynchell_timeout)", - command, tmpfname, timeout)) - local result = f:read("*line") - if result == "asyncshell_done" then - return io.open(tmpfname) - end +-- Calls the remembered callback function on the output of the shell command +-- @param id Request ID +-- @param output Shell command output to be delievered +function asyncshell.deliver(id, output) + local output = string.sub(output, 2, -2) + if asyncshell.request_table[id] then + asyncshell.request_table[id].callback(output) + asyncshell.clear(id) + end end return asyncshell