My Keyboards 2018 Edition

As part of my effort to tick off the last remaining item on my site roadmap, I read through some old keyboard-related posts from my WordPress blog and decided to give them an uplift to include more recent experiments.

The First Crispy Clack

My very first mechanical keyboard is a WASD v2 104 key with Cherry MX Blue switches that I got in 2014. I picked WASD mainly because of their keyboards' minimal look and keycap customization options. I also got my first 60% in 2014, a Poker II with Cherry MX Brown. Switch selection were more limited back then, seemingly because of potential issues with Cherry's patent. Of course, switch choices is only limited if we restrict ourselves to Cherry MX compatibles, but most alternatives (Matias, Topre, buckling spring to name a few) were even harder to come by in an commonly available package that I liked.

A GH60 based 60% that I frankensteined together became my main keyboard at first. I used Cherry MX Clear switches with 62g springs (ErgoClear) on them, mostly for the fun factor instead of preference. I also found myself tinkering with the layout a lot more often than when I had the Poker II. As great as Poker II's built-in macros mapping sounds, I could never remember all the steps without taking a moment to peruse the manual. I also found myself setting macros up and yet completely forget about them ever since. With GH60's firmware, at the very least I have the configuration files to remind me of the new bindings I set up. Even with this shiny new toy though, my early experiments with keyboard layouts still had only limited success. The layout of a 60% is too standard in my opinion to justify significant changes (while maintaining QWERTY layout), i.e. there isn't really a good location to move Enter to. The few tweaks I ended up making were numpad/function key mapping and swapping out Caps Lock for Control, which at best makes my GH60 on par with any random keyboard with xkb.

On a side note, I once aspired to collect keyboards of all form factors, but soon discovered it to be a highly impractical and expansive exercise. Thus far among the more common layouts, I either owned or tried 104 key, 96 key, 87 key, 75 key, 60 key, and 40 key keyboards. Majority of these differs very little experience wise for me, except for 60% or 40%, where a bit of creativity is required to fit all the desired keys.

ErgoDox and Planck

The first keyboard ever to trigger me to give deeper thoughts into my keyboard layout is the ErgoDox. The ErgoDox boasts a layout drastically different from traditional keyboards, and because of this, offers great flexibility over key placements: I can immediately spot seven sensible locations for the Enter key (original pinky location, four 2u keys at the thumb clusters and the lower two 1.5u vertical keys in the center). I've been using ErgoDox almost exclusively since 2015, and have grown to like it even more as I started using Emacs - having access to both Control and Alt/Meta key on the home row just feels awesome.

My ErgoDox Base Layer Layout

I still feel I am under-utilizing the keyboard's capabilities though. As you might tell, I don't know what to do with some of the thumb cluster keys. I currently have three extra layers set up: one for function keys, one for numpad, and another for a modified Dvorak layout. I haven't spend too much time on the Dvorak layer yet, but I am curious about potential benefits of decreased finger motion. Speaking of ergonomics, a perhaps uninteded benefit of ErgoDox's design is that it frees up the center of my desk, so that I can still read and write normally without a super deep desk.

My ErgoDox Dvorak Layer Layout

Planck is another keyboard that I've taken some thoughts designing layout for. It is pretty surprising what a 40% board is actually capable of. However, using Planck is a lot less comfortable just because of how small it is - ErgoDox on the other hand allows me to rest my arms in more natural positions, instead of cramming my hands together. That being said, I would imagine a Let's Split - basically a Planck split in halves - to work fairly well.

My Planck Layout

Keycap Craze

Ever since I discovered Geekhack, I would routinely refresh the groupbuy or interest check section for new keycap sets that other users designed. I gradually went from sets with flashy colors to those with a more muted and uniform look. I also prefer uniform keycap profiles (like DSA) with text legends than those with height gradients and graphical legends, although I find it really hard to appreciate blank keycaps. Getting a full set of keycaps with matching legends for ErgoDox is certainly no easy task (unlike Planck which is almost entirely consisted of 1u keys), and all these quirks I have made keycap shopping increasingly difficult.

My Own Keyboard/Layout

