Greetings Interweb neighbors,
Emacs 28 dropped, but I'm not building it. I'm building tomorrow's Emacs on someone else's power bill.
Native compilation via libgccjit made the stable cut, what was left out is pure GTK support for Wayland. Since I'm on Wayland, I'm tracking the dev branch where it lives, which means frequent rebuilds of bleeding-edge snapshots. PGTK gives native Wayland support without the XWayland compatibility layer overhead.
GitHub offers 2000 minutes monthly of free CI/CD compute. Time to automate Debian package builds for Bullseye.
The beauty of delegation
My desktop is faster to build it, but where's the satisfaction in that? Instead, GitHub Actions spins up a fresh container, installs dependencies, compiles libgccjit support, builds native .eln files, packages to .deb, uploads artifacts.
Build takes around 25 minutes on their infrastructure, zero noise from my machine. Ephemeral compute: servers spin up, do work, vanish.
The setup
Dockerfile does the usual: builds on Bullseye, installs a mountain of dev dependencies (libgccjit-10-dev, libgtk-3-dev, libwebkit2gtk-4.0-dev, etc.), copies in the Emacs source tree, then:
RUN cd emacs && ./configure \
--prefix "/usr" \
--with-native-compilation \
--with-pgtk \
--with-xwidgets \
--with-json \
--with-modules \
CFLAGS="-O2 -pipe -fomit-frame-pointer" && \
make NATIVE_FULL_AOT=1 -j $(nproc)
GitHub Actions workflow:
name: Build package
on:
workflow_dispatch:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Build package
run: ./build.sh
- name: Store package
uses: actions/upload-artifact@v3
with:
name: emacs-pgtk
path: ./packages/emacs-pgtk_*.deb
For the curious reader, all the gory details at github.com/ndrvtl/emacs-pgtk.
After build, manually construct .deb package structure: create DEBIAN/control, strip binaries, pack with dpkg-deb. More control, more tedium.
Smoketest runs apt-get install on built package, verifies emacs --version works. If it fails, workflow goes red. GitHub's problem to tell me.
The real win
Automated builds mean latest dev snapshots always packaged, ready to install. Push to branch, wait 45 minutes, download .deb. No local toolchain noise. No fan spin. No interrupted workflow.
Native compilation is sensibly fast. Pure GTK support works well. But the satisfaction isn't the software — it's having someone else's infrastructure do the work while I drink tea.
Long live free compute, never mind the warming planet. They get metrics, I get watts. Everyone loses.
Howdie folks!
Fzf or fuzzy finder is a neat command line utility that choreographically helps select an element from a list.
I use it to search command history via Ctrl-R (requires fzf shell integration for Zsh). The problem: if you type a query that doesn't match any history entry and exit fzf (with ESC), your precious typed text vanishes. Gone. You have to retype it manually in the shell. Annoying when you realize what you want isn't in history after you've already typed it.
I was hit by this multiple times. Searched the Interweb, found others with same problem, but no ready-made solutions. So I decided to fix it myself.
Programming Zsh requires a lot of patience on my part - too much cruft accumulated over the years, too many ways to do something mixed with arcane syntax.
Anyway, meet fc the builtin fix command. It's the shell's history interface - fc -l lists history, fc -e edits it. The history command you use daily? Just an alias to fc.
In this case, fc -rl 1 does:
-r: reverse order (newest first)-l: list format with line numbers1: starting from entry 1 (entire history)Output format: linenum command which is perfect for piping to fzf. The line number becomes the handle for vi-fetch-history to retrieve the actual command later.
Here's the complete widget implementation:
# CTRL-R - Paste the selected command from history into the command line
fzf-history-widget() {
local selected num ret
setopt localoptions noglobsubst noposixbuiltins pipefail 2> /dev/null
selected=( $(fc -rl 1 |
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS \
-n2..,.. --tiebreak=index --bind=ctrl-s:toggle-sort,ctrl-k:kill-line \
--expect=ctrl-e $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} \
+m --print-query" $(__fzfcmd)) )
ret=$?
if (( $selected[(I)ctrl-e] )); then
if [[ $ret = 0 ]]; then
[[ $selected[3] =~ ^[0-9]+$ ]] && shift selected
zle vi-fetch-history -n $selected[2]
elif [[ $ret = 1 ]]; then
shift -p selected
LBUFFER="${LBUFFER}${selected}"
fi
else
if [[ $ret = 0 ]]; then
zle vi-fetch-history -n $selected[2]
elif [[ $ret = 1 ]]; then
LBUFFER="${LBUFFER}${selected}"
fi
zle accept-line
fi
zle reset-prompt
return $ret
}
zle -N fzf-history-widget
bindkey '^R' fzf-history-widget
The magic happens in the fzf invocation. Key options:
--print-query: outputs the user's typed query as first line, regardless of match--expect=ctrl-e: outputs "ctrl-e" if pressed, allowing edit-vs-execute choice--query=${(qqq)LBUFFER}: pre-fills fzf with current command line buffer (LBUFFER is Zsh's line editor buffer left of cursor, triple-q escapes special chars)+m: single selection modeThe selected array captures fzf's multiline output (Zsh arrays are 1-indexed):
selected[1]: User's query string (from --print-query)selected[2]: Pressed key if --expect matched (e.g., "ctrl-e"), OR history line number if no key pressedselected[3]: Selected history entry (if any)Return codes: 0 = match selected, 1 = no match (ESC/empty selection), 130 = interrupt.
The nested conditionals handle four scenarios. First check: $selected[(I)ctrl-e] uses Zsh subscript flag (I) to search the array for "ctrl-e", returning its index (0 if not found).
Ctrl-e pressed (edit mode):
- Match found (ret=0): fetch history via vi-fetch-history -n (the -n flag uses the line number), don't execute
- No match (ret=1): shift -p removes "ctrl-e" from array, leaving query to append to buffer, don't execute
Enter pressed (execute mode):
- Match found (ret=0): fetch history and execute via accept-line
- No match (ret=1): append user's query to LBUFFER and execute
The quirk: when scrolling without typing, array positions shift - selected[3] gets the history number instead of selected[2]. The regex check ^[0-9]+$ detects this and normalizes via shift.
Finally, zle reset-prompt refreshes the command line with the updated LBUFFER content.
Interrupt (ret=130): code doesn't explicitly handle it, just returns the code and lets the shell deal with it.
With --print-query, user input survives in selected[1] even when ret=1, so no more lost commands when history doesn't match.
Add the function to your .zshrc after sourcing fzf shell integration. This replaces the default fzf-history-widget with the enhanced version. The bindkey '^R' line maps it to Ctrl-R.
Usage:
Fzf version 0.45.0 added built-in support via accept-or-print-query:
export FZF_CTRL_R_OPTS='--bind enter:accept-or-print-query'
Single line versus 30+ lines of Zsh. Progress.
The custom widget above remains useful if you need the Ctrl-e edit mode or are stuck on older fzf versions.
This guy!
I know, I know, the joke was stale the first time I used it, I promise I won't do it again.
So, how is it? For starters, it's surprisingly light for its dimensions and much louder than I expected. My TECK is whisper quiet in comparison and both use Cherry Brown switches, so the noise isn't from the switches themselves. Rather, the shape and all the empty space inside act like a resonance chamber, even though I usually don't bottom out when typing. I wonder if filling the inside with foam or similar materials could dampen the noise — I think I've seen at least one person on the Interweb who showed pictures of an Advantage keyboard stuffed with pieces of cloth.
Regarding the dimensions, it's sensibly taller than my TECK, I would guess its height is about that of three membrane keyboards stacked one on top of the other. Unless you use a height-adjustable desk, I would advise following the manual's suggestion and placing it on a keyboard tray attached under the desk, as I ended up doing. Yeah, I'm the kind of guy who actually reads manuals.
In my opinion, the two thumb clusters are somewhat better than those on an Ergodox, but I would have liked them more if they were rotated a few degrees further — clockwise on the left cluster and counterclockwise on the right.
I'll be honest: at first, I thought having the backspace on the left thumb cluster wasn’t a great idea, but after a few days of use, I started to get the means of it. As for the cursor movement keys’ position, I can’t say I’m a fan. Also, I much prefer the symmetric layout of the modifier keys — Ctrl, Shift, and Alt — on the TECK. On the same note, to use the "Super" modifier, the Gnome leader key, I'm forced to sacrifice one of the two Alt keys. Additionally the Esc key is in a such uncomfortable position and it's considered a second class citizen by Kinesis1 that I remapped the Caps Lock to be another Esc key. Even so I can tell you that is barely usable in Emacs and even in the shell sometimes I would prefer to have an Alt key on the right side of the keyboard.
Another thing I've noticed about the keyboard layout, at least for my hands, is that it's somewhat difficult to make a Ctrl-Shift chord.
About the material used, the key-caps plastic is not bad, but at the same time not exceptional. I would say POM or similar hard plastic with the labels printed on the keys, but no double-shots. I like the blue accent of the keys on the home row, but even if they have a different sculpt to distinguish from the other keys, I would have preferred the classic notch on the F and J to confirm the fingers are on the home row. Sadly no Colemak layout option available even with offering cash, only Dvorak, no surprise there, I know I belong to a niche group of a niche group. Believe me when I say that more than once, coworkers have olled their eyes when I entertained the idea of using layouts other than Qwerty. And they hate me when they start writing on a PC and there's gibberish on the screen.
Anywho, back to my impressions, there is a macro function integrated in the keyboard that could turn out useful, but I have not yet tried to play with it, although it seems more plain than the functions available with the QMk firmware. In my opinion, the clicking tone seems useless, didn't I already write that this thing is loud? I wonder if the internal speaker could be used for other things.
The function keys, if you ask me, still seem a second thought, they are better than el-cheapo first gen rubber buttons, but I'll never be able to touch type with them, they are too far away from the number key row and they are spaced too far apart from each other. The keys with an even number have a little notch in the base, but it's useless in my opinion.
So if you have all read till now you are probably interested in the verdict. So, aiming to be better than a staggered keyboard is placing the bar too low, so yes it's way better than a vanilla run of the mill two centuries old layout membrane USB keyboard, but in my opinion is not the perfect keyboard. Let's see, I would put the two well clusters under "the good", the lacks of full symmetric modifiers under to "the bad" and finally, the function keys to "the ugly" category.
I made a couple of shots of the tray that I literally hacked to my desk, if someone is curious.
P.S. for the record, I purchased the keyboard myself, it's not a test sample generously donated by the manufacturer.
After a few weeks of use, I would say — probably against popular opinion — that my old TECK is more comfortable. Also, as you can see in the photos, the two wrist foam pads are starting to look gross, considering I have only been using it for a few weeks. Imagine what they’ll look like after a year.
If you’re wondering, the Ergodox is tucked away in a drawer, collecting dust. I used it for a very short time because, unfortunately, I didn’t find it very comfortable.
in the first Advantage keyboard model the function keys are riduculosly rubber button bad, in this model the buttons have a spring but are tiny, so forget to touch type with them ↩