443 lines
18 KiB
Markdown
443 lines
18 KiB
Markdown
|
---
|
||
|
title: ZSA Moonlander Review
|
||
|
date: 2020-11-06
|
||
|
series: keeb
|
||
|
tags:
|
||
|
- moonlander
|
||
|
- keyboard
|
||
|
- nixos
|
||
|
---
|
||
|
|
||
|
# ZSA Moonlander Review
|
||
|
|
||
|
I am nowhere near qualified to review things objectively. Therefore this
|
||
|
blogpost will mostly be about what I like about this keyboard. I plan to go into
|
||
|
a fair bit of detail, however please do keep in mind that this is subjective as
|
||
|
all hell. Also keep in mind that this is partially also going to be a review of
|
||
|
my own keyboard layout too. I'm going to tackle this in a few parts that I will
|
||
|
label with headings.
|
||
|
|
||
|
This review is NOT sponsored. I paid for this device with my own money. I have
|
||
|
no influence pushing me either way on this keyboard.
|
||
|
|
||
|
![a picture of the keyboard on my
|
||
|
desk](https://cdn.christine.website/file/christine-static/img/keeb/Elm3dN8XUAAYHws.jpg)
|
||
|
|
||
|
[That 3d printed brain is built from the 3D model that was made as a part of <a
|
||
|
href="https://christine.website/blog/brain-fmri-to-3d-model-2019-08-23">this
|
||
|
blogpost</a>.](conversation://Mara/hacker)
|
||
|
|
||
|
## tl;dr
|
||
|
|
||
|
I like the Moonlander. It gets out of my way and lets me focus on writing and
|
||
|
code. I don't like how limited the Oryx configurator is, but the fact that I can
|
||
|
build my own firmware from source and flash it to the keyboard on my own makes
|
||
|
up for that. I think this was a purchase well worth making, but I can understand
|
||
|
why others would disagree. I can easily see this device becoming a core part of
|
||
|
my workflow for years to come.
|
||
|
|
||
|
## Build Quality
|
||
|
|
||
|
The Moonlander is a solid keyboard. Once you set it up with the tenting legs and
|
||
|
adjust the key cluster, the keyboard is rock solid. The only give I've noticed
|
||
|
is because my desk mat is made of a rubber-like material. The construction of
|
||
|
the keyboard is all plastic but there isn't any deck flex that I can tell.
|
||
|
Compare this to cheaper laptops where the entire keyboard bends if you so much
|
||
|
as touch the keys too hard.
|
||
|
|
||
|
The palmrests are detachable and when they are off it gives the keyboard a
|
||
|
space-age vibe to it:
|
||
|
|
||
|
![the left half of the keyboard without the palmrest
|
||
|
attached](https://cdn.christine.website/file/christine-static/img/keeb/EmJ1bqNXUAAJy4d.jpg)
|
||
|
|
||
|
The palmrests feel very solid and fold up into the back of the keyboard for
|
||
|
travel. However folding up the palmrest does mess up the tenting stability, so
|
||
|
you can't fold in the palmrest and type very comfortably. This makes sense
|
||
|
though, the palmrest is made out of smooth plastic so it feels nicer on the
|
||
|
hands.
|
||
|
|
||
|
ZSA said that iPad compatibility is not guaranteed due to the fact that the iPad
|
||
|
might not put out enough juice to run it, however in my testing with an iPad Pro
|
||
|
2018 (12", 512 GB storage) it works fine. The battery drains a little faster,
|
||
|
but the Moonlander is a much more active keyboard than the smart keyboard so I
|
||
|
can forgive this.
|
||
|
|
||
|
## Switches
|
||
|
|
||
|
I've been using mechanical keyboards for years, but most of them have been
|
||
|
clicky switches (such as cloned Cherry MX blues, actual legit Cherry MX blues
|
||
|
and the awful Razer Green switches). This is my first real experience with
|
||
|
Cherry MX brown switches. There are many other options when you are about to
|
||
|
order a moonlander, but I figured Cherry MX browns would be a nice neutral
|
||
|
choice.
|
||
|
|
||
|
The keyswitches are hot-swappable (no disassembly or soldering required), and
|
||
|
changing out keyswitches **DOES NOT** void your warranty. I plan to look into
|
||
|
[Holy Pandas](https://www.youtube.com/watch?v=QLm8DNH5hJk) and [Zilents
|
||
|
V2](https://youtu.be/uGVw85solnE) in the future. There is even a clever little
|
||
|
tool in the box that makes it easy to change out keyswitches.
|
||
|
|
||
|
Overall, this has been one of the best typing experiences I have ever had. The
|
||
|
noise is a little louder than I would have liked (please note that I tend to
|
||
|
bottom out the keycaps as I type, so this may end up factoring into the noise I
|
||
|
experience); but overall I really like it. It is far better than I have ever had
|
||
|
with clicky switches.
|
||
|
|
||
|
## Typing Feel
|
||
|
|
||
|
The Moonlander uses an ortholinear layout as opposed to the staggered layout
|
||
|
that you find on most keyboards. This took some getting used to, but I have
|
||
|
found that it is incredibly comfortable and
|
||
|
|
||
|
## My Keymap
|
||
|
|
||
|
Each side of the keyboard has the following:
|
||
|
|
||
|
- 20 alphanumeric keys (some are used for `;`, `,`, `.` and `/` like normal
|
||
|
keyboards)
|
||
|
- 12 freely assignable keys (useful for layer changes, arrow keys, symbols and
|
||
|
modifiers)
|
||
|
- 4 thumb keys
|
||
|
|
||
|
In total, this keyboard has 72 keys, making it about a 70% keyboard (assuming
|
||
|
the math in my head is right).
|
||
|
|
||
|
My keymap uses all but two of these keys. The two keys I haven't figured out how
|
||
|
to best use yet are the ones that I currently have the `[` and `]` keycaps on.
|
||
|
Right now they are mapped to the left and right arrow keys. This was the
|
||
|
default.
|
||
|
|
||
|
My keymap is organized into
|
||
|
[layers](https://docs.qmk.fm/#/keymap?id=keymap-and-layers). In each of these
|
||
|
subsections I will go into detail about what these layers are, what they do and
|
||
|
how they help me. My keymap code is
|
||
|
[here](https://tulpa.dev/cadey/kadis-layouts/src/branch/master/moonlander) and I
|
||
|
have a limited view of it embedded below:
|
||
|
|
||
|
<div style="padding-top: 60%; position: relative;">
|
||
|
<iframe src="https://configure.ergodox-ez.com/embed/moonlander/layouts/xbJXx/latest/0" style="border: 0; height: 100%; left: 0; position: absolute; top: 0; width: 100%"></iframe>
|
||
|
</div>
|
||
|
|
||
|
If you want to flash my layout to your Moonlander for some reason, you can find
|
||
|
the firmware binary
|
||
|
[here](https://cdn.christine.website/file/christine-static/img/keeb/moonlander_kadis.bin).
|
||
|
You can then flash this to your keyboard with
|
||
|
[Wally](https://ergodox-ez.com/pages/wally).
|
||
|
|
||
|
### Base Layers
|
||
|
|
||
|
I have a few base layers that contain the main set of letters and numbers that I
|
||
|
type. The main base layer is my Colemak layer. I have the keys arranged to a
|
||
|
standard [Colemak](https://Colemak.com/) layout and it is currently the layer I
|
||
|
type the fastest on. I have the RGB configured so that it is mostly pink with
|
||
|
the homerow using a lighter shade of pink. The color codes come from my logo
|
||
|
that you can see in the favicon [or here for a larger
|
||
|
version](https://christine.website/static/img/avatar_large.png).
|
||
|
|
||
|
I also have a qwerty layer for gaming. Most games expect qwerty keyboards and
|
||
|
this is an excellent stopgap to avoid having to rebind every game that I want to
|
||
|
play. The left side of the keyboard is the active one with the controller board
|
||
|
in it too, so I can unplug the other half of the keyboard and give my mouse a
|
||
|
lot of room to roam.
|
||
|
|
||
|
Thanks to a friend of mine, I am also playing with Dvorak. I have not gotten far
|
||
|
in Dvorak yet, but it is interesting to play with.
|
||
|
|
||
|
I'll cover the leader key in the section below dedicated to it, but the other
|
||
|
major thing that I have is a colon key on my right hand thumb cluster. This has
|
||
|
been a huge boon for programming. The colon key is typed a lot. Having it on the
|
||
|
thumb cluster means that I can just reach down and hit it when I need to. This
|
||
|
makes writing code in Go and Rust so much easier.
|
||
|
|
||
|
### Symbol/Number Layer
|
||
|
|
||
|
If you look at the base layer keymap, you will see that I do not have square
|
||
|
brackets mapped anywhere there. Yet I write code with it effortlessly. This is
|
||
|
because of the symbol/number layer that I access with the lower right and lower
|
||
|
left keys on the keyboard. I have it positioned there so I can roll my hand to
|
||
|
the side and then unlock the symbols there. I have access to every major symbol
|
||
|
needed for programming save `<` and `>` (which I can easily access on the base
|
||
|
layer with the shift key). I also get a nav cluster and a number pad.
|
||
|
|
||
|
I also have [dynamic macros](https://docs.qmk.fm/#/feature_dynamic_macros) on
|
||
|
this layer which function kinda like vim macros. The only difference is that
|
||
|
there's only two macros instead of many like vim. They are convenient though.
|
||
|
|
||
|
### Media Layer
|
||
|
|
||
|
One of the cooler parts of the Moonlander is that it can act as a mouse. It is a
|
||
|
very terrible mouse (understandably, mostly because the digital inputs of
|
||
|
keypresses cannot match the analog precision of a mouse). This layer has an
|
||
|
arrow key cluster too. I normally use the arrow keys along the bottom of the
|
||
|
keyboard with my thumbs, but sometimes it can help to have a dedicated inverse T
|
||
|
arrow cluster for things like old MS-DOS games.
|
||
|
|
||
|
I also have media control keys here. They aren't the most useful on my linux
|
||
|
desktop, however when I plug it into my iPad they are amazing.
|
||
|
|
||
|
### dwm Layer
|
||
|
|
||
|
I use [dwm](/blog/why-i-use-suckless-tools-2020-06-05) as my main window manager
|
||
|
in Linux. dwm is entirely controlled using the keyboard. I have a dedicated
|
||
|
keyboard layer to control dwm and send out its keyboard shortcuts. It's really
|
||
|
nice and lets me get all of the advantages of my tiling setup without needing to
|
||
|
hit weird keycombos.
|
||
|
|
||
|
### Leader Macros
|
||
|
|
||
|
[Leader macros](https://docs.qmk.fm/#/feature_leader_key) are one of the killer
|
||
|
features of my layout. I have a [huge
|
||
|
bank](https://tulpa.dev/cadey/kadis-layouts/src/branch/master/doc/leader.md) of
|
||
|
them and use them to do type out things that I type a lot. Most common git and
|
||
|
Kubernetes commands are just a leader macro away.
|
||
|
|
||
|
The Go `if err != nil` macro that got me on /r/programmingcirclejerk twice is
|
||
|
one of my leader macros, but I may end up promoting it to its own key if I keep
|
||
|
getting so much use out of it (maybe one of the keys I don't use can become my
|
||
|
`if err != nil` key). I'm sad that the threads got deleted (I love it when my
|
||
|
content gets on there, it's one of my favorite subreddits), but such is life.
|
||
|
|
||
|
## NixOS, the Moonlander and Colemak
|
||
|
|
||
|
When I got this keyboard, flashed the firmware and plugged it in, I noticed that
|
||
|
my keyboard was sending weird inputs. It was rendering things that look like
|
||
|
this:
|
||
|
|
||
|
```
|
||
|
The quick brown fox jumps over the lazy yellow dog.
|
||
|
```
|
||
|
|
||
|
into this:
|
||
|
|
||
|
```
|
||
|
Ghf qluce bpywk tyx nlm;r yvfp ghf iazj jfiiyw syd.
|
||
|
```
|
||
|
|
||
|
This is because I had configured my NixOS install to interpret the keyboard as
|
||
|
if it was Colemak. However the keyboard is able to lie and sends out normal
|
||
|
keycodes (even though I am typing them in Colemak) as if I was typing in qwerty.
|
||
|
This double Colemak meant that a lot of messages and commands were completely
|
||
|
unintelligible until I popped into my qwerty layer.
|
||
|
|
||
|
I quickly found the culprit in my config:
|
||
|
|
||
|
```nix
|
||
|
console.useXkbConfig = true;
|
||
|
services.xserver = {
|
||
|
layout = "us";
|
||
|
xkbVariant = "colemak";
|
||
|
xkbOptions = "caps:escape";
|
||
|
};
|
||
|
```
|
||
|
|
||
|
This config told the X server to always interpret my keyboard as if it was
|
||
|
Colemak, meaning that I needed to tell it not to. As a stopgap I commented this
|
||
|
section of my config out and rebuilt my system.
|
||
|
|
||
|
X11 allows you to specify keyboard configuration for keyboards individually by
|
||
|
device product/vendor names. The easiest way I know to get this information is
|
||
|
to open a terminal, run `dmesg -w` to get a constant stream of kernel logs,
|
||
|
unplug and plug the keyboard back in and see what the kernel reports:
|
||
|
|
||
|
```console
|
||
|
[242718.024229] usb 1-2: USB disconnect, device number 8
|
||
|
[242948.272824] usb 1-2: new full-speed USB device number 9 using xhci_hcd
|
||
|
[242948.420895] usb 1-2: New USB device found, idVendor=3297, idProduct=1969, bcdDevice= 0.01
|
||
|
[242948.420896] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
|
||
|
[242948.420897] usb 1-2: Product: Moonlander Mark I
|
||
|
[242948.420898] usb 1-2: Manufacturer: ZSA Technology Labs
|
||
|
[242948.420898] usb 1-2: SerialNumber: 0
|
||
|
```
|
||
|
|
||
|
The product is named `Moonlander Mark I`, which means we can match for it and
|
||
|
tell X11 to not colemakify the keycodes using something like this:
|
||
|
|
||
|
```
|
||
|
Section "InputClass"
|
||
|
Identifier "moonlander"
|
||
|
MatchIsKeyboard "on"
|
||
|
MatchProduct "Moonlander"
|
||
|
Option "XkbLayout" "us"
|
||
|
Option "XkbVariant" "basic"
|
||
|
EndSection
|
||
|
```
|
||
|
|
||
|
[For more information on what you can do in an `InputClass` section, see <a
|
||
|
href="https://www.x.org/releases/current/doc/man/man5/xorg.conf.5.xhtml#heading9">here</a>
|
||
|
in the X11 documentation.](conversation://Mara/hacker)
|
||
|
|
||
|
This configuration fragment can easily go in the normal X11 configuration
|
||
|
folder, but doing it like this would mean that I would have to manually drop
|
||
|
this file in on every system I want to colemakify. This does not scale and
|
||
|
defeats the point of doing this in NixOS.
|
||
|
|
||
|
Thankfully NixOS has [an
|
||
|
option](https://search.nixos.org/options?channel=20.09&show=services.xserver.inputClassSections&from=0&size=30&sort=relevance&query=inputClassSections)
|
||
|
to solve this very problem. Using this module we can write something like this:
|
||
|
|
||
|
```nix
|
||
|
services.xserver = {
|
||
|
layout = "us";
|
||
|
xkbVariant = "colemak";
|
||
|
xkbOptions = "caps:escape";
|
||
|
|
||
|
inputClassSections = [
|
||
|
''
|
||
|
Identifier "yubikey"
|
||
|
MatchIsKeyboard "on"
|
||
|
MatchProduct "Yubikey"
|
||
|
Option "XkbLayout" "us"
|
||
|
Option "XkbVariant" "basic"
|
||
|
''
|
||
|
''
|
||
|
Identifier "moonlander"
|
||
|
MatchIsKeyboard "on"
|
||
|
MatchProduct "Moonlander"
|
||
|
Option "XkbLayout" "us"
|
||
|
Option "XkbVariant" "basic"
|
||
|
''
|
||
|
];
|
||
|
};
|
||
|
```
|
||
|
|
||
|
But this is NixOS and that allows us to go one step further and make the
|
||
|
identifier and product matching string configurable as will with our own [NixOS
|
||
|
options](https://nixos.org/manual/nixos/stable/index.html#sec-writing-modules).
|
||
|
Let's start by lifting all of that above config into its own module:
|
||
|
|
||
|
```nix
|
||
|
# Colemak.nix
|
||
|
|
||
|
{ config, lib, ... }: with lib; {
|
||
|
options = {
|
||
|
cadey.colemak = {
|
||
|
enable = mkEnableOption "Enables colemak for the default X config";
|
||
|
};
|
||
|
};
|
||
|
|
||
|
config = mkIf config.cadey.Colemak.enable {
|
||
|
services.xserver = {
|
||
|
layout = "us";
|
||
|
xkbVariant = "colemak";
|
||
|
xkbOptions = "caps:escape";
|
||
|
|
||
|
inputClassSections = [
|
||
|
''
|
||
|
Identifier "yubikey"
|
||
|
MatchIsKeyboard "on"
|
||
|
MatchProduct "Yubikey"
|
||
|
Option "XkbLayout" "us"
|
||
|
Option "XkbVariant" "basic"
|
||
|
|
||
|
''
|
||
|
''
|
||
|
Identifier "moonlander"
|
||
|
MatchIsKeyboard "on"
|
||
|
MatchProduct "Moonlander"
|
||
|
Option "XkbLayout" "us"
|
||
|
Option "XkbVariant" "basic"
|
||
|
''
|
||
|
];
|
||
|
};
|
||
|
};
|
||
|
}
|
||
|
```
|
||
|
|
||
|
[This also has Yubikey inputs not get processed into Colemak so that <a
|
||
|
href="https://developers.yubico.com/OTP/OTPs_Explained.html">Yubikey OTPs</a>
|
||
|
still work as expected. Keep in mind that a Yubikey in this mode pretends to be
|
||
|
a keyboard, so without this configuration the OTP will be processed into
|
||
|
Colemak. The Yubico verification service will not be able to understand OTPs
|
||
|
that are typed out in Colemak.](conversation://Mara/hacker)
|
||
|
|
||
|
Then we can turn the identifier and product values into options with
|
||
|
[mkOption](https://nixos.org/manual/nixos/stable/index.html#sec-option-declarations)
|
||
|
and string interpolation:
|
||
|
|
||
|
```nix
|
||
|
# ...
|
||
|
cadey.colemak = {
|
||
|
enable = mkEnableOption "Enables Colemak for the default X config";
|
||
|
ignore = {
|
||
|
identifier = mkOption {
|
||
|
type = types.str;
|
||
|
description = "Keyboard input identifier to send raw keycodes for";
|
||
|
default = "moonlander";
|
||
|
};
|
||
|
product = mkOption {
|
||
|
type = types.str;
|
||
|
description = "Keyboard input product to send raw keycodes for";
|
||
|
default = "Moonlander";
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
# ...
|
||
|
''
|
||
|
Identifier "${config.cadey.colemak.ignore.identifier}"
|
||
|
MatchIsKeyboard "on"
|
||
|
MatchProduct "${config.cadey.colemak.ignore.product}"
|
||
|
Option "XkbLayout" "us"
|
||
|
''
|
||
|
# ...
|
||
|
```
|
||
|
|
||
|
Adding this to the default load path and enabling it with `cadey.Colemak.enable
|
||
|
= true;` in my tower's `configuration.nix`
|
||
|
|
||
|
This section was made possible thanks to help from [Graham
|
||
|
Christensen](https://twitter.com/grhmc) who seems to be in search of a job. If
|
||
|
you are wanting someone on your team that is kind and more than willing to help
|
||
|
make your team flourish, I highly suggest looking into putting him in your
|
||
|
hiring pipeline. See
|
||
|
[here](https://twitter.com/grhmc/status/1324765493534875650) for contact
|
||
|
information.
|
||
|
|
||
|
## Oryx
|
||
|
|
||
|
[Oryx](https://configure.ergodox-ez.com) is the configurator that ZSA created to
|
||
|
allow people to create keymaps without needing to compile your own firmware or
|
||
|
install the [QMK](https://qmk.fm) toolchain.
|
||
|
|
||
|
[QMK is the name of the firmware that the Moonlander (and a lot of other
|
||
|
custom/split mechanical keyboards) use. It works on AVR and Arm
|
||
|
processors.](conversation://Mara/hacker)
|
||
|
|
||
|
For most people, Oryx should be sufficient. I actually started my keymap using
|
||
|
Oryx and sorta outgrew it as I learned more about QMK. It would be nice if Oryx
|
||
|
added leader key support, however this is more of an advanced feature so I
|
||
|
understand why it doesn't have that.
|
||
|
|
||
|
## Things I Don't Like
|
||
|
|
||
|
This keyboard isn't flawless, but it gets so many things right that this is
|
||
|
mostly petty bickering at this point. I had to look hard to find these.
|
||
|
|
||
|
I would have liked having another thumb key for things like layer toggling. I
|
||
|
can make do with what I have, but another key would have been nice. Maybe add a
|
||
|
1u key under the red shaped key?
|
||
|
|
||
|
At the point I ordered the Moonlander, I was unable to order a black keyboard
|
||
|
with white keycaps. I am told that ZSA will be selling keycap sets as early as
|
||
|
next year. When that happens I will be sure to order a white one so that I can
|
||
|
have an orca vibe.
|
||
|
|
||
|
ZSA ships with UPS. Normally UPS is fine for me, but the driver that was slated
|
||
|
to deliver it one day just didn't deliver it. I was able to get the keyboard
|
||
|
eventually though. Contrary to their claims, the UPS website does NOT update
|
||
|
instantly and is NOT the most up to date source of information about your
|
||
|
package.
|
||
|
|
||
|
The cables aren't braided. I would have liked braided cables.
|
||
|
|
||
|
Like I said, these are _really minor_ things, but it's all I can really come up
|
||
|
with as far as downsides go.
|
||
|
|
||
|
## Conclusion
|
||
|
|
||
|
Overall this keyboard is amazing. I would really suggest it to anyone that wants
|
||
|
to be able to have control over their main tool and craft it towards their
|
||
|
desires instead of making do with what some product manager somewhere decided
|
||
|
what keys should do what. It's expensive at USD$350, but for the right kind of
|
||
|
person this will be worth every penny. Your mileage may vary, but I like it.
|