MoreRSS

site iconJonas HietalaModify

A writer, developer and wannabe code monkey.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of Jonas Hietala

Securing my partner's digital life

2025-01-20 08:00:00

I’ve been with Veronica for over a decade now and I think I’m starting to know her fairly well. Yet she still manages to surprise me. For instance, a couple of weeks ago she came and asked me about email security:

I worry that my email password is too weak. Can you help me change email address and make it secure?

It was completely unexpected—but I’m all for it.

The action plan

All heroic journeys needs a plan; here’s mine:

  1. Buy her a domain name (her .com surname was available).
  2. Migrate her email to Fastmail.
  3. Setup Bitwarden as a password manager.
  4. Use a YubiKey to secure the important services.

Why a domain?

If you ever want (or need) to change email providers it’s very nice to have your own domain. For instance, Veronica has a hotmail.com address but she can’t bring that with her if she moves to Fastmail.

Worse, what if she gets locked out of her Outlook account for some reason? It might happen if you forget your password, someone breaks into your account, or even by accident.

In almost all cases, your email is your key to the rest of your digital life. The email address is your username and to reset your password you use your email. If you lose access to your email you lose everything.

When you control your domain, you can point the domain to a new email provider and continue with your life.

Why pay for email?

One of the first things Veronica told me when I proposed that she’d change providers was that she didn’t want to pay. It’s a common sentiment online that email must be cheap (or even free).

I don’t think that email is the area where cost should be the most significant factor. As I argued for in why you should own your email’s domain, your email is your most important digital asset. If email is so important, why try to be cheap about it? You should spend your money on the important things and shouldn’t spend money on the unimportant things.

Paying for email gives you a couple of nice things:

  • Human support.

    It’s all too easy to get shafted by algorithms where you might get banned because you triggered some edge case (such as resetting your password outside your usual IP address).

  • Ability to use your own domain.

    Having a custom domain is a paid feature at most email providers.

  • A long-term viable business.

    How do you run an email company if you don’t charge for it?
    (You sell out your users or you close your business.)

Why a password manager?

The best thing you can do security wise is to adopt a password manager. Then you don’t have to try to remember dozens of passwords (leading to easy-to-remember and duplicate passwords) and can focus on remembering a single (stronger) password, confident that the password manager will remember all the rest.

“Putting all your passwords in one basket” is a concern of course but I think the pros outweigh the cons.

Why a YubiKey?

To take digital security to the next level you should use two-factor authentication (2FA). 2FA is an extra “thing” in addition to your password you need to be able to login. It could be a code sent to your phone over SMS (insecure), to your email (slightly better), a code from a 2FA app on your phone such as Aegis Authenticator (good), or from a hardware token (most secure).

It’s easy to think that I went with a YubiKey because it’s the most secure option; but the biggest reason is that a YubiKey is more convenient than a 2FA app.

With a 2FA app you have to whip out your phone, open the 2FA app, locate the correct site, and then copy the TOTP code into the website (quickly, before the code changes). It’s honestly not that convenient, even for someone like me who’s used this setup for years.

With a YubiKey you plug it into a USB port and press it when it flashes. Or on the phone you can use NFC. NFC is slightly more annoying compared to plugging it in as you need to move/hold it in a specific spot, yet it’s still preferable to having to jump between apps on the phone.

There are hardware keys other than YubiKey of course. I’ve used YubiKey for years and have a good experience. Don’t fix what isn’t broken.

The setup

Here’s a few quick notes on how I setup her new accounts:

Password management with Bitwarden

The first thing we did was setup Bitwarden as the password manager for her. I chose the family plan so I can handle the billing.

To give her access I installed Bitwarden as:

  1. An app on her phone
  2. A browser plugin on her computer

I gave her a YubiKey and registered it with Bitwarden for additional security. As a backup I also registered my own YubiKeys on her account; if she loses her key we still have others she can use.

Although it was a bit confusing for her I think she appreciates not having to remember a dozen different passwords and can simply remember one (stronger) password. We can also share passwords easily via Bitwarden (for news papers, Spotify, etc).

The YubiKey itself is very user friendly and she hasn’t run into any usability issues.

Email on Fastmail

With the core security up and running the next step was to change her email:

  1. Gave her an email address on Fastmail with her own domain (<firstname>@<lastname>.com).

    She has a basic account that I manage (there’s a Duo plan that I couldn’t migrate to at this time).

  2. I secured the account with our YubiKeys and a generated password stored in Bitwarden.

  3. We bolstered the security of her old Hotmail account by generating a new password and registering our YubiKeys.

  4. Forward all email from her old Hotmail address to her new address.

With this done she has a secure email account with an email address that she owns.

As is proper she’s been changing her contact information and changing email address in her other services. It’s a slow process but I can’t be too critical—I still have a few services that use my old Gmail address even though I migrated to my own domain more than a decade ago.

Notes on recovery and redundancy

It’s great to worry about weak phishing, weak passwords, and getting hacked. But for most people the much bigger risk is to forget your password or lose your second factor auth, and get locked out that way.

To reduce the risk of losing access to her accounts we have:

  • Multiple YubiKeys for all accounts.
  • The recovery codes for all accounts are written down and secured.
  • My own accounts can recover her Bitwarden and Fastmail accounts via their built-in recovery functionality.

Perfect is the enemy of good

Some go further than we’ve done here, others do less, and I think that’s fine. It’s important to not compare yourself with others too much; even small security measures makes a big difference in practice.

Not doing anything at all because you feel overwhelmed is worse than doing something, even something simple as making sure you’re using a strong password for your email account.

First impressions of Ghostty

2025-01-06 08:00:00

There are two conflicting forces in play in setting up your computer environment:

  1. It’s worthwhile to pursue small improvements in frequently used tools.
  2. It’s not productive to get stuck tweaking your setup endlessly.

It’s common to find people get stuck at the extreme ends of the spectrum; some programmers refuse to configure or learn their tools at all, while others get stuck re-configuring their setups constantly without any productivity gains to show for it.

Finding a balance can be tricky. With regards to terminals I’ve been using alacritty for many years. It gets the job done but I don’t know if I’m missing out on anything? I’ve been meaning to look at alternatives like wezterm and kitty but I never got far enough to try them out.

On one hand it’s just a terminal, what difference could it make?
On the other hand, I spend countless of hours every day inside a terminal so even a small improvement should pay off in the long run.

