I’ve been blogging since 2005, but posts from 2005–10 have not been imported to the current iteration of my website.

One of my goals for Consfigurator is to make it capable of installing Debian to my laptop, so that I can stop booting to GRML and manually partitioning and debootstrapping a basic system, only to then turn to configuration management to set everything else up. My configuration management should be able to handle the partitioning and debootstrapping, too.

The first stage was to make Consfigurator capable of debootstrapping a basic system, chrooting into it, and applying other arbitrary configuration, such as installing packages. That’s been in place for some weeks now. It’s sophisticated enough to avoid starting up newly installed services, but I still need to add some bind mounting.

Another significant piece is teaching Consfigurator how to partition block devices. That’s quite tricky to do in a sufficiently general way – I want to cleanly support various combinations of LUKS, LVM and regular partitions, including populating /etc/crypttab and /etc/fstab. I have some ideas about how to do it, but it’ll probably take a few tries to get the abstractions right.

Let’s imagine that code is all in place, such that Consfigurator can be pointed at a block device and it will install a bootable Debian system to it. Then to install Debian to my laptop I’d just need to take my laptop’s disk drive out and plug it into another system, and run Consfigurator on that system, as root, pointed at the block device representing my laptop’s disk drive. For virtual machines, it would be easy to write code which loop-mounts an empty disk image, and then Consfigurator could be pointed at the loop-mounted block device, thereby making the disk image file bootable.

This is adequate for virtual machines, or small single-board computers with tiny storage devices (not that I actually use any of those, but I want Consfigurator to be able to make disk images for them!). But it’s not much good for my laptop. I casually referred to taking out my laptop’s disk drive and connecting it to another computer, but this would void my laptop’s warranty. And Consfigurator would not be able to update my laptop’s NVRAM, as is needed on UEFI systems.

What’s wanted here is a live system which can run Consfigurator directly on the laptop, pointed at the block device representing its physical disk drive. Ideally this live system comes with a chroot with the root filesystem for the new Debian install already built, so that network access is not required, and all Consfigurator has to do is partition the drive and copy in the contents of the chroot. The live system could be set up to automatically start doing that upon boot, but another option is to just make Consfigurator itself available to be used interactively. The user boots the live system, starts up Emacs, starts up Lisp, and executes a Consfigurator deployment, supplying the block device representing the laptop’s disk drive as an argument to the deployment. Consfigurator goes off and partitions that drive, copies in the contents of the chroot, and executes grub-install to make the laptop bootable. This is also much easier to debug than a live system which tries to start partitioning upon boot. It would look something like this:

    ;; melete.silentflame.com is a Consfigurator host object representing the
    ;; laptop, including information about the partitions it should have
    (deploy-these :local ...
        melete.silentflame.com "/srv/chroot/melete" "/dev/nvme0n1"))

Now, building live systems is a fair bit more involved than installing Debian to a disk drive and making it bootable, it turns out. While I want Consfigurator to be able to completely replace the Debian Installer, I decided that it is not worth trying to reimplement the relevant parts of the Debian Live tool suite, because I do not need to make arbitrary customisations to any live systems. I just need to have some packages installed and some files in place. Nevertheless, it is worth teaching Consfigurator how to invoke Debian Live, so that the customisation of the chroot which isn’t just a matter of passing options to lb_config(1) can be done with Consfigurator. This is what I’ve ended up with – in Consfigurator’s source code:

(defpropspec image-built :lisp (config dir properties)
  "Build an image under DIR using live-build(7), where the resulting live
system has PROPERTIES, which should contain, at a minimum, a property from
CONSFIGURATOR.PROPERTY.OS setting the Debian suite and architecture.  CONFIG
is a list of arguments to pass to lb_config(1), not including the '-a' and
'-d' options, which Consfigurator will supply based on PROPERTIES.

This property runs the lb_config(1), lb_bootstrap(1), lb_chroot(1) and
lb_binary(1) commands to build or rebuild the image.  Rebuilding occurs only
when changes to CONFIG or PROPERTIES mean that the image is potentially
out-of-date; e.g. if you just add some new items to PROPERTIES then in most
cases only lb_chroot(1) and lb_binary(1) will be re-run.

Note that lb_chroot(1) and lb_binary(1) both run after applying PROPERTIES,
and might undo some of their effects.  For example, to configure
/etc/apt/sources.list, you will need to use CONFIG not PROPERTIES."
  (:desc (declare (ignore config properties))
         #?"Debian Live image built in ${dir}")
  (let* (...)
    ;; ...
      ;; ...
               (file:has-content ,auto/config ,(auto/config config) :mode #o755)
             (file:does-not-exist ,@clean)
             (%lbconfig ,dir)
             (%lbbootstrap t ,dir))
           (%lbbootstrap nil ,dir)
           (deploys ((:chroot :into ,chroot)) ,host))
        (%lbchroot ,dir)
        (%lbbinary ,dir)))))