I've thought about creating my own keyboard occasionally. As perfect as ErgoDox may seem, it is somewhat bulky, and I find the thumb cluster a bit hard to reach. For quite some time I used C-Home and C-End to move to top/bottom of a file in Emacs, and that caused pain in my thumb joint as I need to stretch hard to reach both keys (that went away when I found M-< and M-> though). A slim down version of ErgoDox with more compact thumb clusters might just be the perfect keyboard. By the way, I have never before find wireless keyboard necessary in any way, but since ErgoDox features a split design, a wireless version of it can be used while lying down Nintendo-Switch-style, which would be pretty awesome.

Since I started using Emacs as my main editor, I've been taking statistics of my key-presses with keyfreq. When I have gathered enough data, I might look into customizing my layout even further to suit my needs.

Pixel Art

Like many Geekhackers out there, I made a pixel art of my keyboards and used it as my Geekhack signature.

Pixel Art of My Keyboards I, 2015
Pixel Art of My Keyboards II, 2015

Drawing pixel art for keyboard is a fairly interesting exercise, since it is impossible to get the ratios exactly right, I needed to strike a balance between accuracy and simplicity. I will give this a long due update when I have time.

Construction Finished

After eight months, my blog have finally reached a place where I feel comfortable taking down the "under heavy construction" notice on my home page. In stead of out right deleting the site road map though, I'm stashing it into a blog post.

Site Road Map

  • Find new hosting location. Currently using DigitalOcean.
  • ☑ Install Arch Linux on server.
  • Search for WP replacement. Hugo is pretty good.
  • Find a suitable theme. Currently using hugo-xmin , may consider forking it and write my own ( soresu ).
  • ☑ Server side config, like post-receive for git auto deploy.
  • ☑ Language switcher that does more than redirecting to home page.
  • ☑ Enable Disqus.
  • ☑ Support \(\LaTeX\) expressions via MathJax KaTeX.
  • ☑ Copy-paste fixed page contents from old site (and translate them).
  • ☑ Enable https.
  • ☑ Backup old WP site.
  • ☑ Transfer domain to Google Domains and ensure DNS works as intended.
  • ☑ Find out how to write with org-mode or R markdown.
  • ☑ Configure multilingual support, including footer text, title, etc.
  • ☑ Find out how to make emacs work with fcitx .
  • ☑ Use Google's Noto Sans font Oxygen Sans and Source Code Pro Iosevka for code.
  • ☑ Find a suitable icon/favicon.
  • ☑ Improve templates for posts to display tags and categories.
  • ☑ Cosmetic changes, i.e. no underlines for hyperlinks.
  • ☑ Deal with some nuances in using org-mode with hugo , like how to get syntax highlighting to work properly.
  • ☑ Host my own email.
  • ☑ Customize hugo new to make it more useful, i.e. create multilingual versions directly.
  • ☑ Self-host commenting system as a replacement of Disqus.
  • ☑ Use Let's Encrypt's wildcard certificate.
  • ☑ Restore/rewrite and translate some of the more valuable old posts.

What's on Home Page Now?

I already have an about page and a contact page for whatever I think people might be interested in knowing about myself, so I have no clue what I should put on home page. Since I found the old site road map to be a great way of reminding myself the stuffs I need to get done, I'll replace the road map with another to-do list: my goals for 2018. I am definitely not the most motivated kind of person, but seeing an unfinished to-do list every once in a while does get on my nerves. Let's see how well this is gonna work.

Fun With Fonts in Emacs

I finally took some time to look at the my font configurations in Emacs and cleaned them up as much as possible. This dive into the rabbit hole have been tiring yet fruitful, revealing the cravat of typesetting that I didn't know before, especially for CJK characters.

I primarily use Emacs by running a daemon and connecting to it via a graphical emacsclient frame, and I am attempting to tackle three major problems: I don't have granular control over font mapping, glyph widths are sometimes inconsistent with character widths, and emoji show up as weird blocks. Terminal Emacs doesn't suffer as much from these problems, yet I don't want to give away the nice perks like system clipboard access and greater key binding options, so here goes nothing.

Font Fallback Using Fontsets

Ideally, I want to specify two sets of fonts, a default monospace font and a CJK-specific font. Here's how I originally specified the font in Emacs:

(setq default-frame-alist '((font . "Iosevka-13")))

The method above obviously leaves no ground for fallback fonts. However, it turns out I can specify the font to be a fontset instead of an individual font. According to Emacs Manual, a fontset is essentially a mapping from Unicode range to a font or hierarchy of fonts and I can modify one with relative ease.

Sounds like an easy job now? Not so fast. I don't really know which fontset to modify: fontset behavior is quirky in that the fontset Emacs ends up using seems to differ between emacsclient and normal emacs, between terminal and graphical frames, and even between different locales. While there is a way to get the current active fontset ((frame-parameter nil 'font)), this method is unreliable and may cause errors like this one.

After all kinds of attempts and DuckDuckGoing (that really rolled right off the tongue, and no, I am not the first one), I finally found the answer: just define a new fontset instead of modifying existing ones.

(defvar user/standard-fontset
  (create-fontset-from-fontset-spec standard-fontset-spec)
  "Standard fontset for user.")

;; Ensure user/standard-fontset gets used for new frames.
(add-to-list 'default-frame-alist (cons 'font user/standard-fontset))
(add-to-list 'initial-frame-alist (cons 'font user/standard-fontset))

I won't bore you with the exact logic just yet, as I also made other changes to the fontset.

Displaying Emoji

Solution to emoji display is similar—just specify a fallback font with emoji support—or so I thought. I tried to use Noto Color Emoji as my emoji font, only to find Emacs does not yet support colored emoji font. Emacs used to support colored emoji on macOS, but this functionality was later removed.

I ended up using Symbola as my emoji fallback font (actually I used it as a fallback for all Unicode characters), which provided comprehensive coverage over all the emoji and special characters. Also note that since Emacs 25, customization to the symbols charset, which contains puncation marks, emoji, etc., requires some extra work:

(setq use-default-font-for-symbols nil)

There does exist a workaround for colored emoji though, not with fancy fonts, but by replacing Unicode characters with images. emacs-emojify is a package that provides this functionality. I ultimately decided against it as it does slow down Emacs quite noticeably and the colored emoji image library is not as comprehensive.

Quotation Marks

I've always used full-width directional curly quotation marks ("“”" and "‘’") when typing in Chinese, and ASCII style ambidextrous straight quotation marks (""" and "'") when typing in English. Little did I know there really is no such thing as full-width curly quotation marks: there is only one set of curly quotation mark codepoints in Unicode (U+2018, U+2019, U+201C, and U+201D) and the difference between alleged full-width and half-width curly quotation marks is caused solely by fonts. There have been proposals to standardize the two distinct representations, but for now I'm stuck with this ambiguous mess.

It came as no surprise that these curly quotation marks are listed under symbols charset, instead of a CJK one, thus using normal monospace font despite the fact that I want them to show up as full-width characters. I don't have a true solution for this—being consistent is the only thing I can do, so I forced curly quotation marks to display as full width characters by overriding these exact Unicode codepoints in my fontset. I'm not really sure how I feel when I then realized ASCII style quotation marks also suffered from confusion—maybe we are just really bad at this.

My fallback font configurations can be found on both GitHub and Trantor Holocron and I'll list them here just for sake of completeness:

(defvar user/cjk-font "Noto Sans CJK SC"
  "Default font for CJK characters.")

(defvar user/latin-font "Iosevka Term"
  "Default font for Latin characters.")

(defvar user/unicode-font "Symbola"
  "Default font for Unicode characters, including emojis.")

(defvar user/font-size 17
  "Default font size in px.")

