Playing With WSLg Windows


Jwno And WSLg Windows

When it comes to window management, the WSLg windows are quite unique, because they live in both worlds. The cool kids from Microsoft is using a custom Weston compositor to manage the Linux windows, passing the rendered content through a fancy local RDP connection. The Linux GUI we see on our Windows desktops are, in fact, “remote” windows created by Microsoft Remote Desktop Client (msrdc.exe).

It surely has a lot of moving parts, but works great in general. That is, when we manipulate our windows with a mouse. There are long-standing issues in the WSLg repository tracking window movement/layout problems from the Windows side. TL; DR: If we call Win32 APIs, such as SetWindowPos, instead of using the mouse, on a WSLg window, the window on the Linux side would have weird behavior.

If you invoke Jwno’s :describe-window command (bound to Win+W D in the example config) on a WSLg window, you would find that Jwno doesn’t see it as a normal window, because it doesn’t even have a Window Control Pattern:

You can try to force Jwno to manage a WSLg window using the :manage-window command (bound to Win+W M in the example config), and it would look like that window got resized/moved successfully, but the actual Linux GUI would stay where it was, making the WSLg window shown on screen misaligned with the real UI.

I understand that it’s hard to sync states between two distinct graphical environments, but this issue renders Jwno completely useless against WSLg windows.

Well, maybe not completely. Jwno has such a funny design (cough not a compliment cough), we can still hack it, to have some fun!

Hacking It (in Lisp Style)

In Common Lisp (yeah it’s an all-of-a-sudden-detour, buckle up), functions are mostly referenced by their names. That means the actual bodies of the functions can be re-defined at runtime, and be available to their callers immediately. E.g. you can do this:

ECL (Embeddable Common-Lisp) 24.5.10 (git:UNKNOWN)
[...banner text omitted...]
> (defun callee () (format t "printing from CALLEE~%"))
CALLEE
> (defun caller () (callee))
CALLER
> (caller)
printing from CALLEE
NIL
> (defun callee () (format t "printing from NEW CALLEE~%")) ;; re-define CALLEE
CALLEE
> (caller) ;; call CALLER again, the new definition for callee is available immediately
printing from NEW CALLEE
NIL

This is handy, since you can “patch” a running program by simply re-defining existing functions. I did this in my StumpWM config on my Linux machines, to work around bugs and tweak WM behavior.

But, if we try the same trick in Janet:

Janet 1.37.1-83e8aab2 linux/aarch64/clang - '(doc)' for help
repl:1:> (defn callee [] (printf "printing from CALLEE"))
<function callee>
repl:2:> (defn caller [] (callee))
<function caller>
repl:3:> (caller)
printing from CALLEE
nil
repl:4:> (defn callee [] (printf "printing from NEW CALLEE"))
<function callee>
repl:5:> (caller)
printing from CALLEE
nil

Oops, caller still uses the old definition of callee. That’s because, unlike Common Lisp, Janet functions are mostly referenced and passed around by their values, and they get “frozen” at their call sites at compile-time.

To make things more “dynamic”, Jwno exports most of its APIs as objects with their own methods. Objects in Janet are just mutable tables with prototypes, so we can override their methods by simply changing their table content.

Take the WSLg windows we talked about as an example (finally!), since normal transformation using Win32 APIs doesn’t work for them, we’ll provide our own :transform method for these window objects.

What do we do to actually transform the WSLg windows then? When playing with them, I found that some of them respond correctly to X11-related commands on the Linux side, e.g. xdotool, and wsl.exe on the Windows side can pass our window geometries to those commands.

(I should mention that I’m certainly not the first one to come up with such a trick.)

So invoking this command is exactly what we’re going to do:

wsl.exe --exec xdotool search --name <window title> windowmove <x> <y> windowsize <width> <height>

And the control flow would make a roundtrip between the host system and the Linux VM:

But, I failed to find a similar command for the custom Weston compositor, or for Wayland in general, so the trick only works for X11 windows. I did a quick-and-dirty implementation as a separate Janet script, and here’s how it works on the good old glxgears:

You can see it’s reeeaaally laggy, due to the host <-> VM roundtrip. And there’s a console window flash-opening every time the WSLg window is manipulated 😂.

The Actual Code

In case you want to try it out, the whole script is here. Make sure you read the comments for usage tips and caveats. It’s a playful hack after all, and too unstable to be included in Jwno’s core code.

Until next time! 🤘

Get Jwno

Buy Now$10.00 USD or more

Leave a comment

Log in with itch.io to leave a comment.