liquid icon indicating copy to clipboard operation
liquid copied to clipboard

Use WINCH signal if available to trigger refresh on terminal resize

Open tom-adsfund opened this issue 2 years ago • 6 comments

In my window manager, i3, the shape of the terminal changes over time, and so if I return to Liquid, it's all jumbled up. Can we just have a running autorefresh, or something similar?

tom-adsfund avatar Sep 10 '23 08:09 tom-adsfund

I will give it at shot more, soon. As far as I remember it can be difficult to get information about the dimensions of the terminal on runtime. But I will give it another try :-)

mogenslund avatar Sep 14 '23 15:09 mogenslund

Wouldn't it be easiest to do a periodic refresh whatever happens? Maybe add a toggle to stop it if someone finds there's an issue?

tom-adsfund avatar Sep 14 '23 15:09 tom-adsfund

Pressing ESC twice usually redraws everything, but it does not take into account, if the terminal has be resized. I will have to recalculate the size of each window. I think periodic refreshes will be a bit annoying since the user will experience a slight flicker when nothing is happening. But if I manage to create a good resize function, it should be possible to make a toggle on the periodic refresh.

mogenslund avatar Sep 14 '23 15:09 mogenslund

stty size gives the terminal size. Which you're actually using already in tty_output.cljc.

SIGWINCH process signal tells when the terminal has been resized. But Java only has a non-standard mechanism to get it:

; Nice GPT4o solution:
; (:import [sun.misc Signal SignalHandler])

(defn handle-sigwinch [ch]
  (reify SignalHandler
    (handle [_ signal]
      (let [[rows cols] (get-terminal-size)]
        (put! ch {:rows rows :cols cols})))))

(defn redraw-editor [rows cols]
  (println (str "Terminal resized to " rows " rows and " cols " columns")))

(defn main-loop []
  (let [sigwinch-chan (chan)]
    (Signal/handle (Signal. "WINCH") (handle-sigwinch sigwinch-chan))
    (go-loop []
      (let [{:keys [rows cols]} (<! sigwinch-chan)]
        (redraw-editor rows cols)
        (recur)))))

But you can at least hook in stty size to the ESC ESC, and maybe put on a toggleable timer.

tom-adsfund avatar Jun 01 '24 09:06 tom-adsfund

Also:

; (:require [clojure.java.shell :refer [sh]]
(defn get-terminal-size []
  (let [size (sh "stty" "size")]
    (mapv #(Integer/parseInt %) (re-seq #"\d+" (:out size)))))

tom-adsfund avatar Jun 01 '24 09:06 tom-adsfund

#?(:clj
(try ; just ignore if class not found
  (let [
    signal-class (Class/forName "sun.misc.Signal")
    winch-signal (new sun.misc.Signal "WINCH")]
    (.handle sun.misc.Signal winch-signal (reify sun.misc.SignalHandler (handle [_ _] (redraw)))))
  (catch Exception e)))

I've tried it and it works triggering when the terminal size changes.

tom-adsfund avatar Jun 26 '24 14:06 tom-adsfund

This actually doesn't work at all with new versions of Java, which make these internal classes inaccessible.

To solve this in my copy of Liquid, I use the following (while requiring editor):

(defn- future-check-size-change [rs cs]
  (future (Thread/sleep 100)
          (let [rs2 (rows) cs2 (cols)]
            (if (or (not= rs rs2) (not= cs cs2))
              (do (editor/paint-all-buffer-groups) (editor/paint-buffer)))
            (future-check-size-change rs2 cs2))))


(def ^:private checking-size (atom nil))

(defn get-dimensions
  []
  (let [rs (rows) cs (cols)]
    (if (not @checking-size)
      (do
        (reset! checking-size true)
        (future-check-size-change rs cs)))
    {:rows rs :cols cs}))

tom-adsfund avatar Jul 10 '24 04:07 tom-adsfund