Here, %lbconfig is a property running lb_config(1), %lbbootstrap one which runs lb_bootstrap(1), etc. Those properties all just change directory to the right place and run the command, essentially, with a little extra code to handle failed debootstraps and the like.

The ON-CHANGE and ESEQPROPS combinators work together to sequence the interaction of the Debian Live suite and Consfigurator.

  • In the innermost ON-CHANGE expression: create the file auto/config and populate it with the call to lb_config(1) that we need to make, as described in the Debian Live manual, chapter 6.

    • If doing so resulted in a change to the auto/config file – e.g. the user added some more options – ensure that lb_config(1) and lb_bootstrap(1) both get rerun.
  • Now in the inner ESEQPROPS expression, use DEPLOYS to configure the chroot, essentially by forking into the chroot and recursively reinvoking Consfigurator.

  • Finally, if any of the above resulted in a change being made, call lb_chroot(1) and lb_binary(1).

This way, we only rebuild the chroot if the configuration changed, and we only rebuild the image if the chroot changed.

Now over in my personal consfig:

 :git-snapshot :name "consfig" :repo #P"src/cl/consfig/" ...)

(defproplist hybrid-live-iso-built :lisp ()
  "Build a Debian Live system in /srv/live/spw.

Typically this property is not applied in a DEFHOST form, but rather run as
needed at the REPL.  The reason for this is that otherwise the whole image will
get rebuilt each time a commit is made to my dotfiles repo or to my consfig."
  (:desc "Sean's Debian Live system image built")
      '("--archive-areas" "main contrib non-free" ...)
    (os:debian-stable "buster" :amd64)
    (apt:installed "whatever" "you" "want")

    (git:snapshot-extracted "/etc/skel/src" "dotfiles")
    (file:is-copy-of "/etc/skel/.bashrc" "/etc/skel/src/dotfiles/.bashrc")

    (git:snapshot-extracted "/root/src/cl" "consfig")))

The first argument to LIVE-BUILD:IMAGE-BUILT. is additional arguments to lb_config(1). The third argument onwards are the properties for the live system. The cool thing is GIT:SNAPSHOT-EXTRACTED – the calls to this ensure that a copy of my Emacs configuration and my consfig end up in the live image, ready to be used interactively to install Debian, as described above. I’ll need to add something like (chroot:host-chroot-bootstrapped melete.silentflame.com "/srv/chroot/melete") too.

As with everything Consfigurator-related, Joey Hess’s Propellor is the giant upon whose shoulders I’m standing.

Posted Thu 08 Apr 2021 23:35:00 UTC

I was shocked to learn today that Richard Stallman has been reinstated as a member of the board of the Free Software Foundation. I think this is plain inappropriate, but I cannot see how anyone who doesn’t think that could fail to see the reinstatement as counterproductive. As Bradley M. Kuhn put it,

The question is whether an organization should have a designated leader who is on a sustained, public campaign advocating about an unrelated issue that many consider controversial. It really doesn’t matter what your view about the controversial issue is; a leader who refuses to stop talking loudly about unrelated issues eventually creates an untenable distraction from the radical activism you’re actively trying to advance. The message of universal software freedom is a radical cause; it’s basically impossible for one individual to effectively push forward two unrelated controversial agendas at once. In short, the radical message of software freedom became overshadowed by RMS’ radical views about sexual morality.

