How to configure the F9 Fn (display switch) key?

Hey everyone,

So I don’t know if this applies to the Framework 16 laptop, but please let us know if there are any differences between laptops and KB layouts. I use the Framework 13 with the ANSI US keyboard layout.

I want to preface that I’m a DIY kind of person when it comes to Linux, and I even go as far as setting up my own ACPI key events with acpid so that my keys will work in the actual Linux pseudo-TTY console, without a desktop running. Well… at least until systemd integrates more ACPI controls (besides power button and laptop lid events) in their massively complex init system. :laughing:

So I assume this key on F9 has to do something with screen display output switching, which is common for laptops. But this only seems useful if the laptop has at least one HDMI module, or can support DisplayPort Alt mode over USB-C.

I also had a Librem 13 before I bought the Framework, and there’s a useful key on the Librem line of laptops called a screen blanking key. If it’s pressed, it blanks the screen, and can be undone by pressing the key combo again. Seems pretty useful and self-explanatory!

But I wonder if there is a way to change how this key behaves? For example, what if I want to choose between cloning the display, or extending the display? I assume in the officially supported distros like Ubuntu and Fedora, this can be entirely configured with the default desktop GUI software.

But for us that use community-supported distros and prefer the terminal and i3/Sway mostly, our options seem more limited. This key seems to do nothing by default if the desktop or WM does not support it.

But anyway, I’ve done some slight digging at least. When I open a TTY console, and use showkeys -k and showkeys -s as root and press the F9 Fn key, here is what I get:

# showkeys -k
...
keycode 125 press
keycode  25 press
keycode  25 release
keycode 125 release
...
# showkeys -s
...
0xe0 0x5b 0x19
0x99 0xe0 0xdb
...

I’m not a Linux expert, so I don’t know how to decipher raw kbd keycodes, But the first ones are readable to me.

Apparently there are docs here that can be read - Keyboard scancodes
Arch Wiki page for Keyboard input - Keyboard input - ArchWiki
Arch Wiki page for configuring the keyboard for the Linux console - Linux console/Keyboard configuration - ArchWiki

When I use apcid -f -l and press the key combo, these strings shows up: ^X@sp

Next, on Xorg when I use xev, this output is displayed:

KeyPress event, serial 35, synthetic NO, window 0x1e00001,                                                                                                                        
    root 0x3cb, subw 0x0, time 11834460, (65,157), root:(1416,1053),                                                                                                              
    state 0x0, keycode 133 (keysym 0xffeb, Super_L), same_screen YES,                                                                                                             
    XLookupString gives 0 bytes:                                                                                                                                                  
    XmbLookupString gives 0 bytes:                                                                                                                                                
    XFilterEvent returns: False                                                                                                                                                   
                                                                                                                                                                                  
KeyPress event, serial 35, synthetic NO, window 0x1e00001,                                                                                                                        
    root 0x3cb, subw 0x0, time 11834460, (65,157), root:(1416,1053),                                                                                                              
    state 0x40, keycode 33 (keysym 0x70, p), same_screen YES,                                                                                                                     
    XLookupString gives 1 bytes: (70) "p"                                                                                                                                         
    XmbLookupString gives 1 bytes: (70) "p"                                                                                                                                       
    XFilterEvent returns: False                                                                                                                                                   
                                                                                                                                                                                  
KeyRelease event, serial 35, synthetic NO, window 0x1e00001,                                                                                                                      
    root 0x3cb, subw 0x0, time 11834604, (65,157), root:(1416,1053),                                                                                                              
    state 0x40, keycode 33 (keysym 0x70, p), same_screen YES,                                                                                                                     
    XLookupString gives 1 bytes: (70) "p"                                                                                                                                         
    XFilterEvent returns: False                                                                                                                                                   
                                                                                                                                                                                  
KeyRelease event, serial 35, synthetic NO, window 0x1e00001,                                                                                                                      
    root 0x3cb, subw 0x0, time 11834604, (65,157), root:(1416,1053),                                                                                                              
    state 0x40, keycode 133 (keysym 0xffeb, Super_L), same_screen YES,                                                                                                            
    XLookupString gives 0 bytes:                                                                                                                                                  
    XFilterEvent returns: False  

