rework updates to allow file input readiness as well as timers

phoen
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 {: view} (require :fennel))
(local posix (require :posix))
(local fcntl (require :posix.fcntl))
(local metric (require :metric))
(stylesheet "licht.css")
@ -26,28 +29,56 @@
:classes ["hey"]
: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 {
:interval 200
:icon #(if (> (metric.loadavg) 2) "face-sad" "face-smile")
:wait-for { :interval 2000 }
: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 {
:interval (* 10 1000)
:classes ["yellow"]
:text #(let [{:power-supply-energy-full full
:power-supply-energy-now now
:power-supply-status status} (metric.battery)
percent (math.floor (* 100 (/ (tonumber now) (tonumber full))))
icon-code (battery-icon-codepoint status percent)]
(string.format "%s %d%%" (utf8.char icon-code) percent))
:wait-for { :interval (* 1000 10) }
:refresh
#(let [{:power-supply-energy-full full
:power-supply-energy-now now
:power-supply-status status} (metric.battery)
percent (math.floor
(* 100
(/ (tonumber now) (tonumber full))))
icon-code (battery-icon-codepoint status percent)]
{:text
(string.format "%s %d%%" (utf8.char icon-code) percent)
:classes ["yellow"]
})
})
(indicator {
:interval 1000
:text #(os.date "%H:%M")
:wait-for { :interval 1000 }
: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
: cairo } (require :lgi))
(local posix (require :posix))
(local {: view} (require :fennel))
(local icon-theme (Gtk.IconTheme.get_default))
@ -58,40 +60,39 @@
(tset found-icons name 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]
(let [context (widget:get_style_context)]
(each [_ c (ipairs classes)]
(context:add_class c))))
(fn indicator [{: interval
: icon
: poll
: text
: classes
(fn clear-css-classes [widget]
(let [context (widget:get_style_context)]
(each [_ c (ipairs (context:list_classes))]
(context:remove_class c))))
(fn indicator [{: wait-for
: refresh
: on-click}]
(var last-update -1)
(let [button (doto (Gtk.Button { :relief Gtk.ReliefStyle.NONE})
(add-css-classes ["indicator"])
(add-css-classes (or classes [])))
update (fn [now]
(when (and interval (> now (+ last-update interval)))
(update-button button icon text)
(set last-update now)))]
(update 0)
(let [button (Gtk.Button { :relief Gtk.ReliefStyle.NONE})]
(fn update-indicator []
(let [content (resolve refresh)]
(when content
(match (button:get_child) it (button:remove it))
(match content
{:icon icon} (button:add (find-icon icon))
{:text text} (button:add (Gtk.Label {:label text})))
(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
:update #(update $2)
:update update-indicator
:inputs (or wait-for.input [])
:interval wait-for.interval
}))
(fn make-layer-shell [window layer exclusive? anchors]
@ -129,20 +130,35 @@
(box:pack_start i.button false false 0))
(window:add box)))
;; we want to run each indicator's update function only when
;; more than `interval` ms has elapsed since it last ran
(fn gsource-for-file-input [file cb]
(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 []
(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
1000
100
(fn []
(let [now (/ (GLib.get_monotonic_time) 1000)]
(each [_ bar (ipairs bars)]
(each [_ indicator (ipairs bar.indicators)]
(indicator:update now))))
true))
(when (ready-to-update? indicator now update-times)
(indicator:update)
(tset update-times indicator (+ now indicator.interval))))))
true)))
(each [_ b (ipairs bars)]
(make-layer-shell b.window :top true
(collect [_ edge (ipairs b.anchor)]