There is an open letter calling for the removal of the entire Board of the Free Software Foundation in response. I haven’t signed the letter because the Free Software Foundation Board’s vote to reinstate Stallman was not unanimous, so the call to remove all of them does not make sense to me. I agree with the open letter’s call to remove Stallman from other positions of leadership. I hope that this whole situation can be resolved quickly.

Posted Tue 23 Mar 2021 23:01:11 UTC

I had thought that Emacs’ C-t was mainly about correcting typos. It turns out to be extremely useful when working on Lisp macros which themselves write macros. This typically involves nested quasiquotation, where you can have multiple alternating sequences of open parentheses and backticks, or of commas, quotation marks and ampersats. While you’re working on it you often need to reorder these character sequences and C-t does a great job.

Posted Sat 13 Mar 2021 22:04:57 UTC

I’d like to briefly introduce my new project, Consfigurator:

Consfigurator is a system for declarative configuration management using Common Lisp. You can use it to configure hosts as root, deploy services as unprivileged users, build and deploy containers, and produce disc images. [not all of these are implemented yet, but the design permits them to be]

Consfigurator’s design gives you a great deal of flexibility about how to control the hosts you want to configure. If there is a command you can run which will obtain input and output streams attached to an interactive POSIX sh running on the target host/container, then with a little glue code, you can use much of Consfigurator’s functionality to configure that host/container. But if it is possible to get an implementation of Common Lisp started up on the host, then Configurator can transparently execute your deployment code over on the remote side, rather than exchanging information via POSIX sh. This lets you use the full power of Common Lisp to deploy your configuration.

Configurator has convenient abstractions for combining these different ways to execute your configuration on hosts with different ways of connecting to them. Connections can be arbitrarily nested. For example, to combine SSHing to a Debian machine as an unprivileged user, using sudo to become root, and then starting up a Lisp image to execute your deployment code, you would evaluate

    (deploy ((:ssh (:sudo :as "spwhitton@athena.example.com") :debian-sbcl)) athena.example.com)

Declarative configuration management systems like Consfigurator and Propellor share a number of goals with projects like the GNU Guix System and NixOS. However, tools like Consfigurator and Propellor try to layer the power of declarative and reproducible configuration on top of traditional, battle-tested unix system administration infrastructure like apt, dpkg, yum, and distro package archives, rather than seeking to replace any of those. Let’s get as much as we can out of all that existing distro policy-compliant work!

Please check out the user’s manual, which includes a tutorial/quick start guide, and come join us in #consfigurator on irc.oftc.net. It’s early days but you can already do a fair few things with Consfigurator. It’s a good time to come help get all the basic properties defined!

Posted Wed 10 Mar 2021 20:04:46 UTC

While struggling with Pfizer second dose side effects yesterday, with little ability to do anything serious – so surreal to have a fever yet also certainty you’re not actually ill[1] – I thought I’d try building the branch of Emacs with native Wayland support, and try starting up Sway instead of i3. I recently upgraded my laptop to Debian bullseye, as I usually do at this stage of our pre-release freeze, and was wondering whether bullseye would be the release which would enable me to switch to Wayland.

Why might I want to do this? I don’t care about screen tearing and don’t have any fancy monitors with absurd numbers of pixels. Previously, I had been hoping to cling on to my X11 setup for as long as possible, and switch to Wayland only once things I want to use started working worse on X11, because all the developers of those things have stopped using X11. But then after upgrading to bullseye, I found I had to forward-port an old patch to xfce4-session to prevent it from resetting SSH_AUTH_SOCK to the wrong value, and I thought to myself, maybe I could cut out some of the layers here, and maybe it’ll be a bit less annoying. I have a pile of little scripts trying to glue together xfce4 and i3 to get all the functionality I need, but since there have been people who use their computers for similar purposes to me trying to make Sway useful for quite some time now, maybe there are more integrated solutions available.

I have also been getting tired of things which have only ever half-worked under X, like toggling autolock off when there isn’t fullscreen video playing (when I’m video conferencing on another device, I often want to prevent my laptop’s screen from locking, and it works most of the time, but sometimes still locks, sigh). I have a “normalise desktop” keybinding which tries to fix recurrent issues by doing things like restarting ibus, and it would be nice to drop something so hackish.