Enter Ghostty, a terminal so hyped up it made me drop any useful things I was working on and see what the fuzz was about. I don’t quite get why people hype up a terminal of all things but here we are.

Ghostty didn’t revolutionize my setup or anything but I admit that Ghostty is quite nice and it has replaced alacritty as my terminal.

I just want a blank canvas without any decorations

One of the big selling points of Ghostty is its native platform integration. It’s supposed to integrate well with your window manager so it looks the same and gives you some extra functionality… But I don’t know why I should care—I just want a big square without decorations of any kind.

You’re supposed to to be able to simply turn off any window decorations:

window-decoration = false

At the moment there’s a bug that requires you set some weird GTK settings to fully remove the borders:

gtk-titlebar = false
gtk-adwaita = false

It’s unfortunate as I haven’t done any GKT configuration on my machine (I use XMonad as my window manager and I don’t have any window decorations anywhere).

Cursor invert

cursor-invert-fg-bg = true

In alacritty I’ve had the cursor invert the background and foreground and you can do that in Ghostty too.

I ran into an issue where it interferes with indent-blankline.nvim making the cursor very hard to spot in indents (taking the color of the indent guides, which is by design low contrast with the background).

Annoying but it gave me the shove I needed to try out different plugins to see if the problem persisted. I ended up with (an even nicer) setup using snacks.nvim that doesn’t hide the cursor:

Left: indent-blankline.nvim (cursor barely visible)
Right: snacks.nvim (cursor visible and it highlights scope).

Minimum contrast

Unreadable ls output is a staple of the excellent Linux UX. It might look like this:

Super annoying.

You can of course configure the ls output colors but that’s just for one program and it won’t automatically follow when you ssh to another server.

Ghostty’s minimum-contrast option ensures that the text and background always has enough contrast to be visible:

minimum-contrast = 1.05

Most excellent.

Hide cursor while typing

mouse-hide-while-typing = true

A small quality-of-life feature is the ability to hide the cursor when typing. I didn’t know I needed this in my life.

Consistent font sizing between desktop and laptop

With alacritty I have an annoying problem where I need to use a very different font size on my laptop and my desktop (8 and 12). This wasn’t always the case and I think something may have changed in alacritty but I’m not sure.

Ghostty doesn’t have this problem and I can now use the same font settings across my machines ( font-size = 16 ).

Ligature support

The issue for adding ligatures to alacritty was closed eight years ago and even though I wanted to try ligatures I couldn’t be bothered to “run a low quality fork”.

Ghostty seems like the opposite of “low quality” and it renders Iosevka’s ligatures very well:

My configured ligatures of Iosevka, rendered in Ghostty.

Overall I feel that the font rendering in Ghostty is a little better than in alacritty, although that might be recency bias. I’m still undecided on ligatures but I love that I don’t have to feel limited by the terminal.

I use a custom Iosevka build with these Ghostty settings:

font-family = IosevkaTreeLig Nerd Font
font-style = Medium
font-style-bold = Bold
font-style-italic = Medium Italic
font-style-bold-italic = Bold Italic
font-size = 16

Colorscheme

While Ghostty has an absolutely excellent theme selector with a bunch of included themes (ghostty +list-themes) melange-nvim wasn’t included, so I had to configure the colorscheme myself. It was fairly straightforward even though the palette = 0= syntax was a bit surprising:

# The dark variant of melange
background = #292522
foreground = #ECE1D7
palette = 0=#867462
palette = 1=#D47766
palette = 2=#85B695
palette = 3=#EBC06D
palette = 4=#A3A9CE
palette = 5=#CF9BC2
palette = 6=#89B3B6
palette = 7=#ECE1D7
palette = 8=#34302C
palette = 9=#BD8183
palette = 10=#78997A
palette = 11=#E49B5D
palette = 12=#7F91B2
palette = 13=#B380B0
palette = 14=#7B9695
palette = 15=#C1A78E
# I think it's nice to colorize the selection too
selection-background = #403a36
selection-foreground = #c1a78e

I’m happy with Ghostty

In the end Ghostty has improved my setup and I’m happy I took time to try it out. It took a little more time than “just launch it” but it absolutely wasn’t a big deal. The reward was a few pleasant improvements that have improved my life a little.

And perhaps most important of all: I’m now an alpha Nerd that uses a terminal written in Zig.

2024 in review

2025-01-05 08:00:00

It’s time for my 15th yearly review.

Nerdy things I enjoyed

  • I read a lot of fantasy books this year!

    My favorite new series were The Kingkiller Chronicle, Gentlemen Bastards series, and The Stormlight Archive.

  • Customizing Neovim was fun and rewarding.

    It’s amazing I got anything productive done this year…

  • I really enjoy working with Rust in my own hobby projects.

  • Types are coming to Elixir and I’m loving it.

    (I recently migrated some small projects to v1.18 and found a bunch of errors.)

  • The Gleam programming language shows a lot of promise.

    My one gripe is the pain of manually encoding/decoding JSON (even with the various libraries). Compared to for example Elixir dynamic encoding or Rust’s #[derive(Deserialize)] it just feels so bad that I’ve avoided Gleam for some projects. Shame on me?

  • CSS is alive and better than ever.

Things I accomplished

Tentative plans/goals/wishes for 2025

  • For some reason the idea of writing a fantasy novel got stuck in my head.

    I had a stint where I listened to dozens of hours of advice for aspiring writers and started planning a series. The excitement tapered off a bit during the Christmas holidays and I don’t know if this was just a temporary sidetrack or if it’s something I’ll actually end up doing.

  • Design a one-handed keyboard layout.

    Again, this was just something my brain got stuck thinking about and I’m not sure if it’s just a fleeting idea or something I need to do so I can stop thinking about it. (Sometimes just a little planning plus solving the most difficult problems are enough—I don’t have to finish all the crazy/dumb ideas I get for my mind to consider them “done”.)

  • Complete my second 3D printer.

    There’s no point in having a single printer; what if I break it and I can no longer print replacement parts for it?

  • Develop my home automation system more.

    I’ve got a ton of things I’d like to improve (or play around with). For instance, yesterday I received the Home Assistant Voice Preview Edition that I hope works as well as advertised.

A type checking error in Elixir 1.18

2024-12-30 08:00:00

Although I’m a big Elixir fan, the lack of static typing has always been my biggest annoyance (and why I think Gleam is so cool). I think static typing helps catch bugs earlier and in an automated way, leading to less buggy software and saves time in the long run.

