commit 498c7f8f611d93df83cfc84fea1c2a1e84b4cb5a
parent b7ab3d1f31d84c4a4c4ed83dcd71fa5edd58d60c
Author: Daniel Barlow <dan@telent.net>
Date: Wed, 23 Mar 2022 22:58:00 +0000
initial checkin
Diffstat:
5 files changed, 246 insertions(+), 0 deletions(-)
diff --git a/blinkenlicht/README.md b/blinkenlicht/README.md
@@ -0,0 +1,37 @@
+# Blinkenlicht
+
+Yet another "bar" program for wlroots-basd Wayland compositors, but
+this one is written in [Fennel](https://fennel-lang.org/) and
+therefore better than all the others because it is more niche.
+
+More seriously: you might prefer this over another status bar program
+if you want fine-grained control over what is shown in your bar and
+you are happy to exert that control in a Lua-based Lisp language.
+
+```fennel
+(bar
+ {
+ :anchor [:top :right]
+ :orientation :horizontal
+ :indicators
+ [
+ (indicator {
+ :interval 200
+ :icon #(if (> loadavg 2) "sad-face" "happy-face")
+ })
+ (let [f (io.open "/tmp/statuspipe" "r")]
+ (indicator {
+ :poll [f]
+ :text #(f:read:sub 1 10)
+ }))
+ (indicator {
+ :interval 5000
+ :text #(.. (disk-free-percent "/") "%")
+ :on-click #(spawn "baobab")
+ })
+ (indicator {
+ :interval 1000
+ :text #(os.date "%X")
+ })
+ ]})
+```
diff --git a/blinkenlicht/bl.fnl b/blinkenlicht/bl.fnl
@@ -0,0 +1,42 @@
+(local {: bar : indicator : run} (require :blinkenlicht))
+
+(fn loadavg []
+ (with-open [f (io.open "/proc/loadavg" :r)]
+ (tonumber (: (f:read "*a") :match "[0-9.]+" ))))
+
+(fn disk-free-percent []
+ 83)
+
+(fn spawn []
+ true)
+
+(bar
+ {
+ :anchor [:top :right]
+ :orientation :horizontal
+ :indicators
+ [
+ (indicator {
+ :interval 200
+ :icon #(if (> (loadavg) 2) "face-sad" "face-smile")
+ })
+ ;; (let [f (io.open "/tmp/statuspipe" "r")]
+ ;; (indicator {
+ ;; :poll [f]
+ ;; :text #((f:read):sub 1 10)
+ ;; }))
+ (indicator {
+ :text "HI!"
+ })
+ (indicator {
+ :interval 5000
+ :text #(.. (disk-free-percent "/") "%")
+ :on-click #(spawn "baobab")
+ })
+ (indicator {
+ :interval 1000
+ :text #(os.date "%X")
+ })
+ ]})
+
+(run)
diff --git a/blinkenlicht/blinkenlicht.fnl b/blinkenlicht/blinkenlicht.fnl
@@ -0,0 +1,95 @@
+(local {: Gtk
+ : Gdk
+ : GdkPixbuf
+ : GLib
+ : cairo } (require :lgi))
+
+(local {: view} (require :fennel))
+
+(local icon-theme (Gtk.IconTheme.get_default))
+
+(local HEIGHT 48)
+
+(fn resolve [f]
+ (match (type f)
+ "string" f
+ "function" (f)))
+
+(fn find-icon-pixbuf [name]
+ (var found nil)
+ (each [_ res (pairs [HEIGHT 128 64 48]) :until found]
+ (let [pixbuf (icon-theme:load_icon
+ name res
+ (+ Gtk.IconLookupFlags.FORCE_SVG
+ Gtk.IconLookupFlags.USE_BUILTIN))]
+ (when pixbuf
+ (set found (pixbuf:scale_simple
+ HEIGHT (* pixbuf.width (/ HEIGHT pixbuf.height))
+ GdkPixbuf.InterpType.BILINEAR)))))
+ found)
+
+(fn find-icon [name]
+ (if (= (name:sub 1 1) "/")
+ ;; From a direct path
+ (GdkPixbuf.Pixbuf.new_from_file_at_scale name HEIGHT -1 true)
+ ;; From icon theme
+ (Gtk.Image.new_from_pixbuf (find-icon-pixbuf name))))
+
+(fn update-button [button icon text]
+ (match (button:get_child) it (button:remove it))
+ (let [i (resolve icon)]
+ (print :update i (resolve text))
+ (if i
+ (button:add (find-icon i))
+ (button:add (Gtk.Label {:label (resolve text)})))
+ (button:show_all)
+ ))
+
+(fn indicator [{: interval
+ : icon
+ : poll
+ : text
+ : on-click}]
+ (let [button
+ (Gtk.Button { :relief Gtk.ReliefStyle.NONE})
+ update #(update-button button icon text)]
+ (update)
+ {
+ : interval
+ : poll
+ : button
+ : update
+ }))
+
+(local bars [])
+
+(fn bar [{ : anchor : orientation : indicators }]
+ (let [window (Gtk.Window {} )
+ orientation (match orientation
+ :vertical Gtk.Orientation.VERTICAL
+ :horizontal Gtk.Orientation.HORIZONTAL)
+ box (Gtk.Box { :orientation orientation})]
+ (table.insert bars { : window : anchor : indicators })
+ (each [_ i (ipairs indicators)]
+ (box:pack_start i.button false false 0))
+ (window:add box)))
+
+(fn run []
+ (GLib.timeout_add
+ 0
+ 1000
+ (fn []
+ (print :update)
+ (each [_ bar (ipairs bars)]
+ (each [_ indicator (ipairs bar.indicators)]
+ (indicator:update)))
+ true))
+ (each [_ b (ipairs bars)]
+ (b.window:show_all))
+ (Gtk.main))
+
+{
+ : bar
+ : indicator
+ : run
+ }
diff --git a/blinkenlicht/default.nix b/blinkenlicht/default.nix
@@ -0,0 +1,70 @@
+{ stdenv
+, callPackage
+, fetchFromGitHub
+, fetchurl
+, gobject-introspection
+, gtk3
+, lib
+, librsvg
+, lua53Packages
+#, luaDbusProxy
+, lua5_3
+, makeWrapper
+, writeText
+}:
+let
+ pname = "blinkenlicht";
+ fennel = fetchurl {
+ name = "fennel.lua";
+ url = "https://fennel-lang.org/downloads/fennel-1.0.0";
+ hash = "sha256:1nha32yilzagfwrs44hc763jgwxd700kaik1is7x7lsjjvkgapw7";
+ };
+ inifile = let lua = lua5_3; in lua53Packages.buildLuaPackage rec {
+ pname = "inifile";
+ name = "${pname}-${version}";
+ version = "1.0.2";
+ src = fetchFromGitHub {
+ owner = "bartbes";
+ repo = "inifile";
+ rev = "f0b41a8a927f3413310510121c5767021957a4e0";
+ sha256 = "1ry0q238vbp8wxwy4qp1aychh687lvbckcf647pmc03rwkakxm4r";
+ };
+ buildPhase = ":";
+ installPhase = ''
+ mkdir -p "$out/share/lua/${lua.luaversion}"
+ cp inifile.lua "$out/share/lua/${lua.luaversion}/"
+ '';
+ };
+
+ lua = lua5_3.withPackages (ps: with ps; [
+ # luaDbusProxy
+ # inifile
+ # inspect
+ lgi
+ luafilesystem
+ luaposix
+ # penlight
+ readline
+ ]);
+
+in stdenv.mkDerivation {
+ inherit pname;
+ version = "0.1";
+ src =./.;
+
+ inherit fennel;
+
+ buildInputs = [ lua gtk3 gobject-introspection.dev ];
+ nativeBuildInputs = [ lua makeWrapper ];
+
+ makeFlags = [ "PREFIX=${placeholder "out"}" ];
+ # GDK_PIXBUF_MODULE_FILE setting is to support SVG icons without
+ # their having been transformed to bitmaps.
+ # This makes a big difference to how many icons are displayed on
+ # my machine
+ postInstall = ''
+ mkdir -p $out/share/dbus-1/services
+
+ wrapProgram $out/bin/${pname} --set GDK_PIXBUF_MODULE_FILE ${librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache --set GI_TYPELIB_PATH "$GI_TYPELIB_PATH"
+ '';
+}
diff --git a/blinkenlicht/shell.nix b/blinkenlicht/shell.nix
@@ -0,0 +1,2 @@
+with import <nixpkgs> {} ;
+callPackage ./. {}