This repository has been archived by the owner on Mar 15, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
coffeestatus.lua
202 lines (179 loc) · 5.35 KB
/
coffeestatus.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/bin/lua5.3
--[[
Remember to install the following luarocks rocks :
- luasocket : used for sleep/gettime here and used in the mpd module
[ archlinux package: community/lua-socket ]
- luaposix : used for "nonblocking" io to read clicks from i3bar
[ archlinux package: aur/lua-posix ]
- luajson : used to communicate back and forth with i3bar
[ archlinux package: extra/lua-luajson ]
]]
-- Detect if running dev or installed version based on the script's name
MODE = "installed"
if arg[0]:sub(-4) == ".lua" then
MODE = "dev"
end
-- Path of coffeestatus
BINARY_PATH = arg[0]:match(".*/") or "./"
-- Path for requirements
if MODE == "installed" then
package.path = BINARY_PATH.."../lib/coffeestatus/?/init.lua;"..
BINARY_PATH.."../lib/coffeestatus/?.lua;"..
package.path
end
local utils = require("utils")
local posix = require("posix")
local rpoll = require("posix.poll").rpoll
local stdin = require("posix.unistd").STDIN_FILENO
local socket = require("socket")
local json = require("json")
-- if you don't want to use luasocket at all, replace the following functions
-- to another module that you want to use
local sleep = socket.sleep
local gettime = socket.gettime
-- Home
HOME = os.getenv("HOME").."/"
-- Paths for modules
if MODE == "installed" then
local xdg_data_home = utils.getenv("XDG_DATA_HOME", HOME..".local/share").."/"
MODULE_PATHS = {
BINARY_PATH.."../lib/coffeestatus/modules/",
HOME..".coffeestatus/",
xdg_data_home.."coffeestatus/modules/",
}
else
MODULE_PATHS = {BINARY_PATH.."modules/"}
end
-- Paths for config
CONFIG_PATHS = {
"/etc/coffeestatus_conf.json",
HOME..".coffeestatus/conf.json",
utils.getenv("XDG_CONFIG_HOME", HOME..".config").."/coffeestatus/conf.json",
}
if MODE == "dev" then
table.insert(CONFIG_PATHS, 1, BINARY_PATH.."default_conf.json")
end
-- remove access to print in order to prevent devs from "crashing" i3bar with
-- random garbage in stdout
local logfile = io.open("/tmp/coffeestatus_log","w")
-- act as a replacement for print, outputing everything in /tmp/coffeestatus_log
local function log(...)
local output = {}
for i = 1, select("#", ...) do
output[i] = tostring(select(i,...))
end
logfile:write(table.concat(output, "\t").."\n")
logfile:flush()
end
local p = print
_G.print = log
_G._print = p
local output = require("output_handlers")
print("Log started on " .. os.date())
-- Log an error
local function handleError(message)
log("Error happened")
log("--------------")
log(message)
end
---------------------
-- Program startup --
---------------------
local modules = {}
local timers = {}
local conf = io.open(utils.filepath("", CONFIG_PATHS))
local status, value = pcall(json.decode,conf:read("*a"))
if not status then
handleError("Failed to read configuration file:\n"..value)
os.exit(1)
end
to_load = value
conf:close()
local global = {}
for i=1,#to_load do
if to_load[i].name == "_GLOBAL" then
global = table.remove(to_load, i)
break
end
end
for i=1, #to_load do
_G.ARGS = utils.overlaytables(global, to_load[i])
local path = utils.filepath(to_load[i].name..".lua", MODULE_PATHS)
local failed = false
if path == nil then
handleError("Could not find module '" .. to_load[i].name .. "'")
failed = true
else
local status, value = pcall(dofile,path)
if not status then
handleError("Failed to load module "..to_load[i].name..":\n"..value)
failed = true
end
modules[i] = value
end
if failed then
modules[i] = {name=to_load[i].name,status="ERROR ["..to_load[i].name.."]",update=utils.noop, click=utils.noop, interval=100000}
end
timers[i] = modules[i].interval
end
-- Treat str and call target module's click function
local function handleInput(str)
-- immediatly read next line in case of opening bracket
if str == "[" then
str = io.read()
--ignore coma (block following opening bracket does not have a coma)
elseif string.sub(str,1,1) == "," then
str = string.sub(str,2)
end
local table = json.decode(str)
local inst = tonumber(table.instance)
local oldstatus = modules[inst].status
local status, value = pcall(modules[inst].click, table)
if not status then
handleError("Failed to run click function in module "..modules[inst].name..":\n"..value)
return
end
-- reset timer since modules can change status during click events
timers[inst] = 0
return oldstatus ~= modules[inst].status
end
-- Main loop
local oldtime = gettime()
local changed = false
while 1 do
local line = {}
-- compute delta time to be as accurate as possible for timers
local newtime = gettime()
local dt = newtime - oldtime
oldtime = newtime
-- computer probably went to sleep, skip this dt
if dt > 2 then
dt = 0
end
for i=1,#modules do
timers[i] = timers[i] + dt
if timers[i] >= modules[i].interval then
local oldStatus = modules[i].status
local status, value = pcall(modules[i].update)
if not status then
handleError("Failed to update module "..modules[i].name..":\n"..value)
end
if oldStatus ~= modules[i].status then
changed = true
end
timers[i] = timers[i] - modules[i].interval
end
end
-- update only if at least one of the statuses changed
if changed then
output.writestatus(modules)
changed = false
end
-- read input only if an event happened on the fd for stdin
-- (since we fully empty stdin after each read, events happen whenever
-- there is new inputs)
if rpoll(stdin,0) == 1 then
changed = handleInput(io.read()) or changed
end
sleep(0.05)
end