This page is a collection of recipes for some  funny tricks  tasks in Jwno. They may need to be modified before getting incorporated into your own config file. You can usually try them out in the REPL first.

Get Detailed Info From a Window

The details include window title, window class, exe file path, etc. Just bind the :describe-window command to some keys and invoke it. It's Win + W D in the example config.

(:define-key keymap "Win + W  D" :describe-window)

Ignore Windows

Use the :filter-window hook for this:

(:add-hook (in jwno/context :hook-manager) :filter-window
   (fn [_hwnd uia _exe _desktop]
     (not= "SomeNaughtyWindowClass" (:get_CachedClassName uia))))

This code ignores windows according to their class names. Change (:get_CachedClassName uia) into (:get_CachedName uia) if you want to check the windows' title instead.

Please see Windows documentation for other useful properties you can get from the uia object.

Forcibly Manage Windows

Use the :filter-forced-window hook for this:

(:add-hook (in jwno/context :hook-manager) :filter-forced-window
   (fn [_hwnd _uia exe _desktop]
     (string/has-suffix? "\\SomeNaughtyExecutable.exe" exe)))

Here we are checking a window's exe file path, to see if we should force it.

To determine if a window is suitable for forced management, you can invoke the :manage-window command on it, and see how it reacts. This command is bound to Win + W M in the example config.

The :filter-forced-window hook will override the :filter-window hook.

Set Uniform Window Gaps

Window "gaps" are implemented by window margins and frame paddings in Jwno. To get uniform (evenly-sized) gaps between windows, and between windows and screen edges, you do it in two steps:

# First set margin values for all windows
(:add-hook (in jwno/context :hook-manager) :window-created
   (fn [win _uia _exe _desktop]
     (put (in win :tags) :margin 10)))

# Then set padding values for top-level frames
(:add-hook (in jwno/context :hook-manager) :monitor-updated
   (fn [frame]
     (put (in frame :tags) :padding 10)))

The code above will create gaps with a size of 20 virtual pixels (the gaps will scale with monitor DPI settings).

Adjust Top-Level Frames for Ultrawide Monitors

Sometimes an ultrawide monitor is just... too wide. You can adjust Jwno's top-level frames, so that they show your managed windows around the center of your screen. This can be achieved in multiple ways.

You can :transform a top-level frame directly:

(import jwno/util)

(def top-frame (:get-current-top-frame (get-in jwno/context [:window-manager :root])))
(def rect (in top-frame :rect))

# This will reserve 500 pixels of space on the left and right sides of your monitor.
(:transform top-frame (util/shrink-rect rect {:left 500 :right 500 :top 0 :bottom 0}))
(:retile (in jwno/context :window-manager) top-frame)

# To restore the layout that fills the whole screen:
(:transform top-frame (get-in top-frame [:monitor :work-area]))
(:retile (in jwno/context :window-manager) top-frame)

Or, you can use frame paddings to do the same thing automatically:

(import jwno/util)

(defn is-my-ultrawide-monitor? [monitor]
  (def [width height] (util/rect-size (in monitor :rect)))
  (>= (/ width height) (/ 21 9)))

(:add-hook (in jwno/context :hook-manager) :monitor-updated
   (fn [top-frame]
     (when (is-my-ultrawide-monitor? (in top-frame :monitor))
       (put (in top-frame :tags) :paddings {:left 500 :right 500 :top 10 :bottom 10}))))

But this second method only takes effect when a new monitor is detected, or when your monitor configuration is actually changed, e.g. you set it to a different DPI or resolution. It will also override your frame's :padding setting.

Adjust Window Transparency (Alpha Value)

Just invoke the :change-window-alpha command on your window. Pass a negative argument to decrease the alpha value (increase transparency), or a positive argument to increase the alpha value (decrease transparency). This command is bound in a transient keymap, enabled by Win + A, in the example config.

To automatically set the alpha value for selected windows:

(:add-hook (in jwno/context :hook-manager) :window-created
   (fn [win uia _exe _desktop]
     (def class-name (:get_CachedClassName uia))
     (when (find |(= $ class-name)
                 ["Emacs"
                  "ConsoleWindowClass"            # Console windows
                  "CASCADIA_HOSTING_WINDOW_CLASS" # Windows Terminal windows
                  ])
       (:set-alpha win (math/floor (* 256 0.9))))))

It will set the windows with the specified class names to 90% opaqueness (10% transparency).

Note that not all windows' alpha values can be altered like this.

Dialog Window Placement

Most dialog windows show little info, and don't need to expand to cover the whole frames, if their parent frames take up big portions of the screen. You can tell Jwno not to enlarge them:

(:add-hook (in jwno/context :hook-manager) :window-created
   (fn [win uia _exe _desktop]
     (def class-name (:get_CachedClassName uia))
     (when (= "#32770" class-name) # Dialog window class
       # Place the window at the :center. You can also
       # use :left, :bottom-right etc.
       (put (in win :tags) :anchor :center)
       # Change :no-expand to :no-resize, if you don't
       # want no resizing at all.
       (put (in win :tags) :no-expand true))))

Keymap Hint Placement

By default, Jwno shows the keymap hints at the top-left corner of your active monitor. You can, for example, change it to the top edge of your monitor:

(:set-tooltip-anchor (in jwno/context :ui-manager) :keymap :top)

Or the bottom-right corner, etc.:

(:set-tooltip-anchor (in jwno/context :ui-manager) :keymap :bottom-right)

Automatically Put a Window Into a Specific Frame

It can be done in the :window-created hook:

(:add-hook (in jwno/context :hook-manager) :window-created
   (fn [win _uia _exe _desktop]
     (when (is-my-special-window? win)
       (def frame (get-my-special-frame))
       (put (in win :tags) :frame frame))))