And indeed, a lot of basic things do work way better under Sway. swayidle is clean and sane, and I was easily able to add a keybinding which inhibits locking the screen when a certain window is visible. I could bind brightness up/down keys without having to invoke xfce4-power-manager – never managed that before – and, excitingly, I could have those keys bound such that they still work when the screen is locked. I still need two old scripts which interact with the i3/sway IPC, but those two are reasonable ways to extend functionality, rather than bad hacks.

The main thing that I could not figure out is IME – various people claim online to have got typing their Asian languages working natively under Sway, not just into Xwayland windows, but I couldn’t, and there is no standard way to do it yet, it would seem. Also, it seems Qt in Debian doesn’t support Wayland natively, so far as I could tell. And there isn’t really a drop-in replacement for dmenu yet, so you have to run that under Xwayland. A lot of this is probably going to be fixed during 2021, but the thing is, I’ll be on Debian bullseye, so how it works now is probably as good as it is going to get for the next two years or so.

So, wanting to get back to doing something more useful today, I reluctantly booted back into X11. I’m really looking forward to switching to Sway, and getting rid of some of my hacks, but I think I am probably going to have to wait for Debian bookworm – unless I completely run out of patience with the various X11 annoyances described above, and start furiously backporting things.

Update, later that afternoon… Newer versions of fcitx5 packages hit Debian testing within the past few days, it turns out, and the IME problem is solved! So looks like I am slowly going to be able to migrate to Sway during the Debian bullseye lifecycle after all. How nice. Many thanks to various upstreams and those who have been working on these packages in Debian.

[1] Okay, I suppose I could have caught the disease a few days ago and it became symptomatic at the same time I was experiencing the side effects.

Posted Thu 04 Mar 2021 18:29:24 UTC

Recently I have become curious about the Gemini Project and the content that people have made available to be retrieved over the gemini:// protocol. I’m not convinced by the arguments for not just using http, and mostly it’s just that I typically find more things that I am interested in casually reading through on people’s gemlogs than I would on, say, reddit, and similar aggregators. But presumably advocates of gemini:// and the text/gemini format would argue that it’s various respects in which it differs from the web that makes geminispace conducive to the production of the sort of content you find there. So I’m remaining open minded about the possibility that having a completely separate protocol is important, and not just an annoyance because rss2email doesn’t work and I had to spend time writing gmi2email.

I now have a games console at home for the first time in some years, which I bought in response to the ongoing pandemic, and one thing that I have noticed is that using it feels like being offline in a way that playing games on a regular computer never would. It has a WiFi connection but it doesn’t have a web browser, and I am glad that using it provides an opportunity to be disconnected from the usual streams of information. And perhaps something similar ought to be said in favour of how the Gemini project does not just use http. There is, perhaps, a positive psychological effect induced by making the boundary between text/gemini and the web as hard as it is made by using gemini:// rather than http.

Something about which I find myself much more sceptical is how the specification for gemini:// and text/gemini is not extensible. Advocates of Gemini have this idea that they can’t include, say, a version number in the protocol, because the extensibility of the web is what has led to the problems they think it has, so they want to make it impossible. Now on the one hand perhaps the people behind Gemini are in the best position that anyone is in to come up with a spec which they will finalise and render effectively unchangeable, because a lot of them have been using Gopher for decades, and so they have enough experience to be able to say exactly what Gopher is missing, and be confident that they’ve not missed anything. But on the other hand, Gemini is one technological piece in attempts to make a version of the Internet which is healthier for humans – the so-called “small Internet” movement – and maybe there will be new ideas about how the small Internet should be which would benefit from a new version of the Gemini specification. So it seems risky to lock-in to one version.

Comments on Hacker News.

Posted Sun 31 Jan 2021 19:21:48 UTC Tags:

Update 5/ii/2021: It turns out that C-x z not repeating complex commands was an upstream bug introduced in Emacs 22, and is now fixed in the development branch of Emacs 28. So the following code is only interesting to users of Emacs 22 to 27.