(defun user/set-font ()
  "Set Unicode, Latin and CJK font for user/standard-fontset."
  ;; Unicode font.
  (set-fontset-font user/standard-fontset 'unicode
                    (font-spec :family user/unicode-font)
                    nil 'prepend)
  ;; Latin font.
  ;; Only specify size here to allow text-scale-adjust work on other fonts.
  (set-fontset-font user/standard-fontset 'latin
                    (font-spec :family user/latin-font :size user/font-size)
                    nil 'prepend)
  ;; CJK font.
  (dolist (charset '(kana han cjk-misc hangul kanbun bopomofo))
    (set-fontset-font user/standard-fontset charset
                      (font-spec :family user/cjk-font)
                      nil 'prepend))
  ;; Special settings for certain CJK puncuation marks.
  ;; These are full-width characters but by default uses half-width glyphs.
  (dolist (charset '((#x2018 . #x2019)    ;; Curly single quotes "‘’"
                     (#x201c . #x201d)))  ;; Curly double quotes "“”"
    (set-fontset-font user/standard-fontset charset
                      (font-spec :family user/cjk-font)
                      nil 'prepend)))

;; Apply changes.
(user/set-font)
;; For emacsclient.
(add-hook 'before-make-frame-hook #'user/set-font)

CJK Font Scaling

My other gripe is the width of CJK fonts does not always match up with that of monospace font. Theoretically, full-width CJK characters should be exactly twice of that half-width characters, but this is not the case, at least not in all font sizes. It seems that CJK fonts provide less granularity in size, i.e. 16px and 17px versions of CJK characters in Noto Sans CJK SC are exactly the same, and does not increase until size is bumped up to 18px, while Latin characters always display the expected size increase. This discrepancy means their size would match every couple sizes, but different in between with CJK fonts being a bit too small.

One solution is to specify a slightly larger default size for CJK fonts in the fontset. However, this method would render text-scale-adjust (normally bound to C-x C-= and C-x C--) ineffective against CJK fonts for some reason. A better way that preserves this functionality is to scale the CJK fonts up by customizing face-font-rescale-alist:

(defvar user/cjk-font "Noto Sans CJK SC"
  "Default font for CJK characters.")

(defvar user/font-size 17
  "Default font size in px.")

(defvar user/cjk-font-scale
  '((16 . 1.0)
    (17 . 1.1)
    (18 . 1.0))
  "Scaling factor to use for cjk font of given size.")

;; Specify scaling factor for CJK font.
(setq face-font-rescale-alist
      (list (cons user/cjk-font
                  (cdr (assoc user/font-size user/cjk-font-scale)))))

bWhile the font sizes might still go out of sync after text-scale-adjust, I am not too bothered. The exact scaling factor took me a few trial and error to find out. I just kept adjusting the factor until these line up (I found this table really useful):

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云云
雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲雲
ㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞㄞ
ああああああああああああああああああああああああああああああああああああああああ
가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가

Unfortunately, the CJK font I used has narrower Hangul than other full-width CJK characters, so this is still not perfect—the solution would be to specify a Hangul specific font and scaling factor—but good enough for me.

It took me quite some effort to fix what may seem like a minor annoyance, but at least Emacs did offer the appropriate tools. By the way, I certainly wish I had found this article on Emacs Wiki sooner, as it also provides a neat write up of similar workarounds.

2018 in Review

Before anything, happy New Year!

It's an interesting feeling when the time span of one year gradually becomes shorter relative to the time that has already passed in one's life. If only the actual length of one year also scales with one's age, perhaps we would feel more of the excitement instead of anxiety during the New Year count down. That being said, 2018 was a lot of fun for me, even without ray-tracing graphic cards.

The Amazing 2018

To quote my 2017 self:

If I've learned anything from my past failed plans, it would be to always underestimate my own capabilities when planning...

Yeah, it's totally just that my estimates about the amount of free time I would have was off, as can be seen from the status of my 2018 goals.

  • ☒ Run 1000 miles. [405/1000]
  • ☒ Finish a marathon.
  • ☒ Write 20 blog posts. [10/20]
  • ☒ Get the first signature for my PGP key.
  • ☒ Install Gentoo.

Knowing that I can always change the 'publish date' of blog entries (thanks to hugo), I grew into the bad habit of starting an article and just then shelving it for months to come. When I finally remember that one unfinished article, I frequently dismiss the idea as not really worth elaborating. Now that I think about it, maybe this is exactly what blogs are for, providing a snapshot of myself that I can look back later, whether my future self find it silly or 'not really worth elaborating'.

The number of movie theater visits I had in 2018 probably accounts for 50% of my lifetime total, and with double doses of disappointment from Star Wars: The Last Jedi and Incredibles 2. By the way, 2018 also saw 90% of my lifetime popcorn consumption. I've never realized those can be such addicting.

Although not a marathon, I did ran my first trail half marathon in May. It was the first time I've ever hit the wall while running, due to bad pacing and unpreparedness for the weather. The race started mid afternoon on a scorchingly hot day. After witnessing quite a few people stopped to walk in the first 2 miles, I started off quite a bit faster than my intended pace fueled by a stupid sense of superiority, and hit the wall right at the mark of 4 miles. Fortunately the feeling faded away as I walked the next half of the race, gulping ice-cold Gatorade at every hydration point. However, the ice-cold Gatorade was another trap—temperature dropped rapidly as sun started to set and my stomach started to complain about all the chilly liquid. As the finish line appeared within 400 meters of my sight, my legs were hit by the strongest cramps I've ever had. After barely making it through while being surpassed by 3 people right before finish line, I could only be happy to learn that I was still not the last one: actually, I'm even the first one in my age group (whose size is one). The somewhat illegitimate feeling of compliment, mixed with a bit of salt and guilt made the race a wondrous experience.

The Spectacular 2019

Since Google is deprecating Inbox in the coming March, I've lost my last excuse for clinging to Gmail. I'll try to gradually fade out my Gmail usage for my own email server.

On the front of searching for best solution for blog comments, quite a few bloggers I follow have started embracing IndieWeb and Webmention. In a lot of ways, Webmention was the exact thing I wanted: federated blog comments, posts, and more. Yet I'm reluctant to move further away from a static site, not to mentioning most easy-to-follow Webmention solutions I have found relies heavily on third-party services. The IndieWeb movement itself though is fairly intriguing. I've never had much use for Keybase aside from it being a hub linking most of my online presences (decryption and encryption does not work without uploading PGP private keys, and I have no one to securely chat with), perhaps I should just replace it with rel=me links.

Diving into C++17 was fairly enjoyable during the past year, so I'm looking into learning other new programming languages. Rust and Julia have been on my radar for a while, especially Rust. Having a full suite of officially supported tools makes writing Rust a smooth and deeply satisfying experience. I'll try to dive deeper into both languages and hopefully put them into some uses.

As for running and blog posts, I'll try to match 2018's numbers. On top of those, I'm thinking about keeping a record of the books, music, and shows I've read/listened/watched on this blog, along with my thoughts. I actually attempted something similar during this blog's Wordpress days: I once setup a MediaWiki instance for similar purposes, but lacked the motivation to continue maintaining the entries. I'll keep it simple this time, and I should come up with a set of rating system.

What should I do with the remaining 2018 goals? A separate wishlist is a pretty good idea—let's go with that. As a stretch goal, I should probably clean my desktop computer, which is stuffed with four-year-old dirt, cat hair, and dead skin cells.

Here's to another spectacular 2.9e+17 radiation periods of Caesium-133!

Installing Gentoo

I finally bite the bullet and installed Gentoo on VirtualBox (totally not motivated by the front page wishlist), thereby achieving my ultimate digital @5c3n510n (or descent according to DistroWatch).

Jokes aside, the installation process is surprisingly pleasant: the Gentoo Handbook is wonderfully written, and seems to have a plan for everything that might go wrong. I like the Handbook more than ArchWiki's Installation Guide as it also details the rationale behind each step I took, which is often a fun read in its own right. I would go as far as saying the Gentoo Handbook is actually more beginner friendly, as it carefully assembles bits of information that are normally scattered all over the place, providing a great starting point for learning how to tame the operating system. Besides, Gentoo Handbook covers more than installation: it also contains other necessary setup processes to set up a usable system. I will be gradually replicating my current desktop setup to decide if a migration is worth the time.

My very first encounter with GNU/Linux operating systems is Ubuntu 12.04: one of my classmates (vacuuny/A2Clef) was installing it in school's computer labs. There was a time when I would switch between various Ubuntu variants every few days. I dual booted Windows and Ubuntu for a while before switching entirely to Ubuntu in 2014. Much annoyed by the Amazon ads, I tried out Arch Linux as part of my New Year's resolution in 2015. Even with a second computer to look up instructions, it still took me quite a while to adapt to the new system. I ranted "maybe I still haven't gotten the Arch way" in my old blog, but never looked back once I got the knack of it.

I still try out other distributions from time to time in VirtualBox, but never find them to offer much improvements compared with Arch beyond the setup processes, and even more so when considering the excellent documentation on ArchWiki (well now we have a contender). Once I have my desktop environment set up, the experience between distributions is not that different, but the distinctions kicks in when problems occur and I search online for troubleshooting tips. Having more up-to-date packages is another charm Arch has. More recently, the systemd controversy caused me to start shopping around for a new distribution to try out, not so much because of the actual security concerns, but just to see what it is like to use different init system: my time in Ubuntu was spent mostly in GUIs (apt-get and nano was probably the only command I knew for the longest time) without knowing about init systems and Arch was already using systemd when I switched. Aside from Gentoo, the candidates include Void Linux and the BSDs. Void Linux was easy to set up with its installer wizard, yet I didn't feel compelled to move to it. Let's see if Gentoo would change my mind.

Trackpad and Swollen Batteries

For the last few weeks, the right click on my Dell XPS 13's trackpad is getting less responsive: the entire right half of the trackpad sunk around 2mm beneath the palm rest, making clicks hard to register. At first I dismissed it as normal wear, but it turned out that the swollen batteries lifted the left half of the trackpad to such a degree that the trackpad warped. I immediately ordered an OEM replacement (Dell JD25G) swapped out the swollen batteries. XPS 13 (9343) was a breeze to service. The screws that hold the bottom panel (a quite hefty hunk of aluminum) in place are all clearly visible and the component layout allows battery to be swapped with minimal disassembly. I also swapped out the WLAN card (Dell DW1560) for an Intel AC9560, whose drivers are in the mainline Linux kernel.

The trackpad felt normal after the battery swap, of course. However, the fact that average laptop battery starts to degrade around 18 months surprised me quite a bit. Mine lasting nearly four years is probably quite decent. Newer laptops uses prismatic cells (those slab shaped batteries also found in phones) instead of cylindrical ones, as can be found in my first laptop, Dell Vostro 3750. Roughly speaking, prismatic cells trade size for lifespan by emitting external casing and gas vents found on cylindrical cells. The battery swell is caused by gas build up, which might have been avoided in cylindrical cells with vents. It's interesting that (easily) removable batteries have largely disappeared in consumer laptops - even the large desktop replacements (to be fair, those spend most of the time plugged in anyways). The only consumer electronics that still almost always have removable batteries I can think of are cameras.

After the incident, I started to browse current laptops on the market as the new quad/hex core laptop CPUs are quite tempting an upgrade (my XPS 13 has a i5-5200U). I was not a huge fan of the latest XPS 13 (9380) mostly because of the port selection: I just don't have any USB Type-C devices, so the 1 Type-C plus 2 Type-A combination found on XPS 13 (9360) is superior in my opinion. Besides ports, the onboard WLAN card and removal of full-sized SD card slot also make the latest model less appealing.

I also came across the Let's Note line of laptops from Panasonic, which are reliable, lightweight business laptops that often comes with removable batteries and a wide spectrum of ports. If only they weren't so prohibitively expansive, doesn't have those ugly "Wheel Pads", and come with US keyboard layout, they are quite the ideal laptops. I like the aesthetics of 2016 CF-MX5 series the most, but that won't make much of an upgrade.

More realistic choices include HP's EliteBook, Lenovo's ThinkPad T series, and Dell's Latitude/Precision lines. I vetoed EliteBook because all of them had a huge glaring proprietary docking port that I might never use. Latitude 5491 seem to have cooling issues due to the 45W TDP CPUs, while Latitude 7390 and 7490 both seem quite decent, with options to disable Intel ME and official Linux support. ThinkPad T480 pretty much ticks everything on my list, but it seems that the next iteration T490 will no longer have the bridge battery system and only one SODIMM slot, pretty much like T480s.

Hunting for second-handed machines is also an option, but it defeats the purpose of the upgrade since my primary motivation is the new quad core CPUs. Some may argue our laptops are overpowered already, and indeed my XPS 13 still feels pretty snappy though, so I'm not in urgent need for an upgrade. However, I did come up with a list of what I want in a laptop in case the ideal candidate shows up someday.

  • Good Linux driver support.
  • Below 15 inch in size and low travel weight. XPS 13 converted me from a DTR enthusiast to an Ultrabook follower: it does feel nice to be able carry a laptop all day without feeling it.
  • Non-Nvidia graphics. Both AMD and Intel has better open source driver support and I use my desktop for tasks heavily reliant on GPU.
  • Reasonable battery life (6 hours or more) and removable battery.
  • Not-too-radical port selections, not until all mouses and flash drives default to USB Type-C at least.
  • Standard components and easy to upgrade, i.e. SODIMM slot for memory, PCIe for WLAN card/SSD.
  • A nice trackpad. I'm rather insensitive to quality of laptop keyboards so anything marginally decent would do. It would be really cool to have an ErgoDox laptop though.
  • Not-super-high-resolution display. I'm not too picky about screens either, but 4K feels like an utter overkill for laptops this size that provides marginal improvements while draining more power. I've always used 16:9 displays, but I'm open to trying out different ones.

enumerate() with C++

Quite a few programming languages provide ways to iterate through a container while keeping count of the number of steps taken, such as enumerate() in Python:

for i, elem in enumerate(v):
    print(i, elem)

and enumerate() under std::iter::Iterator trait in Rust:

for (i, elem) in v.iter().enumerate() {
    println!("{}, {}", i, elem);
}

This is just a quick note about how to do similar things in C++17 and later without declaring extra variables out of the for loop's scope.

The first way is to use a mutable lambda:

std::for_each(v.begin(), v.end(),
              [i = 0](auto elem) mutable {
                  std::cout << i << ", " << elem << std::endl;
                  ++i;
              });

This could be used with all the algorithms that guarantees in-order application of the lambda, but I don't like the dangling ++i that could get mixed up with other logic.

The second way utilizes structured binding in for loops:

for (auto [i, elem_it] = std::tuple{0, v.begin()}; elem_it != v.end();
     ++i, ++elem_it) {
    std::cout << i << ", " << *elem_it << std::endl;
}

We have to throw in std::tuple as otherwise compiler would try to create a std::initializer_list, which does not allow heterogeneous contents.

The third least fancy method is to just calculate the distance every time:

for (auto elem_it = v.begin(); elem_it != v.end(); ++elem_it) {
    auto i = std::distance(v.begin(), elem_it);
    std::cout << i << ", " << *elem_it << std::endl;
}

Since we have to copy paste the starting point twice, I like other counter based approaches better.

In C++20, we have the ability to add an init-statement in ranged-based for loops, so we can write something like

for (auto i = 0; auto elem : v) {
    std::cout << i << ", " << elem << std::endl;
    i++;
}

Meh, not that impressive. The new <ranges> library provides a more appealing way to achieve this:

for (auto [i, elem] : v | std::views::transform(
         [i = 0](auto elem) mutable { return std::tuple{i++, elem}; })) {
    std::cout << i << ", " << elem << std::endl;
}

I like the structured binding method and the <ranges> based method the most. It would be even better though if we can get a std::views::enumerate to solve this problem once and for all.

Hello Darkness, My Old Friend

With system wide dark modes becoming commonplace, I took the effort to tweak the color scheme of my blog and added a dark mode specific one using prefers-color-scheme in CSS. I also toyed around the idea of adding a user toggle using JavaScript per instructions here, but ultimately decided against it because of my (totally unjustified and groundless) distaste towards the language.

Color Usage Light Theme Dark Theme
Accent #700000 #8fffff
Background #f7f3e3 #080c1c
Text #2e2d2b #d1d2d4
Code Background #e3dacb #1c2534
Border 1 #e7e3d3 #181c2c
Border 2 #d7d3c3 #282c3c

Writing CSS is a such tiring endeavor, but on the bright side, picking colors is a surprisingly relaxing activity. The light mode color scheme now has reduced contrast, and I updated the isso style sheets with matching colors. Yes, I only inverted the colors in dark mode and did not reduce the font weights because of the peculiar way in which human vision work. Part of me already screams heresy when I look at the color codes formed by three numbers that seem to have no connection whatsoever—they are like dissonant chords that cause itches in brain—so I need them to at least sum up to a nice number.

Wissen ist Nacht!