Get Notified When a Window's Title Changes

A custom UIAutomation event handler can be used for this:

(import jwno/util)
(import jwno/log)
(use jw32/_uiautomation)

# The window object we are interested in.
(def win ...)

# The handler function. We just print some simple log here.
(defn handler-fn [sender prop-id new-value]
  (when (= prop-id UIA_NamePropertyId)
    (log/info "++++ Window title changed to: %n ++++" new-value)))

# To register the handler:
(def handler
  (util/with-uia [elem (:get-uia-element win)]
    (:AddPropertyChangedEventHandler
       (get-in jwno/context [:uia-manager :com])
       elem
       TreeScope_Element
       nil
       handler-fn
       [UIA_NamePropertyId])))

# To remove the handler later:
(util/with-uia [elem (:get-uia-element win)]
  (:RemovePropertyChangedEventHandler
     (get-in jwno/context [:uia-manager :com])
     elem
     handler)
  (:Release handler))

Most of UIAutomation elements' properties can be "watched" in this way.

But note that the UIAutomation interface is quite low-level, and subject to change. Improper use will result in memory leaks or crashes.

Bind Win + L to Something Else

To use Win + L in Jwno, you need to disable the Windows lock screen first.

Create a file named DisableLockWorkstation.reg:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System]
"DisableLockWorkstation"=dword:00000001

And a file named EnableLockWorkstation.reg:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System]
"DisableLockWorkstation"=-

Then use them in a Powershell script, say lock.ps1:

$EnableReg = Join-Path $PSScriptRoot "EnableLockWorkstation.reg"
$DisableReg = Join-Path $PSScriptRoot "DisableLockWorkstation.reg"

reg import $EnableReg
sleep 0.5
rundll32 user32.dll,LockWorkStation
sleep 0.5
reg import $DisableReg

Whenever you want to lock the screen, run lock.ps1 (as administrator), instead of pressing Win + L. Now you can bind Win + L to whatever you like in Jwno.

You can bind the script to some keys in Jwno too:

(:define-key keymap
             "Win + Enter L"
             [:exec
              true
              "pwsh.exe"
              "-WindowStyle"
              "Hidden"
              "-Command"
              ```Start-Process -FilePath "pwsh" -ArgumentList "C:\path\to\lock.ps1" -Verb RunAs -WindowStyle Hidden```]
             "Lock screen")

Caveats:

Everything about the lock screen will be disabled, e.g. the screen will never be automatically locked, even after waking up from sleeping. And the UAC window will pop up everytime you run lock.ps1.

Completely Hide the Taskbar

In Settings -> Personalization -> Taskbar, enable Automatically hide the taskbar in desktop mode, and then:

(use jwno/util)
(use jw32/_uiautomation)
(use jw32/_winuser)

(def uia-com (get-in jwno/context [:uia-manager :com]))

(def taskbar-hwnd
  (with-uia [root (:GetRootElement uia-com)]
    (with-uia [condition (:CreatePropertyCondition uia-com UIA_NamePropertyId "Taskbar")]
      (with-uia [elem (:FindFirst root TreeScope_Children condition)]
        (:get_CurrentNativeWindowHandle elem)))))

# To hide it completely
(ShowWindow taskbar-hwnd SW_HIDE)

# To show it again
(ShowWindow taskbar-hwnd SW_SHOW)

Remove the Title Bar

Define these functions, and call them on the window object you want the title bar removed:

(use jw32/_winuser)
(use jw32/_util)

(defn remove-window-title [win]
  (def hwnd (in win :hwnd))
  (def st (int/u64 (signed-to-unsigned-32 (GetWindowLong hwnd GWL_STYLE))))
  (def new-st
    (if (= WS_CAPTION (band st WS_CAPTION))
      # Doc from MS states that WS_CAPTION includes WS_BORDER. We
      # Clear the tile bar but keep the border
      (-> st
          (band (bnot WS_CAPTION))
          (bor WS_BORDER))
      # else
      st))
  (unless (= st new-st)
    (SetWindowLong hwnd GWL_STYLE (int/to-number new-st))
    (SetWindowPos hwnd nil 0 0 0 0 (bor SWP_FRAMECHANGED
                                        SWP_NOSIZE
                                        SWP_NOMOVE
                                        SWP_NOZORDER
                                        SWP_NOOWNERZORDER
                                        SWP_NOACTIVATE))))

(defn restore-window-title [win]
  (def hwnd (in win :hwnd))
  (def st (int/u64 (signed-to-unsigned-32 (GetWindowLong hwnd GWL_STYLE))))
  (def new-st
    (if (not= WS_CAPTION (band st WS_CAPTION))
      (bor st WS_CAPTION)
      # else
      st))
  (unless (= st new-st)
    (SetWindowLong hwnd GWL_STYLE (int/to-number new-st))
    (SetWindowPos hwnd nil 0 0 0 0 (bor SWP_FRAMECHANGED
                                        SWP_NOSIZE
                                        SWP_NOMOVE
                                        SWP_NOZORDER
                                        SWP_NOOWNERZORDER
                                        SWP_NOACTIVATE))))

Note that some windows may get confused when their title bars are removed, and will have weird behavior.

Export a Value to the REPL

If you want to access something defined in your config file from the REPL, you can export it in you config:

(import jwno/util)
(def repl-server
  (or (:get-default-server (in jwno/context :repl-manager))
      (:start-server (in jwno/context :repl-manager))))
(util/export-to-repl repl-server my-awesome-stuff)

It will automatically start an REPL server if no server is already running, and export the value my-awesome-stuff.

Next Step

See Also