rework updates to allow file input readiness as well as timers

This commit is contained in:
Daniel Barlow 2022-03-26 18:08:25 +00:00
parent 26f0f46c71
commit c587817907
2 changed files with 96 additions and 49 deletions

View File

@ -1,6 +1,9 @@
(local {: bar : indicator : stylesheet : run} (require :blinkenlicht)) (local {: bar : indicator : stylesheet : run} (require :blinkenlicht))
(local {: view} (require :fennel)) (local {: view} (require :fennel))
(local posix (require :posix))
(local fcntl (require :posix.fcntl))
(local metric (require :metric)) (local metric (require :metric))
(stylesheet "licht.css") (stylesheet "licht.css")
@ -26,28 +29,56 @@
:classes ["hey"] :classes ["hey"]
:indicators :indicators
[ [
(let [f (io.open "/tmp/statuspipe" :r)]
(fcntl.fcntl (posix.stdio.fileno f)
fcntl.F_SETFL fcntl.O_NONBLOCK)
(indicator {
;; this is a guide to tell blinkenlicht when it might
;; be worth calling your `content` function. Your
;; function may be called at other times too
:wait-for { :input [f] }
;; the `content` function should not block, so e.g
;; don't read from files unless you know there's data
;; available. it returns a hash
;; { :text "foo" } - render "foo" as a label
;; { :icon "face-sad" } - render icon from theme or pathname
;; { :classes ["foo" "bar"] - add CSS classes to widget
:refresh
#(let [l (posix.unistd.read (posix.stdio.fileno f) 1024)]
(if l
{:text l}))
}))
(indicator { (indicator {
:interval 200 :wait-for { :interval 2000 }
:icon #(if (> (metric.loadavg) 2) "face-sad" "face-smile") :refresh
#{:icon (if (> (metric.loadavg) 2) "face-sad" "face-smile")}
}) })
;; (let [f (io.open "/tmp/statuspipe" "r")]
;; (indicator {
;; :poll [f]
;; :text #((f:read):sub 1 10)
;; }))
(indicator { (indicator {
:interval (* 10 1000) :wait-for { :interval (* 1000 10) }
:classes ["yellow"] :refresh
:text #(let [{:power-supply-energy-full full #(let [{:power-supply-energy-full full
:power-supply-energy-now now :power-supply-energy-now now
:power-supply-status status} (metric.battery) :power-supply-status status} (metric.battery)
percent (math.floor (* 100 (/ (tonumber now) (tonumber full)))) percent (math.floor
icon-code (battery-icon-codepoint status percent)] (* 100
(string.format "%s %d%%" (utf8.char icon-code) percent)) (/ (tonumber now) (tonumber full))))
icon-code (battery-icon-codepoint status percent)]
{:text
(string.format "%s %d%%" (utf8.char icon-code) percent)
:classes ["yellow"]
})
}) })
(indicator { (indicator {
:interval 1000 :wait-for { :interval 1000 }
:text #(os.date "%H:%M") :refresh #{:text (os.date "%H:%M:%S")}
})
(indicator {
:wait-for { :interval 4000 }
:refresh #{:text (os.date "%H:%M:%S")}
}) })
]}) ]})

View File

@ -5,6 +5,8 @@
: GLib : GLib
: cairo } (require :lgi)) : cairo } (require :lgi))
(local posix (require :posix))
(local {: view} (require :fennel)) (local {: view} (require :fennel))
(local icon-theme (Gtk.IconTheme.get_default)) (local icon-theme (Gtk.IconTheme.get_default))
@ -58,40 +60,39 @@
(tset found-icons name icon) (tset found-icons name icon)
icon)))) icon))))
(fn update-button [button icon text]
(match (button:get_child) it (button:remove it))
(let [i (resolve icon)]
(if i
(button:add (find-icon i))
(button:add (Gtk.Label {:label (resolve text)})))
(button:show_all)
))
(fn add-css-classes [widget classes] (fn add-css-classes [widget classes]
(let [context (widget:get_style_context)] (let [context (widget:get_style_context)]
(each [_ c (ipairs classes)] (each [_ c (ipairs classes)]
(context:add_class c)))) (context:add_class c))))
(fn indicator [{: interval (fn clear-css-classes [widget]
: icon (let [context (widget:get_style_context)]
: poll (each [_ c (ipairs (context:list_classes))]
: text (context:remove_class c))))
: classes
(fn indicator [{: wait-for
: refresh
: on-click}] : on-click}]
(var last-update -1) (let [button (Gtk.Button { :relief Gtk.ReliefStyle.NONE})]
(let [button (doto (Gtk.Button { :relief Gtk.ReliefStyle.NONE}) (fn update-indicator []
(add-css-classes ["indicator"]) (let [content (resolve refresh)]
(add-css-classes (or classes []))) (when content
update (fn [now] (match (button:get_child) it (button:remove it))
(when (and interval (> now (+ last-update interval))) (match content
(update-button button icon text) {:icon icon} (button:add (find-icon icon))
(set last-update now)))] {:text text} (button:add (Gtk.Label {:label text})))
(update 0) (clear-css-classes button)
(add-css-classes button ["indicator"])
(match content
{:classes classes} (add-css-classes button classes))
(button:show_all))))
(update-indicator)
{ {
: interval
: poll
: button : button
:update #(update $2) :update update-indicator
:inputs (or wait-for.input [])
:interval wait-for.interval
})) }))
(fn make-layer-shell [window layer exclusive? anchors] (fn make-layer-shell [window layer exclusive? anchors]
@ -129,20 +130,35 @@
(box:pack_start i.button false false 0)) (box:pack_start i.button false false 0))
(window:add box))) (window:add box)))
;; we want to run each indicator's update function only when (fn gsource-for-file-input [file cb]
;; more than `interval` ms has elapsed since it last ran (let [fd (posix.stdio.fileno file)]
(doto (GLib.unix_fd_source_new fd GLib.IOCondition.IN)
(: :set_callback cb))))
(fn ready-to-update? [indicator now update-times]
(if indicator.interval
(> now (or (. update-times indicator) 0))))
(fn run [] (fn run []
(GLib.timeout_add (each [_ bar (ipairs bars)]
(each [_ indicator (ipairs bar.indicators)]
(each [_ file (ipairs indicator.inputs)]
(GLib.Source.attach
(gsource-for-file-input
file
#(or (indicator:update) true))))))
(let [update-times {}]
(GLib.timeout_add
0 0
1000 100
(fn [] (fn []
(let [now (/ (GLib.get_monotonic_time) 1000)] (let [now (/ (GLib.get_monotonic_time) 1000)]
(each [_ bar (ipairs bars)] (each [_ bar (ipairs bars)]
(each [_ indicator (ipairs bar.indicators)] (each [_ indicator (ipairs bar.indicators)]
(indicator:update now)))) (when (ready-to-update? indicator now update-times)
true)) (indicator:update)
(tset update-times indicator (+ now indicator.interval))))))
true)))
(each [_ b (ipairs bars)] (each [_ b (ipairs bars)]
(make-layer-shell b.window :top true (make-layer-shell b.window :top true
(collect [_ edge (ipairs b.anchor)] (collect [_ edge (ipairs b.anchor)]