To my great joy Elixir is working on a new type system that will hopefully give us the early type checking errors I’ve been craving for. The system has been rolled out in steps since v1.17 and when I migrated to v1.18 I found my first type checking warning that I wanted to highlight.

Comparison with structs

This is the offending code with the corresponding warning:

def get_surrounding_events_as_dt(events, now = %DateTime{}) do
time = DateTime.to_time(now)
next_i = Enum.find_index(events, fn {_, event_time} -> time < event_time end) || 0
warning: comparison with structs found:
time
given types:
dynamic(%Time{})
where "event_time" was given the type:
# type: dynamic()
# from: lib/haex/sun.ex
{_, event_time}
where "time" was given the type:
# type: dynamic(%Time{})
# from: lib/haex/sun.ex:88:10
time = DateTime.to_time(now)
Comparison operators (>, =,
Comparing with a struct won't give meaningful results.
Structs that can be compared typically define a compare/2 function within their modules that
can be used for semantic comparison.
typing violation found at:
│ next_i = Enum.find_index(events, fn {_, event_time} -> time
│ ~

(The type checker cannot yet resolve event_time to the Time struct, leaving it as dynamic() in the text above.)

The issue here as that < isn’t overloaded for the Time struct (like it would be in for instance Rust) and will instead perform structural comparison.

You should use Time.before? instead of < (and DateTime.before for DateTime etc).

My hope for the future

I’ve always disliked matching against atoms in Elixir as it’s so easy to make a mistake, for example like this:

case Supervisor.start_child(supervisor, child_spec) do
{:error, {:already_stated, pid}} ->
Logger.info("Got pid: #{inspect(pid)}")

(There’s a missing r in :already_stated.)

At the moment this doesn’t produce an error but I really hope we’ll reach this point sooner rather than later as I make these kinds of mistakes all the time. I think I catch most of these with tests but I’m sure some slip through.

I hope this isn’t that far away as the v1.18 type checker manages to catch a simpler case like this:

def num_to_descr(num) do
case num do
1 -> :one
2 -> :two
_ -> :many
end
end
def print(num) do
case num_to_descr(num) do
:zero -> IO.puts("zero")
x -> IO.puts("Other: #{x}")
end
end
warning: the following clause will never match:
:zero
because it attempts to match on the result of:
num_to_descr(num)
which has type:
dynamic(:many or :one or :two)
typing violation found at:
41 │ :zero -> IO.puts("zero")
│ ~~~~~~~~~~~~~~~~~~~~~~~~

The current Cybershard layout

2024-11-26 08:00:00

This is the keyboard layout I’m using for my custom keyboard that I generated, printed, and hand-wired. It’s a minimalistic keyboard of 35 keys and features an integrated trackball on the right-hand side.

The keyboard layout started out as a direct copy of the T-34 keyboard layout, with some small modifications from the 34-key keyboard T-34 was designed for:

  • An extra two keys on the left-hand side, in the ring and middle finger columns.
  • Removed the second thumb key on the right-hand side.
  • Accommodation for mouse keys and trackball handling.

While the layout has diverged since then, the design philosophy from original T-34 post still holds true and I recommend it as it may explain why the layout looks like it does.

Legend

I use quite a number of special features for the keys and I’ve tried to color code according to the above legend.

Layers

Layers are super important for smaller keyboards and I use them a ton.

Base

  • The repeat key is used to output the last pressed key.
  • I shift keys using auto shift (see long press) and CAPSWORD.
  • The letters Z and Q, together with a bunch of other keys, are on combos.
  • The keys for F2, F12 and FUN are just extras and aren’t in a comfortable enough position to warrant anything more common.

Swedish overlay

Swedish overlay.

When I want to write Swedish I activate this layer that replaces ()_ with åäö, or I use combos from any layer.

Symbols

Symbols layer.

I typically use combos to output symbols (following the same layout pattern as the symbols layer). The symbols layer is mostly used to roll symbol pairs like {} or #[.

Some common symbol sequences (like ->, != , or ```) exists as combos and others as long press.

Numbers

Numbers layer. The browner keys (like J) turn off NUMWORD.

While I can activate the number layer persistently (using leader sequences) I typically use combos for single digits (like 0), or NUMWORD for larger numbers (like 1984).

NUMWORD makes the number layer smart, so it will deactivate when certain keys are pressed. It’s used to type numbers in text or code and for relative movement in Neovim, where 17J would move 17 lines down and then turn off the number layer. Jumping directly to a line in Neovim with 12G is also made convenient.

If I want to enter the layer without it turning off I can either use leader sequences to activate it persistently or hold the NUMWORD combo (hold both thumbs). The layer won’t release until both thumb keys are released, so Space can be tapped with the left thumb without leaving the number layer.

@u is there to easily activate macros in Neovim. For example 7@u in the number layer would run the u macro 7 times and then turn off NUMWORD.

Function keys

Function keys.
  • The function keys follow the same layout as the numbers layer.
  • The trackball DPI can be lowered and raised at runtime.

Navigation layers

Navigation layer.
  • Keys with Gui are used in xmonad.

    • Gui + W, Gui + E and Gui + R are used to switch between monitor.
    • Gui + J/Gui + K to switch windows.
    • Gui + ,/Gui + . to add/remove windows from the “master” area.
  • This layer is commonly used in conjunction with the trackball, giving access to mouse keys.

    For instance, Shift + Left Mouse can be used to drag, Ctrl + A to select, and Ctrl + C to copy.

  • Tabbing is for switching browser tabs and the Back / Fwd mouse buttons goes backwards and forwards in history.

  • Ctrl + arrow is used to switch windows in Neovim.

  • Ctrl + D and Ctrl + U are used in Neovim to scroll half a screen (compared to PgUp/PgDn that scrolls an entire screen).

  • The workspace layer is triggered by first holding Space, and then holding the right thumb key (WNAV).

Arrows on the left-hand side.

This layer exists for the rare occasions I want to navigate using only the left hand. The keys on the right side is there just because and are rarely, if ever, used.

Workspace layer

Workspace layer. All keys have an implicit Gui modifier.

This is used for all window and workspace management in xmonad. Some common operations are also on the navigation layer. Auto shift works and can used to send a window to another workspace (Gui + Shift + 2).

Windows layer

Windows layer. All keys have an implicit Alt modifier.

This is purely to enable window switching using Alt-Tab and Ctrl-Alt-Tab, without releasing Alt.

Mouse layer

The mouse layer for right-hand only usage.

I generally prefer to use the navigation layer, where the mouse buttons are on the left side, but in some cases I’d like to only use my right hand.

Special characters

Specials layer.

The dead keys add diacritic to any letter. For example, to get é you can use the dead key ´ then e, and the operating system will merge them together. (É also exists as a combo.)

Modifiers

Modifiers.

I typically use long press for shift and combos for other modifiers, this layer is a fallback for when those aren’t enough (the layer is mostly used for Right Alt).

Gaming layer

A simple gaming layer.

I’m not a heavy gamer by any means but this layer allows me to play Core Keeper with my kid. By necessity this layer disables auto shift.

Combos

Combos is another fantastic tool that I (ab)use a lot. Simply put it allows you to press multiple keys at once and acts as another key—very useful for smaller layouts.

Neighbour combos

These combos are made by keys next to each other, either horizontally (pressed with two fingers) or vertically (pressed with one finger in the middle of two keys).

2-key neighboring combos.
  • Some combos have a separate hold behaviour; for instance holding Escape activates the symbols layer, allowing me to output [] easily.
  • vsplit splits a window vertically in Neovim and hsplit splits it horizontally, and Close Window closes a window in Neovim (<C-w>q).
  • Clear resets all states; sets the base layer, releases modifiers, stops CAPSWORD and NUMWORD, and clears other persistent states.
  • Ctrl + Shift + M is the shortcut to mute/unmute in Teams.
3-key horizontal combos.
  • SWE activates the Swedish layer, and if prefixed with ()_ it will replace that with åäö and vice versa. So for example if I typed hall( I would press SWE to get hallå, with the Swedish layer activated.
  • Ctrl W is used to close tabs in Firefox.
A 4-key horizontal combo.
  • Save Neovim is a 4-key combo that saves the buffer in Neovim.

Split combos

These split combos uses the ring and index finger.

Combos for the quotes " and '.
Combos don’t have to be on the same row, these angled combos are fairly comfortable.
One-shot Alt on the left combo and holding the right combo activates the windows layer (with implicit Alt).
Leader key on the bottom row.

Combos over keyboard halfs

  • Tapping T + A once activates CAPSWORD, tapping again makes it persistent (CAPS LOCK), and a third tap to deactivate CAPS LOCK.
  • Tapping Space + E activates NUMWORD and tapping them again activates the number layer persistently.

The repeat key works with the above, making them easier to double-tap.

  • Swaps _ and -.
  • Access the specials layer with the ring fingers.

Thumb key combos

I have a bunch of 2-key thumb + key combos:

Combos using Space and another key.
Combos using E and another key.

The logic here is that same-side thumb + key = symbol and opposite-side thumb + key = digit, following the placements of the numbers, symbols and swedish layers. They’re used if I want to just type a single character, without having to activate a layer first.

Combos using FUN and another key.

I have similar combos for the function keys.

QMK boot

The keycode QMK_BOOT enters boot mode for the microcontroller connected via USB, making it easy to update the keymap on the keyboard. These two 5-key combos (one for each half) are almost impossible to trigger accidentally while being easily accessible.

Additional features

While layers and combos are the two main features I use, QMK has a lot of other nifty features (and you roll your own implementation of them too).

Long press

Most keys have a different behaviour when tapped compared to a long press. Most commonly I use this to produce shifted keys (called auto shift). So tapping the A key will output a as normal and if it it A will appear instead.

There are a bunch of special cases as well (many on top of combos):

Tap Long press
_ < > / \ # Double, e.g __
" ' = ` 0 . Triple, e.g """
| & = Double with spaces, e.g  || 
!  !=  (with spaces)
? {:?}
# {:#?}
% %{}
( [ { Close and move cursor between
@ @u (paired with qu combo for Neovim macro execution)

Leader sequences

I use the combo l + ) as the leader key. This will wait for a sequence of key presses (in contrast to combos where keys must be pressed at the same time). I use this with mnemonics for rarely used outputs:

Leader sequence Action
l + ), c Caps lock
l + ), s Swedish input in Linux (mapped in xmonad)
l + ), t, n Toggle Number layer
l + ), t, s Toggle Symbols layer
l + ), t, f Toggle Function layer
l + ), t, c Toggle Caps lock escape swap
l + ), Esc Ctrl Shift Escape

CAPSWORD

CAPSWORD is a “smart caps lock”. It works like a regular caps lock, except it automatically turns off after certain keys are typed (most commonly space).

It will not turn off on letters, numbers, _ - Backspace and the Repeat keys.

NUMWORD

NUMWORD is a “smart layer”. It’s similar to CAPSWORD, except it activates and then turns off the numbers layer instead of caps lock.

It will not turn off on these keys: 0-9 % / + * - _ . , : = x Backspace Enter and the Repeat keys.

Repeat key

The repeat key simply repeats the previous key. So to type fall I can type f a l Repeat, using four different fingers instead of pressing l twice. It can also repeat things like Ctrl-c or Delete, and unlike regular keys that use auto shift the Repeat key can be held.

Trackball

The trackball is normally configured to move the mouse as a regular trackball.

There are different modes that alters the behavior of the trackball:

  1. Lower the DPI when Space or _ are held (the mouse moves slower when the navigation layer or the mouse layer are active).
  2. Raise the DPI when the MOD combo is held (the mouse moves faster).
  3. Scroll instead of moving the mouse when the SYM combo is held.

More info

  • Read the T-34 series for the design process and motivations of my other keyboard layout (it’s the same layout with minor refinements and additions).

  • See the post Building my ultimate keyboard for how I designed and built the keyboard I’m using this layout with.

  • For implementation details and the most up-to-date reference check out the layout’s QMK source code.


  • Copied the T-34 layout and adapted it for the new keyboard by adding a mouse layer, remove the shortcut layer, and changed the activation of the specials layer.

  • Moved - to an angled combo, moving the WIN key to the top row, and move % to the home-row and ! to the bottom row.

  • Reworked the mouse layer and use a more advanced triggering mechanism to be more explicit about when the layer is turned on and off.

  • Added double-tap functionality to NUMWORD and CAPSWORD combos.

    1. Moved - back to it’s original position and placed % on the angled combo.
    2. Experimental functionality to swap - and _ for the languages that use kebab-case.
    3. Removed the mouse layer and placed mouse buttons on the navigation layer.
  • Reworked the navigation layer to keep the original positions for PgUp, PgDn, and Tabs. To allow this I moved the mouse click to index finger and demoted the up/down to the top row.

    1. Reworked the navigation layer to be more focused on two hands on the board. The idea is to use better placed keys on the right-hand side instead of cramming in too much on the left. Moved PgUp and Home to the right side and it’s enough to use the arrow keys on the right. (The secondary navigation layer exists to enable arrow keys on the left side.)

    2. Added mouse button combos on the right-hand side so in a pinch I can operate the mouse with one hand.

    1. Removed mouse button combos from the right-side and place them on a separate layer instead (to keep the shift combo in the regular place).

    2. Add Gui + ,/. for xmonad master window management to the navigation layer, moving Ctrl + A to the home-row once again.

  • Added the gaming layer.

Building my ultimate keyboard

2024-11-26 08:00:00

The Cybershard keyboard.

What comes to mind when you see the description “the ultimate keyboard”?

There are many keyboards in this world; here are some that might fit the “ultimate” moniker:

Some even have “ultimate” in their name, although I’ll assert that they’re far from ultimate.

Any man who must say, “I am the King”, is no true king.

Tywin Lannister

I’ll go one step further to say that no keyboard is universally the ultimate because it’s impossible to agree on how to rank different keyboards. For example, while I personally prefer a split keyboard, you might not. Some people have very long fingers and some have very short fingers, making some layouts more preferable. Others may not even have 10 fingers (or both hands), requiring more drastic modifications.

If an ultimate keyboard exists, it differs from person to person. This is my attempt to build my ultimate keyboard.

My wishlist

To me, the ultimate keyboard should have these features:

  1. Should be split to support a more natural typing position.

    Really the biggest ergonomical leap in my opinion.

  2. Customized for my own fingers and typing eccentricities.

    Column stagger, curvatures and tenting are features I think I want but they need to be tuned, probably by trial-and-error. The position of the thumb keys is another sticking point that the other keyboards I’ve tried have failed to get just right.

  3. Have an integrated trackball or trackpad.

    This way I don’t have to move my hand so far and I can free up some valuable desk space. It shouldn’t be operated with my thumb due to my RSI.

  4. Contain the keys I need but no more.

    I like smaller keyboards and I’ve been very happy and with my custom keyboard layout that only has 34 keys. Some modifications are fine of course but for the most part I want to be able to use the same layout on both the Ferris and my new keyboard.

  1. To fulfill these requirements I need to be able to customize all parts of the keyboard and I really don’t want to learn CAD and create one from scratch; I wonder what alternatives I have?

Cosmos keyboard configurator

Having looked around, I probably want something similar to a Dactyl / Dactyl Manuform (many variants exists). They’re keyboards you generate from parameters (such as number of rows and columns and the amount of curvature). I’ve always wanted to try one and now with a 3D printer, I can.

When looking for a generator I stumbled upon the Cosmos keyboard configurator and I want to gush about it a little because it’s excellent.

A relatively standard Dactyl Manuform with an encoder and trackpad.

It’s excellent because it allows a clueless sod like me to configure a keyboard the way I want to and it has an impressive feature list:

  1. Easily generate keyboards of any size.
  2. Customize XY-spacing, row- and colomn curvature, and more.
  3. Several pre-made thumb clusters.
  4. UI to move around all the keys.
  5. Supports different switches (I so need my Choc switches).
  6. An Expert mode that allows you to customize anything via JavaScript.
  7. Supports encoders, trackpads, OLED displays, and trackballs.
  8. Can generate a wrist rest.
  9. Exports .stl for easy printing or .step you can import to CAD.

Here’s a small snippet from how the code in Expert mode might look like:

const curvature = {
curvatureOfColumn: 15,
curvatureOfRow: 5,
spacingOfRows: 18, // 18x19 Choc spacing
spacingOfColumns: 19,
arc: 0,
};
/**
* Useful for setting a different curvature
* for the pinky keys.
*/
const pinkyCurvature = {
...curvature,
curvatureOfColumn: 15,
};
/**
* The plane used to position the upper keys.
* It's rotated by the tenting and x rotation
* then translated by the z offset.
*/
const upperKeysPlane = new Trsf()
// `20` specifies the tenting angle.
.rotate(20, [0, 0, 0], [0, 1, 0], false)
.rotate(1, [0, 0, 0], [1, 0, 0], false)
.translate(0, 0, 0, false);

The entire state of the keyboard is also stored in the url, so I can easily share my config by including a link: Cosmos reference of the final keyboard configuration. (Barring any breaking changes in the tool of course…)

Initial design parameters

Even with a keyboard configurator I needed a way to start. I already have a layout that I really like so I wasn’t starting from nothing. These were the important parts going into the design process:

  1. A 3x5 grid with 1-2 thumb keys (in practice one thumb key is enough).

    If you question why I want to build such a small keyboard I’ll redirect you to the discussion in The T-34 keyboard layout post.

  2. Integrated trackball on the right-hand side.

  3. Choc switches.

    One of the major decisions with a keyboard is what kind of switch to use. While MX-style switches are the most common I personally really love Choc switches for a couple of reasons:

    • Low-profile
    • Low actuation force
    • Can be closer together

    While a low profile switch is more important for a flat keyboard, not a tented and curved one like I’m building now, the flatter keycaps and the switches being closer together is crucial for pressing two keys with one finger:

    A horizontal combo is pressed with the finger in the middle of the keys. It’s surprisingly comfortable.

    The low-actuation force is also more comfortable to me as it helps reduce the strain on my fingers, and makes combos (pressing several switches at once) generally more pleasant.

Hardware and material

It’s not enough with just a 3D printer, to build a working keyboard you need a bunch of hardware:

  1. Two microcontrollers.

    I got the Liatris microcontroller as it has enough pins to connect a trackball sensor and it supports QMK.

  2. Switches

    What kind of Choc switch should I use?
    Linear, tactile, or clicky?
    Exactly how heavy should they be?
    Should they be silent?

    I wasn’t sure so I ordered a sampling of different switches to try.

    A collection of different Choc switches.

    For the final keyboard I used the Ambients silent Noctural (linear / 20gf) switches, where the deciding factor was getting as light switches as possible. (I’ve previously used modded 15gf switches, which were even better, but I couldn’t find a way to buy them.)

  3. Keycaps

    It’s hard to decide on a colorscheme so I bought a bunch of random colors.

    Keycaps aren’t only for looking cool. A convex keycap for the thumb button instead of the standard concave one makes it much more comfortable:

    The blue convex keycap to the left and the red concave to the right.

    I also got keycaps for the index row with these small homing notches to help my fingers more easily find the home row.

  4. A pair of TRRS connectors and a TRRS cable.

  5. A Trackball with a matching sensor.

    I decided to pick up this PMW3389 sensor because it was recommended in the keyboard configurator and a red 34mm trackball from Amazon.

  6. Filament for the 3D printed pieces.

    I ended up settling on the PolyTerra PLA Army Purple for the case but I used a bunch of different filament during the prototype phase.

  7. Diodes, screws, heatset inserts, and cable to do the wiring.

Prototypes

Some discarded prototypes.

When you’re trying to design something like a custom keyboard I think you need to go through a bunch of trial-and-error until you find something that fits.

Here’s a short rundown of some of the significant revisions I went through, mostly to illustrate that it’s very much an iterative process.

First print

For my first print I mostly wanted to print it out and test how a keyboard with a standard curvature felt. I also wanted to try to place a trackball somewhere.

I ended up removing a regular thumb key (I’ve used two thumb keys with my keyboard layout) to make it fit and I added a “mouse thumb key” that I plan to use as a left mouse button when I’m operating the trackball. It was tricky to place the trackball as I wanted to operate it with my index + middle finger, not my thumb.

Another tweak I made was to reduce the spacing between the keys to be closer to the Choc spacing. Choc spacing seems to be 18.6 x 17.6 mm, but I used 19 x 18 mm spacing—the attraction to round numbers is real.

This is the very first prototype I printed. Please ignore the bad print quality; the filament was wet and the temperature was too high. It’s just a prototype so it really doesn’t matter.

Pressing the top right key with the ring finger

Most of the keys on the keyboard felt fine but I had one major annoyance: I have a habit of using the ring finger to press the top right key instead of the pinky but with the curvature on the keyboard this just wasn’t possible anymore.

You might wonder, why don’t I just create a new habit and use the pinky as you’re supposed to? The simple answer is that I hate it. To my fingers that feels beyond terrible and I’d rather remove the key and only have two keys in the outermost column. As it happens, pressing the key with my ring finger (on a flat keyboard) feels good so I’d rather adjust the key than remove it.

The second printed prototype, with some keys installed for testing. The printer clogged before the print was finished but it did its job as a prototype.
Here’s an angle from the configurator showing the offset of the p key compared the other keys in the column.

I also added an extra mouse thumb key and lowered the pinky column a bit.

Adjust mouse keys and increase tenting

It’s starting to look like an actual keyboard. With color coded keycaps; red for mouse-only buttons and violet for the home-row keys.

Pressing p with my ring finger feels great. Pressing the thumb normal thumb key feels awful because the mouse thumb keys are in the way when I relax my hand.

Adjustments made:

  • Moved mouse thumb keys to be more vertical and pressed from the side.
  • Added an extra pinky key reachable when I’m using the trackball.
  • Increased tenting to 20 degrees from 10 degrees.
20 degrees of tenting.

Rounded base and pinky tweak

Yet another prototype.
  • I tried the “rounded” sides and top feature of Cosmos.
  • The mouse pinky key was too low, I raised it up a bunch.

Thumb keys adjustments

  • Rotated the main thumb key inwards
  • Added an area for a display
  • Lowered the mouse thumb keys a little
  • Removed the “rounded” features

More tweaks and the left half

  • Configure the left half of the keyboard
  • Move pinky keys a little upwards
  • Move all thumb keys a little further away
  • Removed the display (felt like too much of a hassle for a little coolness)

Although I said I wanted to have a 3x5 grid, the generator included an easy option to include a small bottom row with 2 extra keys (for the ring and middle finger) that I wanted to try out for the left side. They’re… Okay I guess. Not crazy uncomfortable but not quite comfortable enough that I want to have common keys there.

Beta V3

At this point the Beta V3 of configurator is out and in it there’s several improvements, most notably:

  • Both halves can be configured at the same time.

  • Can go between the Advanced and Expert tabs! WOW!

    I had to manually keep track of the JavaScript changes I made, and update them manually if I wanted to make a change in the UI… But no more!

I had to redo most of the configuration and I think I made some minor changes that I didn’t keep track of, but I made two larger ones:

  • Lowered the tenting angle to 15 degrees (from 20)
  • Lowered ring pinky column key a little

Small tweaks to pinky and thumb keys

  • Raise/tilt top pinky row key
  • Move thumb keys on left side closer together

Trackball mounting types

Roller bearings for the trackball.

When I started this project Cosmos only supported a single type of trackball mount: roller bearings. They worked quite poorly for me as the ball was spinning well in one direction but poorly in others.

Luckily new options were added and as I’m writing this there’s 4 different ways you can mount the trackball:

  1. Roller bearings (the old option)
  2. BTU (7.5mm or 9mm)
  3. Static ball bearings (3.175mm)

Because I was burned with the bad experience (and I didn’t want to rebuild the keyboard yet again) I made small prototypes of the three different options:

The new trackball mounts; BTU 9mm, BTU 7.5mm, and static ball bearings.

The BTUs had the least friction and it felt really easy to spin the ball but they were also distressingly loud. The static ball bearings had more friction than the BTUs and less than the roller bearings while being completely silent, so I chose to go with the ball bearings.

The ball bearings installed on the keyboard, with the trackball sensor peeking through.

While they don’t feel nearly as good as the Kensington SlimBlade they’re decent enough. I try not to use the mouse that much and having the trackball so much closer is worth it compared to having a separate trackball unit besides the keyboard.

Remove mouse keys

After having used the keyboard for real I realized that the three keys dedicated to mouse buttons would have to go. There were two major issues with them:

  • The pinky key got in the way sometimes when I reached for the top column, and I couldn’t retrain myself to avoid it.
  • One of the goals with the keyboard layout I use is to reduce the thumb and pinky usage of my right hand. The mouse keys counteract this goal.

So I had them removed and I rewired the right half for the 3rd time. Sigh.

I think the lesson is that it’s not enough to print a prototype and press switches pretending to type, you have to build and use the keyboard a bunch before you can evaluate some of the design decisions.

Cosmos reference

Additional printed parts

While the case is the biggest and most important part of this kind of keyboard, there are a few other parts I had to print to complete the keyboard.

Wrist rests

Some of the different wrist rests I tried.

Magnet attachments

The wrist rests didn’t come with any sort of attachment to the case, so they just always drifted away. I tried to combat this by gluing magnets inside the case and outside the wrist rest, making them stick together just enough to stay together during normal use, while being easily removable.

I ended up not using the wrist rests

Despite my efforts, I haven’t been using the printed rests as I reverted to the ”squishy” ones I’ve used before:

It’s not visible from this angle but my hand rests using the palm instead of the wrist.

The printed felt too uncomfortable and I couldn’t find an angle I liked more than the gel rests. Oh well.

Microcontroller holder

The microcontroller is held in place by a holder you screw into the case.

There’s a holder to fasten the microcontroller to the case that I use. I had to manually make a hole to make the Boot button accessible, which was easily accomplished when slicing the model.

Bottom plate

A flexible bottom plate hides the wiring.

One problem with the Ferris was that it would sometimes slip on the table. I counteracted this by using an old Netrunner playmat but I wanted another solution.

The keyboard is generated with a bottom plate that’s used to hide and protect the internals. I printed it in TPU, a flexible and rubbery material, that gives enough grip to stay relatively still when I’m typing.

Wiring

The complete wiring of the right-side keyboard.

Matrix

One of the first things you need to do when wiring up a custom keyboard is to plan out a matrix. I guess you could directly wire every switch directly to the controller too, but that’s not feasible if you have a larger amount of keys, so the usual thing is to use a matrix.

What a matrix means is you should wire together all keys in a row and connect that to a pin on the controller, and to the same with the columns.

It might look something like this:

The green lines indicate columns and the purple lines indicates rows.

You should also use diodes in the matrix (for either rows or columns, I chose the rows). Pay attention to the diode direction.

The rows are connected with diodes and with a cable to a pin on the controller.
The first matrix I soldered on the right side. It was a lot more difficult to solder because of the awkward positions of the switches.
The 3rd matrix I soldered for the right side, this time without the challenging mouse keys. Note that I wired it up slightly differently than in the wiring plan above because it felt more straightforward. Exactly how you choose to construct the matrix doesn’t really matter, as long as every key has a unique row/column combination.
Both the rows and columns are wired up and soldered to the controller.

The wiring is horrible, I know.

I only lost one microcontroller due to a short… With my wiring prowess I consider that a success!

Controller wiring

The right-side controller connected to the TRRS and the trackball sensor. This was after I had removed them from my second wired right half and was preparing to rebuild it.
The pins of the Liatris microcontroller. It’s an RP2040-based controller.
Controller pin Connection
1 Handedness (VCC on the left keyboard and GND on the right)
2 TRRS data
3, 4, 5, 6, 7 Matrix columns
20, 22, 26, 27 Matrix rows
13 (CS1) Trackball SS
14 (SCK1) Trackball SCK
15 (TX1) Trackball MOSI
16 (RX1) Trackball MISO
The TRRS connector is connected to Vcc, GND, and pin 2 (and similarly on the right side). Pin 1 is also connected to Vcc to signal to QMK that this is the left side of the keyboard, whereas pin 1 is connected to GND on the right controller.
The pmw3389 sensor attached beneath the trackball.

Adding the keyboard to QMK

The QMK cli has the qmk new-keyboard command that helps you get started. I couldn’t get the generated template to work for me, so I copied settings from an existing keybord with rp2042 support.

I’ll try to hit on the most important parts of the config, take a look at the source code for all details.

Basic setup

The folder structure for the keyboard looks like this:

cybershard
├── keyboard.json
├── rules.mk
├── halconf.h
├── mcuconf.h
└── keymaps
   └── default
   ├── config.h
   ├── keymap.c
   ├── rules.mk
   └── ...

(Cybershard is the name I eventually settled on for the keyboard.)

The most important part is keyboard.json that defines (almost) everything we need for a new keyboard in QMK.

First you need to set the processor, bootloader, and usb values. The Liatris microcontroller uses the RP2040 MCU, and I just picked some vendor- and product identifiers:

{
"keyboard_name": "cybershard",
"processor": "RP2040",
"bootloader": "rp2040",
"usb": {
"device_version": "0.0.1",
"pid": "0x0002",
"vid": "0x0361"
},
}

Then we need to define the matrix (with the pins we soldered) and the layout (how we’ll configure the keymap in keymap.c):

{
"diode_direction": "COL2ROW",
"matrix_pins": {
// We need to use a `GP` prefix for the pins.
"rows": ["GP26", "GP27", "GP22", "GP20"],
"cols": ["GP3", "GP4", "GP5", "GP6", "GP7"]
},
"layouts": {
"LAYOUT": {
"layout": [
// First physical row
{ "matrix": [1, 0], "x": 0, "y": 0 },
{ "matrix": [0, 1], "x": 0, "y": 0 },
{ "matrix": [0, 2], "x": 0, "y": 0 },
{ "matrix": [0, 3], "x": 0, "y": 0 },
{ "matrix": [0, 4], "x": 0, "y": 0 },
// Second row
{ "matrix": [2, 0], "x": 0, "y": 0 },
{ "matrix": [1, 1], "x": 0, "y": 0 },
{ "matrix": [1, 2], "x": 0, "y": 0 },
{ "matrix": [1, 3], "x": 0, "y": 0 },
{ "matrix": [1, 4], "x": 0, "y": 0 },
// etc...
]
}
}
}

Note that we can pick whatever physical pins we want as we can move around and configure them in software. The LAYOUT macro is what we use in keymap.c to define our keymap. When defining it we can choose to skip certain keys and reorganize it to be easier to define; for example, there’s no switch at 0,0 in my keyboard so I skip that.

The above LAYOUT can then be used like this:

LAYOUT(
SE_J, SE_C, SE_Y, SE_F, SE_P,
SE_R, SE_S, SE_T, SE_H, SE_K,
SE_COMM, SE_V, SE_G, SE_D, SE_B,
SE_A, SE_B,
// Thumb keys
FUN_CLR, MT_SPC,
),

Flashing

With the above setup we should be able to flash the keyboard by first entering the boot loader and running:

qmk flash -kb cybershard -km default

Now the process of updating the firmware is quite nice and unless I screw up I don’t need to connect another keyboard to do it.

  1. Start flashing with qmk flash (it will wait until it finds a flashable target).
  2. Press the QK_BOOT combo (the keyboard becomes unresponsive).
  3. Wait until the script finishes and the keyboard is available again.

Split keyboard

To get the split keyboard feature to work I had to set the SERIAL_DRIVER option in rules.mk:

SERIAL_DRIVER = vendor

And add the split configuration to keyboard.json and modify the LAYOUT macro:

{
"split": {
"enabled": true,
// The pin that signals if the current controller is the left (high)
// or right (low) controller.
"handedness": {
"pin": "GP1"
},
// The TRRS data pin.
"soft_serial_pin": "GP2",
"matrix_pins": {
"right": {
// We can override the pins for the right controller.
// Note that GP26 and GP27 are swapped compared to the left side
// due to a mistake I made when soldering.
"rows": ["GP27", "GP26", "GP22", "GP20"],
"cols": ["GP3", "GP4", "GP5", "GP6", "GP7"]
}
},
"transport": {
"sync": {
// We need to sync the matrix state to allow combos, mods, and
// other stuff to work.
"matrix_state": true
}
}
},
"layouts": {
"LAYOUT": {
// The rows 0 to 3 specifies rows on the left side and
// 4 to 7 the rows on the right side.
"layout": [
// These 5 keys are the first row on the left side.
{ "matrix": [1, 0], "x": 0, "y": 0 },
{ "matrix": [0, 1], "x": 0, "y": 0 },
{ "matrix": [0, 2], "x": 0, "y": 0 },
{ "matrix": [0, 3], "x": 0, "y": 0 },
{ "matrix": [0, 4], "x": 0, "y": 0 },
// These 5 keys are the first row on the right side.
{ "matrix": [4, 0], "x": 0, "y": 0 },
{ "matrix": [4, 1], "x": 0, "y": 0 },
{ "matrix": [4, 2], "x": 0, "y": 0 },
{ "matrix": [4, 3], "x": 0, "y": 0 },
{ "matrix": [4, 4], "x": 0, "y": 0 },
// etc..
]
}
}
}

The LAYOUT macro is just a function with many arguments but with the right order it can be formatted to look similar to the physical keyboard. For example, this is how the base layer of my keyboard could look like:

LAYOUT(
// Left side // Right side
SE_J, SE_C, SE_Y, SE_F, SE_P, SE_X, SE_W, SE_O, SE_U, SE_DOT,
SE_R, SE_S, SE_T, SE_H, SE_K, SE_M, SE_N, SE_A, SE_I, REPEAT,
SE_COMM, SE_V, SE_G, SE_D, SE_B, SE_SLSH, SE_L, SE_LPRN, SE_RPRN, SE_UNDS,
// The extra two keys on the left side
SE_MINS, SE_PLUS,
// Left thumb keys // Right thumb key
FUN_CLR, MT_SPC, SE_E
),

Trackball

It took a long time for me to get the trackball working (admittedly, mostly because I soldered the pins wrong). There’s quite a lot of documentation for QMK but curiously enough I didn’t find anything that covered the whole setup. I arrived here by trial and error, trying to piece together parts from other keyboards into a setup that worked for me.

First we need to create the files halconf.h and mcuconf.h (they go in the same folder as keyboard.json) to enable the SPI driver:

#pragma once
#include_next <halconf.h>
#define HAL_USE_SPI TRUE
#pragma once
#include_next <mcuconf.h>
#undef RP_SPI_USE_SPI1
#define RP_SPI_USE_SPI1 TRUE

And enable the pointing device with the pmw3389 device driver in rules.mk

POINTING_DEVICE_ENABLE = yes
POINTING_DEVICE_DRIVER = pmw3389

Now we need to add the sensor pins to config.h:

// SPI1, matching mcuconf.h
#define SPI_DRIVER SPID1
// The pin connections from the pmw3389 sensor
#define SPI_MISO_PIN GP12
#define PMW33XX_CS_PIN GP13
#define SPI_SCK_PIN GP14
#define SPI_MOSI_PIN GP15

This should be enough to get the sensor going, but because we have a split keyboard we need to set that up too:

#define SPLIT_POINTING_ENABLE
// The trackball is on the right
#define POINTING_DEVICE_RIGHT

There are some additional tweaks that I had to play with to make the trackball work well:

// The trackball is quite sensitive to how
// large the liftoff distance should be.
#define PMW33XX_LIFTOFF_DISTANCE 0x10
// Sets the mouse resolution, up to 16000.
#define PMW33XX_CPI 1600
// The directions where messed up, this fixes it.
#define POINTING_DEVICE_ROTATION_270 1
#define POINTING_DEVICE_INVERT_X 1

With that I got the trackball moves the mouse as expected.

Debug

As I struggled to get the trackball working I tried to use the debug output. I’ll include it here for completeness sake:

  1. Enable the console in rules.mk:

    CONSOLE_ENABLE = yes
  2. Enable pointing device debugging in config.h:

    #define POINTING_DEVICE_DEBUG
  3. Turn on debugging in keymap.c:

    void keyboard_post_init_user(void) {
    debug_enable = true;
    debug_mouse = true;
    }

And then run qmk console from the command line.

Is this the ultimate keyboard?

No.

This keyboard is certainly the most comfortable keyboard I’ve used but it’s not close to being an “ultimate” keyboard. Here’s a few things that might improve the keyboard:

  1. The trackball still isn’t nearly as comfortable as the Kensington SlimBlade.

    Maybe a keyboard with a larger trackball would be better?

  2. The extra keys on the left side are barely useful.

    It’s not a big deal, maybe I can find some usage for them, but to me having barely useful keys feels wrong.

  3. There are more extra features I feel an ultimate keyboard should have.

    The keyboard I’ve built is nice… But it’s still just a normal keyboard with a trackball. Maybe a vibration sensor, a display, or even some LEDs? A smart knob with software-configurable endstops and detents would really add some weight to the moniker of an ultimate keyboard.

Next steps

It’s hard to know how good the keyboard is before I’ve put it through extensive use, and to do that I need to settle on a keyboard layout for the keyboard. I’ve already designed a layout for a 34-key keyboard that should be fairly straightforward to adapt but I still need to figure out how to add mouse keys and what to do with the “extra” keys on the left-hand side.

Check out The current Cybershard layout for how the keyboard layout is coming along.