Created
December 19, 2024 03:10
-
-
Save agent-kilo/553befdada13a0d896c0d601834697ce to your computer and use it in GitHub Desktop.
A quick-and-dirty hack to manage WSLg windows with Jwno
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # | |
| # A quick-and-dirty hack to manage WSLg windows with Jwno | |
| # (https://github.com/agent-kilo/jwno) | |
| # | |
| # To try it out, save this script to the same directory where | |
| # your Jwno config resides, and do this in Jwno's REPL: | |
| # | |
| # (import wslg-transform) | |
| # (def wt (wslg-transform/wslg-transform jwno/context)) | |
| # (:enable wt) | |
| # | |
| # To disable it: | |
| # | |
| # (:disable wt) | |
| # | |
| # Caveats: | |
| # | |
| # 1. Only works for X11 windows. | |
| # 2. It does not escape window titles properly, certain characters | |
| # in window titles may break it. | |
| # 3. It gets really confused when there're more than one window with | |
| # the same title. | |
| # 4. A console window will flash-open when resizing/moving a WSLg | |
| # window. | |
| # 5. It's reeeeaaaally laggy. | |
| # | |
| # The author puts this file into the public domain. Happy hacking! 🤘 | |
| # | |
| (use jw32/_winuser) | |
| (use jw32/_util) | |
| (use jwno/util) | |
| (import jwno/log) | |
| (def wslg-title-peg | |
| (peg/compile | |
| ~{:main (sequence (opt (sequence :warn-text :s*)) :title-text (sequence :s* :distro-text -1)) | |
| :warn-text (sequence "[WARN:" (thru "]")) | |
| :title-text (capture (to (sequence :s* :distro-text -1))) | |
| :distro-text (sequence "(" (thru ")"))})) | |
| (defn normalize-title-pattern [title] | |
| (def clean-title | |
| (if-let [matched (peg/match wslg-title-peg title)] | |
| (first matched) | |
| (errorf "unknown window title format: %n" title))) | |
| (def dot-ascii (first ".")) | |
| (buffer/from-bytes | |
| ;(map (fn [c] | |
| (if (find |(= $ c) "+*()[]?!") | |
| dot-ascii | |
| c)) | |
| clean-title))) | |
| (defn wslg-window-transform [win orig-rect &opt tags wm normalize-fn] | |
| (default tags @{}) | |
| (default wm (:get-window-manager win)) | |
| (log/debug "transforming WSLg window: %n" (in win :hwnd)) | |
| (def scale (calc-pixel-scale orig-rect)) | |
| (def [scale-x scale-y] scale) | |
| (def merged-tags (merge (in win :tags) tags)) | |
| (def margins | |
| (if-let [v (in merged-tags :margins)] | |
| v | |
| (let [v (in merged-tags :margin 0)] | |
| {:top v | |
| :left v | |
| :bottom v | |
| :right v}))) | |
| (def scaled-margins | |
| (if (and (= 1 scale-x) (= 1 scale-y)) | |
| margins | |
| {:top (* scale-y (in margins :top)) | |
| :left (* scale-x (in margins :left)) | |
| :bottom (* scale-y (in margins :bottom)) | |
| :right (* scale-x (in margins :right))})) | |
| (log/debug "scaled-margins = %n" scaled-margins) | |
| (def rect (shrink-rect orig-rect scaled-margins)) | |
| (log/debug "final rect = %n" rect) | |
| (def win-title | |
| (with-uia [uia-win (:get-uia-element win nil wm)] | |
| (def name (:get_CurrentName uia-win)) | |
| (if normalize-fn | |
| (normalize-fn name) | |
| name))) | |
| (log/debug "win-title = %n" win-title) | |
| (let [x (in rect :left) | |
| y (in rect :top) | |
| [w h] (rect-size rect)] | |
| (os/execute ["pwsh.exe" | |
| "-WindowStyle" | |
| "hidden" | |
| "-Command" | |
| "wsl.exe" | |
| "--exec" | |
| "xdotool" | |
| "search" | |
| "--name" | |
| (string/format "'%s'" win-title) | |
| "windowmove" (string x) (string y) | |
| "windowsize" (string w) (string h)] | |
| :p))) | |
| (defn wslg-transform-on-window-created [self win uia-win exe-path _desktop-info] | |
| (when (or (not= "RAIL_WINDOW" (:get_CachedClassName uia-win)) | |
| (not (string/has-suffix? "\\msrdc.exe" exe-path))) | |
| (break)) | |
| (def normalize-fn | |
| (if-let [normalize-title-pattern-fn (in self :normalize-title-pattern-fn)] | |
| normalize-title-pattern-fn | |
| normalize-title-pattern)) | |
| (put win | |
| :transform | |
| (fn [win rect &opt tags wm] | |
| (wslg-window-transform win rect tags wm normalize-fn))) | |
| (put (in win :tags) :margins {:left 10 :top 10 :right 23 :bottom 43})) | |
| (defn wslg-transform-on-filter-forced-window [self _hwnd uia-win exe-path _desktop-info] | |
| (and (= "RAIL_WINDOW" (:get_CachedClassName uia-win)) | |
| (not= "" (:get_CachedName uia-win)) | |
| (string/has-suffix? "\\msrdc.exe" exe-path))) | |
| (defn wslg-transform-enable [self] | |
| (:disable self) | |
| (def win-created-hook-fn | |
| (:add-hook (in self :hook-manager) :window-created | |
| (fn [& args] | |
| (:on-window-created self ;args)))) | |
| (put self :win-created-hook-fn win-created-hook-fn) | |
| (def forced-win-hook-fn | |
| (:add-hook (in self :hook-manager) :filter-forced-window | |
| (fn [& args] | |
| (:on-filter-forced-window self ;args)))) | |
| (put self :forced-win-hook-fn forced-win-hook-fn)) | |
| (defn wslg-transform-disable [self] | |
| (def win-created-hook-fn (in self :win-created-hook-fn)) | |
| (def forced-win-hook-fn (in self :forced-win-hook-fn)) | |
| (when forced-win-hook-fn | |
| (put self :forced-win-hook-fn nil) | |
| (:remove-hook (in self :hook-manager) :filter-forced-window forced-win-hook-fn)) | |
| (when win-created-hook-fn | |
| (put self :win-created-hook-fn nil) | |
| (:remove-hook (in self :hook-manager) :window-created win-created-hook-fn))) | |
| (def wslg-transform-proto | |
| @{:on-window-created wslg-transform-on-window-created | |
| :on-filter-forced-window wslg-transform-on-filter-forced-window | |
| :enable wslg-transform-enable | |
| :disable wslg-transform-disable}) | |
| (defn wslg-transform [context] | |
| (def {:hook-manager hook-man} | |
| context) | |
| (table/setproto | |
| @{:hook-manager hook-man | |
| :normalize-title-pattern-fn nil} | |
| wslg-transform-proto)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment