Remap one key to another in Windows, like Capslock to Control

There's three options to consider for remapping keys on Windows.

(1) Manually edit the Windows Registry

You can directly edit the Windows registry to remap keys, for example to Map capslock to control.  You'll need to know the keyboard hex scan codes involved though.

(2) Edit the Windows Registry with SharpKeys

If you have many keys to remap, or don't want to manually mess with the registry, there's an App for that!  SharpKeys looks pretty good.  You can use it to remap the Right Alt to the Left Windows key for example.

(3) Don't edit the Registry but run a background app like PT or AHK

Some common remappings like Capslock to control can be easily done without editing the registry at all!  You can run an app that stays in the background, listening for the keys you want changed, then they'll inject the key you want for it to change to.

Microsoft's PowerToys has a module for that sort of usage, and it comes with other toys like advanced file name changer, etc.  The kind of key remapping allowed is extremely limited though.

Another option is AutoHotKey (AHK).  It'll give you extreme customization beyond remapping a single key to a single key.  It'll let you script keys and combos to do pretty much arbitrary things to the keyboard and mouse.

Don't run both PowerToys keyboard remapping module and AHK key remappings together as they may interfere with each other!

Remap Capslock to Control with AutoHotKey

Make a text file and copy the script below into it, then save the text file with a .ahk file extension.  Double click the .ahk script file and AutoHotKey will run it to give you this 3FTD behaviour.

If you want the script to run every time you log in to Windows, you'll need to move the script or add a shortcut to it into the Windows startup folder.  See detailed instructions for that.


#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

#SingleInstance force


Limitations to all methods

Some Windows hotkeys cannot be remapped!

Key combos like Windows key + L are dealt with by the Windows OS at a much lower level and treated specially.  Those cannot be remapped by PowerToys or AHK, at least not normally or usefully.

You could disable Win+L completely by disabling the Windows lock screen function [1].  But then your computer will never lock... ever.  Not when waking from sleep, not from the Control + Alt + Delete menu.  It's all or nothing.

Some apps don't allow / respect the remappings

Also apps that access the keyboard at a lower level, like some games and VirtualBox, don't play nice with AHK remappings.

Games access keyboards in a way that reduces latency to increase gaming responsiveness. And some games have anti-scripting functionality to discourage cheating.

VirtualBox is virtualizing your PC for another OS so a Windows background app messing with the keyboard of course won't play nice with VirtualBox.  And yes, Win+L will lock Windows even if you're inside a Linux guest in VirtualBox!

Vendor hotkeys can't be remapped

There are vendor specific keys and combos that your PC maker may have added via their keyboard drivers.  Like Lenovo's Fn+Tab starts the Windows magnifier key combo.  Press that while in a VirtualBox guest OS and it'll still run the magnifier!

Neither AHK nor PowerToys can remap the Fn+Tab combo either.  In fact, remapping the Fn key is usually tough if not impossible.  It's something handled specially by vendor's drivers on laptops to provide special functionality.



Compared to Macs

Scripting with AppleScript

Coming from the Mac world where AppleScript lets you implement customized and scripted automation of tasks, AutoHotKey (AHK) is similar in many ways.  Since AppleScript can be substituted with JavaScript, then the scripting language itself is better than AHK.

However, AppleScript doesn't provide hooks to customize or remap keys and key combos, so AHK does have this feature AppleScript doesn't.

On the flip side, Mac apps that make extensive use of Apple's Open Scripting Architecture allow AppleScripts to directly access certain data inside those apps for better integration.  Not every or many apps support advanced OSA usage, but it gives AppleScript an edge.

Remapping with Karabiner

As for providing key and combo remapping, I'm finding Karabiner-Elements on Macs to be better for that job than AHK.

Karabiner is more intuitive, purpose built for remapping keys, with many of the same AHK features for multi-key remapping.  It can also run scripts on key triggers like AHK, so Karabiner + AppleScript is roughly equivalent to AHK's functionality.

Less limitations

Macs have less limitations than Windows on the key remapping front too.  You'll have no problems using Karabiner-Elements to remap the "Apple key" (command key) vs the difficulties of remapping the Windows key (especially special combos like Win+L).

Key remappings with Karabiner on Macs are also respected by VirtualBox unlike remappings done via AHK on Windows.  To be fair, some AHK key remappings do work with VirtualBox, but I had trouble with modifier keys and special Windows hotkeys.


[1] How to disable Windows 10 Workstation Lock (Window + L) Functionality


My app uses only java.base, but actually jdeps missed the jdk.crypto.cryptoki module!

I was building a small app and I purposely used only the java.base module.  That way when I build a custom Java Runtime for it, I can build the smallest possible runtime with jlink.

Java's jdeps lets you know which module your app depends on, and it said my app only uses the java.base module. 

Build the runtime with jlink and run my app with it and oops, an error having to do with cryptoki.

Turns out jdeps made a mistake and did not identify the jdk.crypto.cryptoki module!

I suspect my use of the java.net.URI class was to blame, as it was used to access an https URL.

Anyway, if you purposely depend only on the java.base module, but also use the URI class, note that it may actually also depend on the jdk.crypto.cryptoki module!


Build your own custom Java Runtime with jlink and jdeps

Since Java 9, the way Java programs are supposed to be distributed changed.

It used to be that users would install a Java Runtime Environment (JRE) on their system.  The user then gets the Java app from the app developer.  The Java app then runs on the JRE that's installed.

Some OS like Macs used to even have a "system" JRE installed as part of the OS.

The world's moved on from that style of app distribution.  Nowadays, many users want statically compiled programs, or a single executable file with no dependencies.  For better or worse.

That means when you develop a Java app and want to distribute it, it's on you as the developer to create your own custom Java Runtime and package that runtime together with the rest of your app for distribution.

The upshot is that if you don't use too much of the Java standard library and customize your Java Runtime, then you pay for only what you use in terms of the size of the Java runtime you need to package with your app.

You can find what Java modules your app depends on using jdeps easily: jdeps my.jar

Then you can use jlink to create the Java Runtime your app needs: jlink --output my-custom-runtime --add-modules java.base,and.other.modules

Then you can run your app with the custom Java Runtime: /path/to/my-custom-runtime/bin/java -jar my.jar

The Java Runtime that jlink creates is specific to the OS platform you ran jlink on.  So on Windows, you'll have to substitute java.exe for java in the path above.

There's a way to cross-compile a Java Runtime for a different OS platform with jlink, but you'll need to download the target platform's JDK.  It was easier to run the target platform's OS in a virtual machine and run jlink on it instead.

A thorough and detailed tutorial is How to Create Java Runtime Images with jlink.


How to reduce PDF file size on Macs and Linux?

 Of the suggestions I saw, using the "shrink PDF" script files with Ghostscript was the best.

Install Ghostscript first.  On Linux, apt install ghostscript or on Macs using homebrew, brew install ghostscript.

Save that shrink PDF script into a shrinkpdf.sh file.  Make sure to chmod u+x shrinkpdf.sh to allow it to execute.

Then use it, /path/to/shrinkpdf.sh in.pdf out.pdf.

On my old Mac running Sierra macOS 10.12, Ghostscript had trouble running because the way brew built it, Ghostscript was looking for the libXt.6.dylib library in the wrong place.

You need XQuartz installed first.  But XQuartz installs X11 libraries into /opt/X11.  But Ghostscript looks for X11 libraries in /usr/X11.

Easy fix?  Symlink the X11 directory to where Ghostscript expects it.

Go to where Ghostscript looks for X11: cd /usr

Then symlink to where the X11 libraries actually are: sudo ln -s /opt/X11.



Why upgrade past Java 8?

What reasons are there to use Java versions newer than Java 8 (JDK 1.8)?

I'll give two: (1) security, and (2) great new features!  Let me explain.

(1) security

The last "major release" of Java was Java 9.  The Java upgrade cadence changed at that point.  They're now on a steady stream of "feature releases" model.  Kind of like how there's no major release of Chrome, Firefox, or Windows 10 nowadays... it's just a stream of feature releases.  Java's cadence is 6 months, so about every 6 months, they ship and bump the version number.  JDK 15 was released not long ago.

So who cares?  Well only the current release (JDK15 as of this writing) gets proper maintenance and security updates for free.

Note "for free".  Certainly some software development shops will stick with old JDKs, but they might have good reasons and good mitigation for using old JDKs.  Good reasons like certain customers are stuck (for whatever reason) with old JDKs, or vendors of certain dependencies they need are stuck with old JDKs.

Good mitigations include... paying for paid Long Term Support versions of Java from a JDK vendor.  E.g. it looks like Azul offers a JDK 8 LTS, although the current LTS from any vendor is JDK 11.  The next LTS is JDK 17 scheduled for September 2021 (because of the stream of feature releases model, JDK 12 thru 16 are relatively small feature upgrades, hence the gap between the LTS versions).

You can even use the "free LTS" versions if you want to download those binaries from Oracle, Red hat, or Azul etc., just don't expect actual support without paying!  Mostly they backport security fixes from current version to the latest LTS version, and provide real support for paying JDK LTS customers.  So "free LTS" builds aren't really fully supported for free.  If you must use an LTS release, at least use the latest one (that's JDK 11).

But really, for free security and maintenance, you should just use the latest version.  It's no different from Chrome or Firefox... you should always use the latest to avoid security vulnerabilities.

As for which vendor to choose from? There's Oracle, Red hat, Azul, Amazon Corretto, AdoptOpenJDK, etc.  But they're all built from the same OpenJDK from Oracle, even Oracle's JDK, and they contribute back upstream to OpenJDK.  If you want a slick downloads page and easy installers, AdoptOpenJDK and Azul looks pretty ok.

(2) great new features

Java has a lot of great features in newer releases.  These features aren't just for looks, they bring life back into Java.  I know... "kids these days"... always wanting

  • type inference --- like in Swift, C#, etc
  • lambdas --- like in lisp, C++11, JavaScript, etc.
  • async --- like in JavaScript, rust, etc
  • better garbage collectors --- like Go

But those language features are, rightly or wrongly, table stakes to be considered an up-to-date language nowadays.  Even modern C++ has type inference and async, and smart pointers with garbage collection (reference counted).


  • Java 8 has lambdas
  • Java 10 has type inference (use var)
  • I believe Java 16 will have virtual threads (Java's better answer to async) from Project Loom
  • Java 15 has better garbage collectors (by some metrics), and in fact multiple GCs to choose from for different use-cases: Shenandoah, ZGC, and G1 (admittedly, only more advanced development might care about GC performance)
  • There's multiline text blocks since Java 15, finally
  • There's extended switch expressions that can yield values since Java 14
  • and also a safer switch statement/expression using "arrow case" labels instead of "colon case" labels since Java 14
  • Java 9 introduced JShell, which is fantastic to be able to use a REPL to test out Java code

There's so much to get excited about!  There's a joke about Java being the new Cobol.  But not anymore with features that bring it parity with other languages.