From c587817907f3f6788c51e9582bef6a75625145fa Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sat, 26 Mar 2022 18:08:25 +0000 Subject: [PATCH] rework updates to allow file input readiness as well as timers --- blinkenlicht/bl.fnl | 65 ++++++++++++++++++++-------- blinkenlicht/blinkenlicht.fnl | 80 +++++++++++++++++++++-------------- 2 files changed, 96 insertions(+), 49 deletions(-) diff --git a/blinkenlicht/bl.fnl b/blinkenlicht/bl.fnl index 103ef4e..cd3652e 100644 --- a/blinkenlicht/bl.fnl +++ b/blinkenlicht/bl.fnl @@ -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")} }) ]}) diff --git a/blinkenlicht/blinkenlicht.fnl b/blinkenlicht/blinkenlicht.fnl index a7b004f..627702d 100644 --- a/blinkenlicht/blinkenlicht.fnl +++ b/blinkenlicht/blinkenlicht.fnl @@ -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)]