My Quest to Map <Win>+<Left> to <Home> on Wayland

Pawit Pornkitprasan
3 min readJun 23, 2019

--

The only arrow key configuration for laptop keyboard that makes sense is when:

  • Fn+Left gives you the “Home” key
  • Fn + Right gives you the “End” key
  • Fn + Up gives you the “Page Up” key
  • Fn + Down gives you the “Page Down” key

I have no idea why many laptop manufacturers don’t adopt that convention and put the Home/End/PgUp/PgDown keys in either easily accidentally clicked location or a location impossible to accurately access without carefully perusing the keyboard with your eyes.

Keyboard Layout on my Laptop

While it’s impossible to remap the Fn key since it’s handled at firmware level, I got quite fond of Chromium OS’ convention of Mapping Win+Arrow Key to Home/End/PgUp/PgDown which made quite a lot of sense. I wanted to reproduce that in Linux. Given that Linux is ultra-customizable, I thought that it would be a simple job, but it turns out to be very complicated.

Naive Attempt

Linux’s method of customizing key layout is using XKB, which is a very cryptic config file. (Hey, it’s Linux-style!) First, I’ve tried editing /usr/share/X11/xkb/symbols/pc to add the arrow keys setup.

    key <LEFT> {
type[Group1]= "PC_SUPER_LEVEL2",
symbols[Group1]= [ Left, Home ]
};
key <RGHT> {
type= "PC_SUPER_LEVEL2",
symbols[Group1]= [ Right, End ]
};
key <UP> {
type= "PC_SUPER_LEVEL2",
symbols[Group1]= [ Up, Prior ]
};
key <DOWN> {
type= "PC_SUPER_LEVEL2",
symbols[Group1]= [ Down, Next ]
};

After disabling Super-related shortcuts in GNOME, that config worked reasonably well in GTK-native applications, but it did not work in Chrome! That is because Chrome sees the “Super” modifier and decide to think that it’s a different shortcut. We must make Chrome not see “Super” somehow. (Note:
“Super” is a generic name for the “Windows” key.)

RedirectKey

If I were using X11, I could’ve used the “RedirectKey” action in XKB to remove the “Super” modifier when the shortcut is executed. Unfortunately, this option is no longer supported in Wayland.

Second Attempt

I tried mapping the “Windows” key to “Mod5” instead of “Super”, which Chrome hopefully will not care about.

    key <LWIN> { [ISO_Level3_Shift ] };
modifier_map Mod5 { ISO_Level3_Shift };
key <LEFT> {
type[Group1]= "THREE_LEVEL",
symbols[Group1]= [ Left, Left, Home ]
};
key <RGHT> {
type= "THREE_LEVEL",
symbols[Group1]= [ Right, Right, End ]
};
key <UP> {
type= "THREE_LEVEL",
symbols[Group1]= [ Up, Up, Prior ]
};
key <DOWN> {
type= "THREE_LEVEL",
symbols[Group1]= [ Down, Down, Next ]
};

Now my setup works in Chrome. There is a disadvantage that shortcuts relying on “Super” no longer works in GNOME, but it’s possible to remap them

gsettings set org.gnome.mutter overlay-key 'ISO_Level3_Shift'
gsettings set org.gnome.settings-daemon.plugins.media-keys screensaver '<Mod5>l'

I thought I got everything working, but unfortunately not. I later found out that Shift+Super+Left doesn’t map to Shift+Home in Chrome. The use case is to highlight text until the beginning or end of the line.

Patching libX11

I have thought about many ways to solve this problem, including patching evdev in the Kernel and patching the xkbcommon library. Ultimately, I’ve found somewhere I can patch, the libX11 library.

This problem only occurs in Chrome and IntelliJ which are X11 applications running on XWayland and uses the libX11 library to get events, so if I hack the library to strip “Super” modifier flag, then everything should work. Thus, I came up with this patch.

diff --git a/src/XlibInt.c b/src/XlibInt.c
index 4e45e62b..afe382cb 100644
--- a/src/XlibInt.c
+++ b/src/XlibInt.c
@@ -915,6 +915,11 @@ _XWireToEvent(
ev->state = event->u.keyButtonPointer.state;
ev->same_screen = event->u.keyButtonPointer.sameScreen;
ev->keycode = event->u.u.detail;
+#define REMAP(from, to) if (ev->keycode == from && ev->state & 0x40) {ev->state = ev->state & ~0x40;ev->keycode=to;}
+ REMAP(114, 115);
+ REMAP(113, 110);
+ REMAP(111, 112);
+ REMAP(116, 117);
}
break;
case ButtonPress:

The gist of this patch is that if it sees an arrow key with the “Super” modifier from the X Server, it converts it to the respective Home/End/PgUp/PgDown key and strip the “Super” modifier. Now everything works!

--

--

Responses (1)