In Emacs, you can use C-x z to repeat the last command you input, and subsequently you can keep tapping the ‘z’ key to execute that command again and again. If the command took minibuffer input, however, you’ll be asked for that input again. For example, suppose you type M-z : to delete through the next colon character. If you want to keep going and delete through the next few colons, you would need to use C-x z : z : z : etc. which is pretty inconvenient. So there’s also C-x ESC ESC RET or C-x M-: RET, which will repeat the last command which took minibuffer input, as if you’d given it the same minibuffer input. So you could use M-z : C-x M-: RET C-x M-: RET etc., but then you might as well just keep typing M-z : over and over. It’s also quite inconvenient to have to remember whether you need to use C-x z or C-x M-: RET.

I wanted to come up with a single command which would choose the correct repetition method. It turns out it’s a bit involved, but here’s what I came up with. You can use this under the GPL-3 or any later version published by the FSF. Assumes lexical binding is turned on for the file you have this in.

;; Adapted from `repeat-complex-command' as of November 2020
(autoload 'repeat-message "repeat")
(defun spw/repeat-complex-command-immediately (arg)
  "Like `repeat-complex-command' followed immediately by RET."
  (interactive "p")
  (if-let ((newcmd (nth (1- arg) command-history)))
        (add-to-history 'command-history newcmd)
        (repeat-message "Repeating %S" newcmd)
        (apply #'funcall-interactively
               (car newcmd)
               (mapcar (lambda (e) (eval e t)) (cdr newcmd))))
    (if command-history
        (error "Argument %d is beyond length of command history" arg)
      (error "There are no previous complex commands to repeat"))))

(let (real-last-repeatable-command)
  (defun spw/repeat-or-repeat-complex-command-immediately ()
    "Call `repeat' or `spw/repeat-complex-command-immediately' as appropriate.

Note that no prefix argument is accepted because this has
different meanings for `repeat' and for
`spw/repeat-complex-command-immediately', so that might cause surprises."
    (if (eq last-repeatable-command this-command)
        (setq last-repeatable-command real-last-repeatable-command)
      (setq real-last-repeatable-command last-repeatable-command))
    (if (eq last-repeatable-command (caar command-history))
        (spw/repeat-complex-command-immediately 1)
      (repeat nil))))

