Compare commits

..

6 Commits

Author SHA1 Message Date
bluepython508
e6fc901c26 Fix error on change main tag 2026-02-19 10:37:34 +00:00
bluepython508
2b1bc9e7fb Add floating htop 2026-02-19 10:12:14 +00:00
bluepython508
0ac299589e Fix pointer warp 2026-02-19 09:49:30 +00:00
bluepython508
6ec59c3be2 Floating windows by default (pinentry, ...); pointer warp; clear other tags on main tag switch 2026-02-19 09:08:14 +00:00
bluepython508
7c6abcaddb Change formatting for status display: <t 2026-02-16 22:05:57 +00:00
bluepython508
db554d2d52 Improve pulldowns & status display 2026-02-16 17:08:35 +00:00
2 changed files with 123 additions and 74 deletions

View File

@@ -24,7 +24,20 @@
(def registry @{}) (def registry @{})
(def wl-outputs @{}) (def wl-outputs @{})
(def wm @{:outputs @[] :seats @[] :windows @[] :render-order @[] :inputs @[] :pulldowns @{}}) (def wm @{:outputs @[] :seats @[] :windows @[] :render-order @[] :inputs @[] :pulldowns @{} :callbacks @{}})
(defn callback [id & args]
(each cb (get-in wm [:callbacks id] {}) (cb ;args)))
(defmacro register-callback [id args & body]
(def keysym (gensym))
~(do
(def ,keysym [:callbacks ,id @{}])
(defn clear [] (put-in wm ,keysym nil))
(put-in wm ,keysym (fn ,args ,;body))
clear))
(defmacro register-callback-once [id args & body]
'(register-callback ,id ,args ,;body '(clear)))
(def tags-main [:a :r :s :t :n :e :i :o]) (def tags-main [:a :r :s :t :n :e :i :o])
(def tags-offload [:comma :period]) (def tags-offload [:comma :period])
@@ -128,7 +141,7 @@
(defn handle-event [event] (defn handle-event [event]
(match event (match event
[:closed] (put window :closed true) [:closed] (put window :closed true)
[:dimensions-hint min-w min-h max-w max-h] (put window :dimensions-bounds {:min-w min-w :max-w max-w :min-h min-h :max-h max-h}) [:dimensions-hint min-w min-h max-w max-h] (put window :dimensions-bounds [min-w max-w min-h max-h])
[:dimensions w h] (do (put window :w w) (put window :h h)) [:dimensions w h] (do (put window :w w) (put window :h h))
[:app-id app-id] (put window :app-id app-id) [:app-id app-id] (put window :app-id app-id)
[:parent parent] (put window :parent (if parent (:get-user-data parent))) [:parent parent] (put window :parent (if parent (:get-user-data parent)))
@@ -146,40 +159,15 @@
(each k keys (each k keys
(put obj k nil)))) (put obj k nil))))
(defn output/set-main-tag [output tag]
(map |(when (= ($ :tag-main) tag) (put $ :tag-main nil)) (wm :outputs))
(put output :tag-main tag)
(put output :tag-offload nil))
(defn output/toggle-other-tag [output tag]
(if (has-value? (output :tags-other) tag)
(do
(put output :tags-other (filter |(not (= $ tag)) (output :tags-other)))
false)
(do
(map (fn [output] (update output :tags-other (fn [tags] (filter |(not (= $ tag)) tags)))) (wm :outputs))
(update output :tags-other |[;$ tag])
true)))
(defn output/toggle-offload-tag [output tag]
(put output :tag-offload (if (= (output :tag-offload) tag) nil tag)))
(defn output/ensure-main-tag [output]
(when (not (output :tag-main))
(def available (->> tags-main
(reverse)
(filter (fn [tag] (not (some |(= tag ($ :tag-main)) (wm :outputs)))))))
(def tag (or
(find (fn [tag] (not (some |(= ($ :tag) tag) (wm :windows)))) available)
(first available)))
(put output :tag-main tag)))
(defn output/manage [output] (defn output/manage [output]
(put output :tags [(output :tag-main) (def new-tags [(output :tag-main)
;(output :tags-other) ;(output :tags-other)
;(if (output :tag-offload) ;(if (output :tag-offload)
[[(output :tag-main) (output :tag-offload)]] [[(output :tag-main) (output :tag-offload)]]
[])])) [])])
(unless (= new-tags (output :tags))
(put output :tags new-tags)
(callback [:output-new-tags (get-in output [:wl-output :name])] new-tags)))
(defn window/enter-fullscreen [window &opt inform] (defn window/enter-fullscreen [window &opt inform]
(put window :fullscreen true) (put window :fullscreen true)
@@ -241,6 +229,10 @@
(put seat :focused nil)) (put seat :focused nil))
:none (focus-non-layer))) :none (focus-non-layer)))
(defn window/should-float [window]
(let [[min-w max-w min-h max-h] (or (window :dimensions-bounds) [0 1 0 1])]
(and (= min-w max-w) (= min-h max-h) (< 0 min-w) (< 0 min-h))))
(defn window/manage [window] (defn window/manage [window]
(when (window :new) (when (window :new)
(:use-ssd (window :obj)) (:use-ssd (window :obj))
@@ -251,11 +243,12 @@
(:propose-dimensions (window :obj) 0 0)) (:propose-dimensions (window :obj) 0 0))
(do (do
(def seat (first (wm :seats))) (def seat (first (wm :seats)))
(window/set-float window false) (window/set-float window (window/should-float window))
(window/move-output window (seat :focused-output)) (window/move-output window (seat :focused-output))
(each seat (wm :seats) (seat/focus seat window)))) (each seat (wm :seats) (seat/focus seat window))))
(when-let [rule ((config :appid-rules) (window :app-id))] (when-let [rule ((config :appid-rules) (window :app-id))]
(rule window))) (rule window))
(callback :new-window window))
(match (window :fullscreen-requested) (match (window :fullscreen-requested)
[:enter] (window/enter-fullscreen window) [:enter] (window/enter-fullscreen window)
@@ -345,14 +338,49 @@
(layout/main (length windows) W H)) (layout/main (length windows) W H))
(each window (filter |($ :float) (output/windows output)) (each window (filter |($ :float) (output/windows output))
(let [t (+ Y (/ H 4)) (callback :layout-floating window)
l (+ X (/ W 4)) (let [[min-w max-w min-h max-h] (or (window :dimensions-bounds) [(/ W 2) (/ W 2) (/ H 2) (/ H 2)])
h (/ H 2) h (if (< 10 min-h) min-h (/ H 2))
w (/ W 2)] w (if (< 10 min-w) min-w (/ W 2))
t (+ Y (/ H 2) (- (math/trunc (/ h 2))))
l (+ X (/ W 2) (- (math/trunc (/ w 2))))]
(window/set-position window l t) (window/set-position window l t)
(window/propose-dimensions window w h) (window/propose-dimensions window w h)
(:place-top (window :node))))) (:place-top (window :node)))))
(defn output/set-main-tag [output tag]
(map |(when (= ($ :tag-main) tag) (put $ :tag-main nil)) (wm :outputs))
(put output :tag-main tag)
(put output :tag-offload nil)
(put output :tags-other []))
(defn output/toggle-other-tag [output tag]
(if (has-value? (output :tags-other) tag)
(do
(put output :tags-other (filter |(not (= $ tag)) (output :tags-other)))
false)
(do
(map (fn [output] (update output :tags-other (fn [tags] (filter |(not (= $ tag)) tags)))) (wm :outputs))
(update output :tags-other |[;$ tag])
(when-let [window (find |(= ($ :tag) tag) (wm :windows))]
(put window :output output)
(seat/focus (first (wm :seats)) window))
true)))
(defn output/toggle-offload-tag [output tag]
(put output :tag-offload (if (= (output :tag-offload) tag) nil tag)))
(defn output/ensure-main-tag [output]
(when (not (output :tag-main))
(def available (->> tags-main
(reverse)
(filter (fn [tag] (not (some |(= tag ($ :tag-main)) (wm :outputs)))))))
(def tag (or
(find (fn [tag] (not (some |(= ($ :tag) tag) (wm :windows)))) available)
(first available)))
(put output :tag-main tag)))
(defn wm/manage [] (defn wm/manage []
(set (wm :render-order) (filter |(not ($ :closed)) (wm :render-order))) (set (wm :render-order) (filter |(not ($ :closed)) (wm :render-order)))
(defn close [o] (not (when (or (o :removed) (o :closed)) (:destroy (o :obj)) true))) (defn close [o] (not (when (or (o :removed) (o :closed)) (:destroy (o :obj)) true)))
@@ -360,8 +388,6 @@
(set (wm :windows) (filter close (wm :windows))) (set (wm :windows) (filter close (wm :windows)))
(set (wm :seats) (filter close (wm :seats))) (set (wm :seats) (filter close (wm :seats)))
(defn output/set-main-tag [output tag]
(map |(if (= ($ :tag-main)) tag) (wm :outputs)))
(map seat/manage (wm :seats)) (map seat/manage (wm :seats))
(map output/ensure-main-tag (wm :outputs)) (map output/ensure-main-tag (wm :outputs))
(map output/manage (wm :outputs)) (map output/manage (wm :outputs))
@@ -376,6 +402,7 @@
(:manage-finish (registry "river_window_manager_v1"))) (:manage-finish (registry "river_window_manager_v1")))
(defn rgb-to-u32-rgba [rgb] (defn rgb-to-u32-rgba [rgb]
[(* (band 0xff (brushift rgb 16)) (/ 0xffff_ffff 0xff)) [(* (band 0xff (brushift rgb 16)) (/ 0xffff_ffff 0xff))
(* (band 0xff (brushift rgb 8)) (/ 0xffff_ffff 0xff)) (* (band 0xff (brushift rgb 8)) (/ 0xffff_ffff 0xff))
@@ -420,18 +447,22 @@
(defn mark-dirty [] (defn mark-dirty []
(:manage-dirty (registry "river_window_manager_v1"))) (:manage-dirty (registry "river_window_manager_v1")))
(defn status-display [] (defn status-display [output]
(def seat (first (wm :seats))) (def output (find |(= (get-in $ [:wl-output :name]) output) (wm :outputs)))
(def output (seat :focused-output))
(string (string
":" (output :tag-main) ":"
(case (output :tag-offload) (if (= (output :tag-offload) :comma) "<" "")
:comma "<" (output :tag-main)
:period ">" (if (= (output :tag-offload) :period) ">" "")
"")
" " " "
(string/join (map |(string ":" $) (filter keyword? (output :tags-other)))))) (string/join (map |(string ":" $) (filter keyword? (output :tags-other))))))
(defn status-watcher [output]
(def out (dyn *out*))
(def clear (register-callback [:output-new-tags output] [tags] (with-dyns [*out* out] (print (status-display output)))))
(print (status-display output))
(defer (clear) (forever (ev/sleep 0.1))))
(defn action/target-in [obj list dir] (defn action/target-in [obj list dir]
(let [i (or (index-of obj list) -1)] (let [i (or (index-of obj list) -1)]
@@ -459,7 +490,10 @@
outputs (wm :outputs) outputs (wm :outputs)
new-output (action/target-in current-output outputs dir)] new-output (action/target-in current-output outputs dir)]
(seat/focus-output seat new-output) (seat/focus-output seat new-output)
(seat/focus seat (first (output/windows new-output)))))) (seat/focus seat (first (output/windows new-output)))
(def [x y] (new-output :position))
(def [w h] (new-output :dimensions))
(:pointer-warp (seat :obj) (+ x (/ w 2)) (+ y (/ h 2))))))
(defn action/goto-tag [tag] (defn action/goto-tag [tag]
(def f (cond (def f (cond
@@ -521,20 +555,25 @@
(def appid (window :app-id)) (def appid (window :app-id))
(put window :tag [:pulldown appid]) (put window :tag [:pulldown appid])
(window/set-float window true) (window/set-float window true)
(when-let [w ((wm :pulldowns) appid)] (when-let [w ((wm :pulldowns) appid)
open (not (w :closed))]
(:close (w :obj))) (:close (w :obj)))
(put (wm :pulldowns) appid window))) (put (wm :pulldowns) appid window)))
(defn action/pulldown [appid command] (defn action/pulldown [appid command]
(fn [seat binding] (fn [seat binding]
(when (output/toggle-other-tag (seat :focused-output) [:pulldown appid]) (let [output (seat :focused-output)
(if-let [window (get-in wm [:pulldowns appid])] tag [:pulldown appid]
(if-not (window :closed) has-tag (has-value? (output :tags-other) tag)
(do window (get-in wm [:pulldowns appid])
(put window :output (seat :focused-output)) closed (when window (window :closed))
(seat/focus seat window)) has-window (and window (not closed))
(spawn command)) toggle-tag (delay (output/toggle-other-tag output tag))
(spawn command))))) open (delay (spawn command))]
(cond
has-window (toggle-tag)
has-tag (open)
(do (open) (toggle-tag))))))
(defn locked-screen [] (defn locked-screen []
@@ -578,6 +617,7 @@
[:o G (action/spawn "wl-present-ui")] [:o G (action/spawn "wl-present-ui")]
[:t G (action/pulldown "floating-terminal" "alacritty --class floating-terminal -e tmux new-session -ADX -s floating")] [:t G (action/pulldown "floating-terminal" "alacritty --class floating-terminal -e tmux new-session -ADX -s floating")]
[:r G (action/pulldown "floating-repl" "alacritty --class floating-repl -e censtablo-repl")] [:r G (action/pulldown "floating-repl" "alacritty --class floating-repl -e censtablo-repl")]
[:h G (action/pulldown "floating-htop" "alacritty --class floating-htop -e htop")]
[:XF86AudioLowerVolume {} (action/spawn "pamixer -d 5")] [:XF86AudioLowerVolume {} (action/spawn "pamixer -d 5")]
[:XF86AudioRaiseVolume {} (action/spawn "pamixer -i 5")] [:XF86AudioRaiseVolume {} (action/spawn "pamixer -i 5")]
[:XF86AudioMute {} (action/spawn "pamixer -t")] [:XF86AudioMute {} (action/spawn "pamixer -t")]
@@ -590,12 +630,15 @@
(defn rule/tag [tag] (fn [window] (put window :tag tag))) (defn rule/tag [tag] (fn [window] (put window :tag tag)))
(defn rule/float [float] (fn [window] (put window :float float)))
(put config :appid-rules @{"thunderbird" (rule/tag :m) (put config :appid-rules @{"thunderbird" (rule/tag :m)
"vesktop" (rule/tag :d) "vesktop" (rule/tag :d)
"io.github.alainm23.planify" (rule/tag :j) "io.github.alainm23.planify" (rule/tag :j)
"floating-terminal" (rule/pulldown) "floating-terminal" (rule/pulldown)
"floating-repl" (rule/pulldown)}) "floating-repl" (rule/pulldown)
"floating-htop" (rule/pulldown)
"steam" (rule/float false)})
(defn startup [] (defn startup []
@@ -608,8 +651,10 @@
(spawn "io.github.alainm23.planify")) (spawn "io.github.alainm23.planify"))
# TODO: layout flexibility # TODO: layout flexibility
# TODO: fullscreen oddities with games: debug (warframe, novadrift, portal)
# TODO: reload with repl? # TODO: firefox focus weirdness
# TODO: double fullscreen
# TODO: swaylock hibernate acquire outputs
(defn configure-keymap [] (defn configure-keymap []
(def ctx (xkbcommon/context/new)) (def ctx (xkbcommon/context/new))

View File

@@ -3,19 +3,23 @@
(defn run-commands [sock args] (defn run-commands [sock args]
(with [stream (net/connect :unix sock)] (with [stream (net/connect :unix sock)]
(def recv (msg/make-recv stream)) (def recv- (msg/make-recv stream))
(def send (msg/make-send stream)) (def send (msg/make-send stream))
(send (string/format "\xFF%j" {:auto-flush false :name ""})) (send (string/format "\xFF%j" {:auto-flush true :name ""}))
(defn recv []
(def ms (recv-))
(case (ms 0)
nil ms
0xFF (do
(prin (slice ms 1))
(flush)
(recv))
0xFE (slice ms 1))
ms)
(def prompt (recv)) (def prompt (recv))
(each arg args (each arg args
(send (string "\xFF" arg)) (send arg)
(def [ok val] (-> (recv) (loop [:until (= prompt (recv))]))))
(parse)))
(if (string? val)
(print val)
(pp val))
(unless ok
(os/exit 1)))))
(defn main [exe & args] (defn main [exe & args]
(def sock (string (os/getenv "XDG_RUNTIME_DIR") "/censtablo-" (os/getenv "WAYLAND_DISPLAY"))) (def sock (string (os/getenv "XDG_RUNTIME_DIR") "/censtablo-" (os/getenv "WAYLAND_DISPLAY")))