Okay, now we’re getting somewhere. It says it is Super+P key combo. Interesting… I don’t know what Super+P does on most desktops, but after looking it up, yes, it is meant to cycle through desktop displays, but this seems to be a hardware-coded key, not ACPI, if it is sending a key combo, instead of a single keycode. It would be useful to have it be a remapped as a single key, maybe.

Finally, on Xorg/Wayland, using libinput debug-events (from installing the libinput-tools package on Debian), this output is shown:

# libinput debug-events
...
 event0   KEYBOARD_KEY            +0.000s       KEY_LEFTMETA (125) pressed
 event0   KEYBOARD_KEY            +0.000s       *** (-1) pressed
^X@sp event0   KEYBOARD_KEY            +0.075s  *** (-1) released
 event0   KEYBOARD_KEY            +0.075s       KEY_LEFTMETA (125) released
...

It prints out ^X@sp instead of the expected p that would be the case if it was a key macro, but the HW key integrates it as one thing.

So okay, I guess one solution would be to map Super+P keyboard combo to the same thing if the key mapper of choice supports combos. Otherwise, it’s not going to work if it only supports one keycode.

Let me know if anyone else has dug into this more. I’d like to maybe have this key show a menu to change between like: “Turn screen off”, “clone display output”, or “switch display output”.

Probably possible, right?

1 Like

Yeah, it’s completely different on the FWL16. All keyboards there have their own attached controllers running QMK firmware. And Display (F9), or any key for that matter, can be changed through a point-and-click GUI by going to keyboard.frame.work with Chrome or a Chromium-based browser.

Whereas the FWL13 handles the keyboard like a normal laptop. The keyboard’s raw keymatrix is connected to the mainboard where the EC (Embedded Controller) processes key presses. If you haven’t seen it yet, there is a thread on remapping keys in the EC. Changed my keyboard layout in hardware to colemak!. It has a fairly easy command that will tell the EC to remap a key (until next boot). Trouble is, for the F# keys where there is an alt function & a main F#, I haven’t seen anyone find a command or easy way to separate the alt function. E.g. you can remap Display, But F9 will come with it.

Now the EC firmware has been open sourced by Framework, and is on their github. So one could edit it to move an F# alt function separately. But that’s more work. And there is always a risk of bricking your EC if you make a serious mistake. If your EC is messed up, your laptop won’t boot. In that case, you’ll need to reflash the EC’s flash chip with an external programmer. It sounds bad, but it can be not hard for someone inclined towards electronics and who has a steady hand for precise work. You need a USB flash programmer, which is a couple dollars to $15 depending on where you get it. And a pogo pin jig, which you’ll need to hold to the chip, about $16 on aliexpress. So, $20-30 in all. How to prepare for reflashing firmware - #24 by knutaf

1 Like

Fn+F9 is mapped to Super+P, yes. For Framework Laptop 13, the relevant code is at a different location depending on generation:

Hacking your Framework Laptop's EC for fun and profit :: HowettNET is a helpful blog post on modifying the EC code. It only covers hx20 and hx30; it mostly also applies to azalea, but the toolchain setup is a bit different and I’m not sure it’s properly documented. I assume marigold is roughly the same as azalea but I’m not sure if anyone outside Framework has actually tried building the EC firmware for it yet.

If you just want to make it emit a different set of scancodes it should be fairly clear how to do that. Properly making it correspond to the XF86Display keysym would be ideal but is more difficult. The corresponding HID control is “System Display Toggle Int/Ext Mode”, but you’d have to modify the descriptors and reproduce the hid_consumer logic yourself since that’s in the Generic Desktop Controls page as opposed to Consumer Controls.

If you go ahead with the latter, I know there’s other people who would be interested too. Perhaps you could even PR it upstream? I don’t know why they wouldn’t accept it, though admittedly I don’t know for sure that Windows handles that control equivalently to Super+P.

3 Likes

Wait, I think we might have both misunderstood your request. Is this just about having a Windows/KDE/etc-style Super+P on i3 or sway? If so, I’m not sure there’s a drop-in tool for this. This blog post seems to offer a workable custom solution: New laptop part 6: Managing multi screens with i3wm and autorandr | Bacardi55's Web Cave

For the underlying display configuration that script uses autorandr, which is an X11-specific tool. I believe kanshi is a similar tool for wlroots-based Wayland compositors like sway, and you may wish to combine it with something like wl-mirror for screen mirroring functionality (see Output mirroring and complex layout configurations · Issue #1666 · swaywm/sway · GitHub).

1 Like