Unexpected-Keyboard icon indicating copy to clipboard operation
Unexpected-Keyboard copied to clipboard

Nix / Nix-on-Droid Module for Layout Generation

Open harryaskham opened this issue 4 months ago • 1 comments

I was spending quite a while finessing a bunch of variants of experimental layouts for UK, and ended up writing a Nix module for Unexpected Keyboard layout generation, in case this is useful to anyone.

It's written as a platform-agnostic module, so can be used either as a NixOS module, Nix-on-Droid module, home-manager module, etc.

Main contribution is a funky syntax for defining layouts in Nix code, which can then also be manipulated in Nix to generate variations (i.e. left and right handed modes, split layouts, additional modifier columns, etc), which are ultimately written out as XML files to /etc/unexpected_keyboard and (on Nix-on-Droid) optionally then copied out to /storage/emulated/0/... to be picked up by a layout loader in UK.

Layout Variants Example: Gives a sense of how keyboards are defined (one can also just write XML, or mix and match this syntax for one row with raw XML for the next).

A snippet from one of my layouts:

  {
    name = "Code QWERTY";
    bottomRow = false;
    rows = with codes; [
      {
        keys =
          K             ne."1"
                   c.q
            sw."!"      "⎋" se.esc
          _
                        ne."2"
                   c.w
            sw."@"
          _
                        ne."3"
                   c.e
            sw."#"
          _
            nw."£"      ne."4"
                   c.r
            sw."$"

        ...

          _
            nw.del      ne."0"
                   c.p
            sw.bsp
          K;
      }
      ...
    ];
}

and a snippet from one of the transforms, which is used to transform a layout into a split layout with mod keys in the middle:

{
  ...
  Grid = {gap, paddingL, paddingR, ...} @ args: precompose [
    clearModsAndEsc
    returnOverCursor
    (updateKey 0 5 (addShift paddingR))
    (updateKey 1 5 (addShift paddingR))
    (insertCol 5 (modGridL args))
    (insertCol 6 (modGridR args))
  ];
  ...
}

It's currently part of my system flake as the syntactical and type-checking stuff relies on other parts of the library, but the module should already be independently usable via the flake's exposed agnosticModules, and I'll plan to split it out to its own NUR-homed flake.

I am loading the layouts myself in a fork of UK that supports loading XML from a directory, but before I'd set that up, I just wrote a janky shell script I could run from Nix-on-Droid that would copy each layout to clipboard with a 10 second delay between, making it somewhat easy to run in the background and manually paste the layouts one-by-one into the UK app settings custom layouts panel.

Lastly I'm putting together a UI around this that builds the Nix module into a WASM Nix evaluator based off of tvix, so there'll be a simple way to generate, edit, validate, share layouts in this format (though the idea was that it's a visual enough syntactic representation that editing the text is intuitive).

harryaskham avatar Aug 05 '25 12:08 harryaskham

thanks for this, you had a good time

generate, edit, validate, share layouts

and where are they shared? or you mean in a private scope?

simper66 avatar Sep 14 '25 07:09 simper66