;; `suspend-frame' is bound to both C-x C-z and C-z
(global-set-key "\C-z" #'spw/repeat-or-repeat-complex-command-immediately)
Posted Sun 08 Nov 2020 21:16:09 UTC Tags:

Marks and mark rings in GNU Emacs

I recently attempted to answer the question of whether experienced Emacs users should consider partially or fully disabling Transient Mark mode, which is (and should be) the default in modern GNU Emacs.

That blog post was meant to be as information-dense as I could make it, but now I’d like to describe the experience I have been having after switching to my custom pseudo-Transient Mark mode, which is labelled “mitigation #2” in my older post.

In summary: I feel like I’ve uncovered a whole editing paradigm lying just beneath the surface of the editor I’ve already been using for years. That is cool and enjoyable in itself, but I think it’s also helped me understand other design decisions about the basics of the Emacs UI better than before – in particular, the ideas behind how Emacs chooses where to display buffers, which were very frustrating to me in the past. I am now regularly using relatively obscure commands like C-x 4 C-o. I see it! It all makes sense now!

I would encourage everyone who has never used Emacs without Transient Mark mode to try turning it off for a while, either fully or partially, just to see what you can learn. It’s fascinating how it can come to seem more convenient and natural to pop the mark just to go back to the end of the current line after fixing up something earlier in the line, even though doing so requires pressing two modified keys instead of just C-e.


I was amused to learn some years ago that someone was trying to make Emacs work as an X11 window manager. I was amazed and impressed to learn, more recently, that the project is still going and a fair number of people are using it. Kudos! I suspect that the basic motivation for such projects is that Emacs is a virtual Lisp machine, and it has a certain way of managing visible windows, and people would like to be able to bring both of those to their X11 window management.

However, I am beginning to suspect that the intrinsic properties of Emacs buffers are tightly connected to the ways in which Emacs manages visible windows, and the intrinsic properties of Emacs buffers are at least as fundamental as its status as a virtual Lisp machine. Thus I am not convinced by the idea of trying to use Emacs’ ways of handling visible windows to handle windows which do not contain Emacs buffers. (but it’s certainly nice to learn it’s working out for others)

The more general point is this. Emacs buffers are as fundamental to Emacs as anything else is, so it seems unlikely to be particularly fruitful to move something typically done outside of Emacs into Emacs, unless that activity fits naturally into an Emacs buffer or buffers. Being suited to run on a virtual Lisp machine is not enough.

What could be more suited to an Emacs buffer, however, than a typical Unix command shell session? By this I mean things like running commands which produce text output, and piping this output between commands and into and out of files. Typically the commands one enters are sort of like tiny programs in themselves, even if there are no pipes involved, because you have to spend time determining just what options to pass to achieve what you want. It is great to have all your input and output available as ordinary buffer text, navigable just like all your other Emacs buffers.

Full screen text user interfaces, like top(1), are not the sort of thing I have in mind here. These are suited to terminal emulators, and an Emacs buffer makes a poor terminal emulator – what you end up with is a sort of terminal emulator emulator. Emacs buffers and terminal emulators are just different things.

These sorts of thoughts lead one to Eshell, the Emacs Shell. Quoting from its documentation:

The shell’s role is to make [system] functionality accessible to the user in an unformed state. Very roughly, it associates kernel functionality with textual commands, allowing the user to interact with the operating system via linguistic constructs. Process invocation is perhaps the most significant form this takes, using the kernel’s fork' andexec’ functions.

Emacs is … a user application, but it does make the functionality of the kernel accessible through an interpreted language – namely, Lisp. For that reason, there is little preventing Emacs from serving the same role as a modern shell. It too can manipulate the kernel in an unpredetermined way to cause system changes. All it’s missing is the shell-ish linguistic model.

Eshell has been working very well for me for the past month or so, for, at least, Debian packaging work, which is very command shell-oriented (think tools like dch(1)).

The other respects in which Eshell is tightly integrated with the rest of Emacs are icing on the cake. In particular, Eshell can transparently operate on remote hosts, using TRAMP. So when I need to execute commands on Debian’s ftp-master server to process package removal requests, I just cd /ssh:fasolo: in Eshell. Emacs takes care of disconnecting and connecting to the server when needed – there is no need to maintain a fragile SSH connection and a shell process (or anything else) running on the remote end.

Or I can cd /ssh:athena\|sudo:root@athena: to run commands as root on the webserver hosting this blog, and, again, the text of the session survives on my laptop, and may be continued at my leisure, no matter whether athena reboots, or I shut my laptop and open it up again the next morning. And of course you can easily edit files on the remote host.

Update 31/Jan/2021: I now find myself mostly using M-!, M-&, C-x p ! and C-x p & to run shell commands. It would seem that the “shell-ish linguistic model” is less important to the work that I do than I thought it was. Thus, I was getting even less out of running bash in terminal emulators than I thought. I still reach for Eshell for Debian packaging work, I think because tools like dch assume the shell linguistic model.

Posted Thu 23 Jul 2020 18:55:24 UTC

A little under two months ago I invested in an expensive ergonomic keyboard, a Kinesis Advantage 2, and set about figuring out how to use it most effectively with Emacs. The default layout for the keyboard is great for strong typists who control their computer mostly with their mouse, but less good for Emacs users, who are strong typists that control their computer mostly with their keyboard.

It took me several tries to figure out where to put the ctrl, alt, backspace, delete, return and spacebar keys, and aside from one forum post I ran into, I haven’t found anyone online who came up with anything much like what I’ve come up with, so I thought I should probably write up a blog post.

The mappings

  • The pairs of arrow keys under the first two fingers of each hand become ctrl and alt/meta keys. This way there is a ctrl and alt/meta key for each hand, to reduce the need for one-handed chording.

    I bought the keyboard expecting to have all modifier keys on my thumbs. However, (i) only the two large thumb keys can be pressed without lifting your hand away from the home row, or stretching in a way that’s not healthy; and (ii) only the outermost large thumb key can be comfortably held down as a modifier.

    It takes a little work to get used to using the third and fifth fingers of one hand to hold down both alt/meta and shift, for typing core Emacs commands like M-^ and M-@, but it does become natural to do so.

  • The arrow keys are moved to the four ctrl/alt/super keys which run along the top of the thumb key areas.

  • The outermost large thumb key of each hand becomes a spacebar. This means it is easy to type C-u C-SPC with the right hand while the left hand holds down control, and sequences like C-x C-SPC and C-a C-SPC C-e with the left hand with the right hand holding down control.

    It took me a while to realise that it is not wasteful to have two spacebars.

  • The inner large thumb keys become backspace and return.

  • The international key becomes delete.

    Rarely needed for Emacs users, as we have C-d, so initially I just had no delete key, but soon came to regret this when trying to edit text in web forms.

  • Caps Lock becomes Super, but remains caps lock on the keypad layer.

    See my rebindings for ordinary keyboards for some discussion of having just a single Super key.

Sequences of two modified keys on different halves of the keyboard

It is desirable to input sequences like C-x C-o without switching which hand is holding the control key. This requires one-handed chording, but this is trecherous when the modifier keys not under the thumbs, because you might need to press the modified key with the same finger that’s holding the modifier!

Fortunately, most or all sequences of two keys modified by ctrl or alt/meta, where each of the two modifier keys is typed by a different hand, begin with C-c, C-x or M-g, and the left hand can handle each of these on its own. This leaves the right hand completely free to hit the second modified key while the left hand continues to hold down the modifier.

My rebindings for ordinary keyboards

I have some rebindings to make Emacs usage more ergonomic on an ordinary keyboard. So far, my Kinesis Advantage setup is close enough to that setup that I’m not having difficulty switching back and forth from my laptop keyboard.

The main difference is for sequences of two modified keys on different halves of the keyboard – which of the two modified keys is easiest to type as a one-handed chord is different on the Kinesis Advantage than on my laptop keyboard. At this point, I’m executing these sequences without any special thought, and they’re rare enough that I don’t think I need to try to determine what would be the most ergonomic way to handle them.

Posted Thu 23 Jul 2020 16:44:27 UTC Tags:

I’ve been less good at taking adequate typing breaks during the lockdown and I’ve become concerned about how much chording my left hand does on its own during typical Emacs usage, with caps lock rebound to control, as I’ve had it for years.

I thought that now was as good a time as any to do something drastic about this. Here are my rebindings:

  • the keys on either side of the spacebar are control
  • the keys just outside of those are alt/meta
  • caps lock is Super, Windows or Command depending on OS
  • move any window manager keybindings which now become one handed left hand chords such that they are not.

Optional extras:

  • left control is caps lock
  • right control is the compose key.

This has the following advantages:

  • you can easily achieve this rebinding on GNU/Linux, Windows and macOS
  • almost every keyboard has enough keys near the spacebar to make it work, and it’s fine to have just one super key since it is not involved in any one handed chords
  • does not involve relying on the difference between tapping and releasing and holding a modifier key, which I find fragile
  • there are control and alt/meta keys available to both hands, so there is much less call for one-handed chording
  • control and alt/meta are pressed by the thumb, the strongest finger, so when one-handed chording does come up (e.g. C-x C-o without having to switch between control keys) it’s the least harmful form
    • my plan is to use the control/meta key available to the opposite hand for the first key of each sequence, and allow some one handed chording to complete the sequence.
      • Update 23/Jul/2020: I’ve found that I’m typing some of these sequences by performing a one-handed chord in order to type the second key, as planned, and others by performing a one-handed chord in order to type the first key. There are actually only a handful of these sequences, so it seems okay to have sequence-specific habits.
    • there is some temptation to use a curled up little finger on the new alt/meta key, I’m finding, but I’m trying to stop myself from doing that.

The main disadvantage, aside from an adjustment period when I feel that someone has inserted a massive marshmellow between me and my computer, is that Ctrl-Alt combinations are a bit difficult; in Emacs, C-M-SPC is hard to do without. However I think I’ve found a decent way to do it (thumb on control, curled ring finger on alt, possibly little finger on shift for Emacs’ infamous C-M-S-v standard binding). (Update 23/Jul/2020: this has been working out fine.)

Posted Fri 05 Jun 2020 16:21:48 UTC