Skip to content

Instantly share code, notes, and snippets.

@andrejcremoznik
Last active April 12, 2024 07:09
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save andrejcremoznik/e56234138305226abd41fe4d1d2561a3 to your computer and use it in GitHub Desktop.
Save andrejcremoznik/e56234138305226abd41fe4d1d2561a3 to your computer and use it in GitHub Desktop.
Logitech K780 switch media and function keys on linux

Media/function keys on K780

K780 doesn't have a hard switch to lock the function keys. Logitech provides a utility to do this on Windows and iOS but not on Linux. You need to manually remap the keys.

Below works for Arch Linux, other systemd based distros should be about the same.

There's a problem with the F1-F3 keys as they're hardware specific and don't emit an event if pressed on their own and therefore can't be remapped. I might be wrong as I haven't spend any time on researching that.

Edit: about htat, see @tangruize comments below https://gist.github.com/andrejcremoznik/e56234138305226abd41fe4d1d2561a3#gistcomment-3390489

Create file /etc/udev/hwdb.d/95-k780.hwdb

# Logitech K780 map:
# Fn + F4/F5/F6 ==> F4/F5/F6
evdev:input:b0003v046Dp405B*
  KEYBOARD_KEY_c0223=f4
  KEYBOARD_KEY_70065=f5
  KEYBOARD_KEY_c0224=f6

Rebuild hwdb.bin and apply new keys

sudo systemd-hwdb update
sudo udevadm trigger

Make permanent

Make sure the ConditionNeedsUpdate=/etc is commented out in /usr/lib/systemd/system/systemd-hwdb-update.service. If it isn't create an override:

sudo systemctl edit --full systemd-hwdb-update.service

Comment out:
#ConditionNeedsUpdate=/etc

This creates a replacement systemd unit file in /etc/systemd/system/systemd-hwdb-update.service. If some update breaks anything related to this, remove it and recreate with systemctl edit like before.

Read more

@Phaestion
Copy link

Awesome gist! How did you convert the scancodes to keyboards_key? I read the Arch wiki links you posted but couldn't figure out how to do it.
I actually want to map Fn + F4 to Fn - F12 to F4 to F12.

@BlueVette
Copy link

Thank you for your information above. I was able to re-map the keys using the bluetooth (not the unifying receiver usb) connection. I did a full swap of the FN and media keys (F1/2/3 couldn't be done). Maybe the mappings below will help others.

# Logitech K780 map:
# Swapping the function keys and media keys
evdev:input:b0005v046DpB33Be0028*
  KEYBOARD_KEY_c0223=f4
  KEYBOARD_KEY_70065=f5
  KEYBOARD_KEY_c0224=f6
  KEYBOARD_KEY_c00b6=f7
  KEYBOARD_KEY_c00cd=f8
  KEYBOARD_KEY_c00b5=f9
  KEYBOARD_KEY_c00e2=f10
  KEYBOARD_KEY_c00ea=f11
  KEYBOARD_KEY_c00e9=f12
#Mapping old FN keys back to media keys  
  KEYBOARD_KEY_7003d=homepage
  KEYBOARD_KEY_7003e=compose
  KEYBOARD_KEY_7003f=back
  KEYBOARD_KEY_70040=previoussong
  KEYBOARD_KEY_70041=playpause
  KEYBOARD_KEY_70042=nextsong
  KEYBOARD_KEY_70043=mute
  KEYBOARD_KEY_70044=volumedown
  KEYBOARD_KEY_70045=volumeup

@majenkotech
Copy link

I have just started reverse engineering the protocol used to control these keyboards since I got one today. Disappointed at the lack of support I decided to dig out my USB debugging hardware and get to work.

After less than an hour I have a proof of concept program working.

It is very very crude (as I say it's just a proof of concept) but it does work (for me at least).

@arkadius
Copy link

arkadius commented Oct 4, 2019

@BlueVette's list can be extended by this mappings if someone want to have easier access to insert key:

 KEYBOARD_KEY_c0221=insert
 KEYBOARD_KEY_70049=search

@youssoupha
Copy link

For information if you are using ubuntu the hwdb.d is not located in /usr/lib/udev/hwdb.d but in /etc/udev/hwdb.d

@HoinzeyBear
Copy link

Where do you source the input variable from ?

@andrejcremoznik
Copy link
Author

Where do you source the input variable from ?

See https://wiki.archlinux.org/index.php/Keyboard_input#Identifying_keycodes

I'll update the links that have changed since I've posted this.

@HoinzeyBear
Copy link

I was able to get around this my connecting the keyboard to my Windows machine, downloaing the logitech software, updating for the Function keys to be default

@adriankontny
Copy link

I have found a solution for those, who just want F* keys on their K780 back on:

$ sudo add-apt-repository ppa:solaar-unifying/ppa
$ sudo apt-get update

$ apt list -a solaar
Listing... Done
solaar/bionic,bionic 1.0.1+dfsg-3+git940-624247d-202004052005~ubuntu18.04.1 all [residual-config]
solaar/bionic,bionic,now 0.9.2+dfsg-8 all [residual-config]

$ sudo apt-get install solaar=1.0.1+dfsg-3+git940-624247d-202004052005~ubuntu18.04.1

$ solaar config 1
Wireless Multi-Device Keyboard K780 (K780) [XXXX:XXXXXXXX]

# Swap Fx function
# When set, the F1..F12 keys will activate their special function, and you must hold the FN key to activate their standard function.  When unset, the F1..F12 keys will activate their standard function, and you must hold the FN key to activate their special function.
#   possible values: on/true/t/yes/y/1 or off/false/f/no/n/0
fn-swap = True

$ solaar config 1 fn-swap "false"

@adriankontny
Copy link

@HoinzeyBear, is your keyboard K780? Are you able to reproduce? For me, switching on Windows (in logitech options) didn't help after going back to linux.

@HoinzeyBear
Copy link

@adriankontny Yes I was having the same issue, I was trying to fix this on my Kubuntu machine hence my comment on this thread back in March. Connecting the K780 to Logitech Options found here: https://www.logitech.com/en-us/product/options and swapping from the default of media buttons to instead using FN keys did also apply when I connected the keyboard to my Kubuntu machine.
Hoping the best for you

@sean1985zc
Copy link

this doesn't work for me, unfortunately

@adriankontny Yes I was having the same issue, I was trying to fix this on my Kubuntu machine hence my comment on this thread back in March. Connecting the K780 to Logitech Options found here: https://www.logitech.com/en-us/product/options and swapping from the default of media buttons to instead using FN keys did also apply when I connected the keyboard to my Kubuntu machine.
Hoping the best for you

@adriankontny
Copy link

@sean1985zc

$ sudo add-apt-repository ppa:solaar-unifying/ppa
$ sudo apt-get update
$ sudo apt upgrade solaar

There is 1.0.2-RC, which has an actual graphical interface for K780, cheers!

@HootenWeb
Copy link

Here's hoping @BlueVette or someone out there can help a frustrated noob out. I am trying to change my Fkeys on my K780 which I have connected via bluetooth on ArchLinux.

Following the above instructions isn't working for me. I have a feeling that i have the evdev:input address incorrect.

When I run evemu-describe I get: Input device ID: bus 0x03 vendor 0x46d product 0x405b version 0x111

Which I am decoding to be: evdev:input:b003v046Dp0405Be0111*

I am then putting this in my /etc/udev/hwdb.d/95-k780.hwdb file and rebuild as @andrejcremoznik describes in his OG post but there is no change to the function of my keyboard.

I have also tried: evdev:input:b0005v046Dp405Be0111* to match @BlueVette BusID, no joy either.

I have also tried: evdev:input:b0003v046Dp405Be0111* for S's and G's...

What am I not seeing or missing?

Thanks in advance.

~Webster

@arkadius
Copy link

@HootenWeb have you tried with evdev:input:b0003v046Dp405B*? Also have you invoked sudo systemd-hwdb update && sudo udevadm trigger after changes of content? Maybe location of udev rule file is incorrect. On Ubuntu 20.04 it is located at /usr/lib/udev/hwdb.d/

@arkadius
Copy link

If someone is interested here is also config for Logitech K380:

# Logitech K380 map:
# Swapping the function keys and media keys
evdev:input:b0005v046DpB342*
  KEYBOARD_KEY_c0223=f4
# Unfortunately F5 is handle as a combination of alt-tab so it can be handled that way - use FN-F5 combination instead
  KEYBOARD_KEY_70065=f6
  KEYBOARD_KEY_c0224=f7
  KEYBOARD_KEY_c00b6=f8
  KEYBOARD_KEY_c00cd=f9
  KEYBOARD_KEY_c00b5=f10
  KEYBOARD_KEY_c00e2=f11
  KEYBOARD_KEY_c00ea=f12
  KEYBOARD_KEY_c00e9=insert
#Mapping old FN keys back to media keys  
  KEYBOARD_KEY_7003d=homepage
  KEYBOARD_KEY_7003f=compose
  KEYBOARD_KEY_70040=back
  KEYBOARD_KEY_70041=previoussong
  KEYBOARD_KEY_70042=playpause
  KEYBOARD_KEY_70043=nextsong
  KEYBOARD_KEY_70044=mute
  KEYBOARD_KEY_70045=volumedown
  KEYBOARD_KEY_70049=volumeup

@tangruize
Copy link

I spent some time to research this problem. F1 and F3 are frequently used but key map doesn't support to invert it. And I think @HoinzeyBear's method won't work. @adriankontny's method is useful if you use a Logitech unifying receiver. However, Solaar doesn't support Bluetooth. I learned how to analyze the communication to the HID device. And here is my workaround:

SCRIPT_FILE=/usr/local/bin/logi-fn-swap.pl
vim $SCRIPT_FILE

#!/usr/bin/perl

# Add K780. Related resources:
## https://askubuntu.com/questions/326959/how-can-i-make-the-function-keys-the-default-on-a-logitech-k760-bluetooh-keyboar
## http://www.trial-n-error.de/posts/2012/12/31/logitech-k810-keyboard-configurator
## https://www.spinics.net/lists/linux-input/msg24280.html
## https://gist.github.com/andrejcremoznik/e56234138305226abd41fe4d1d2561a3

use strict;
use warnings;

use constant HIDIOCGRAWINFO         => 2148026371;
#use constant BUS_BLUETOOTH          =>          5;
use constant HID_VENDOR_ID_LOGITECH =>       1133;
use constant HID_DEVICE_ID_K760     =>     -19690;
use constant HID_DEVICE_ID_K760_ALT =>     -19688;
use constant HID_DEVICE_ID_K810     =>     -19687;
use constant HID_DEVICE_ID_K780     =>     -19653;
use constant HID_DEVICE_ID_K780_USB =>      16475;

my %message = (
    HID_DEVICE_ID_K760() => {
        on   => (pack "C*", 0x10, 0xff, 0x05, 0x14, 0x00, 0x00, 0x00),
        off  => (pack "C*", 0x10, 0xff, 0x05, 0x14, 0x01, 0x00, 0x00),
    },
    HID_DEVICE_ID_K760_ALT() => {
        on   => (pack "C*", 0x10, 0xff, 0x05, 0x14, 0x00, 0x00, 0x00),
        off  => (pack "C*", 0x10, 0xff, 0x05, 0x14, 0x01, 0x00, 0x00),
    },
    HID_DEVICE_ID_K780() => {
        on   => (pack "C*", 0x11, 0xff, 0x0c, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
        off  => (pack "C*", 0x11, 0xff, 0x0c, 0x1d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
    },
    HID_DEVICE_ID_K780_USB() => {
        on   => (pack "C*", 0x11, 0xff, 0x0c, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
        off  => (pack "C*", 0x11, 0xff, 0x0c, 0x1d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
    },
    HID_DEVICE_ID_K810() => {
        on   => (pack "C*", 0x10, 0xff, 0x06, 0x15, 0x00, 0x00, 0x00),
        off  => (pack "C*", 0x10, 0xff, 0x06, 0x15, 0x01, 0x00, 0x00),
    },
);

#die
#   "usage: $0 [on|off]\n",
#   "\ton  makes the media keys the default\n",
#   "\toff makes the function keys the default\n"
#unless @ARGV == 1 and my ($choice) = $ARGV[0] =~ /^(on|off)$/;
my ($choice) = @ARGV ? $ARGV[0] =~ /^(on|off)$/ : "off";

my $device;

# find the first device we can set the option on
# TODO: add a parameter to directly specify a device
# TODO: add a parameter to make it set all devices
FILE_SEARCH:
for my $file (</sys/class/hidraw/hidraw*/device/uevent>) {
    open my $fh, "<", $file or do {
        warn "could not open $file: $!\n";
        next;
    };

    while (<$fh>) {
        if (/HID_NAME=(Logitech|Keyboard) K(76|78|81)0/) {
            my ($hid_raw_name) = $file =~ m{(hidraw[^/]+)};
            $device = "/dev/$hid_raw_name";
            last FILE_SEARCH;
        }
    }
}

die "sorry, could not find a suported device on your machine\n" unless $device;

# re-exec with sudo if we can't open the device
unless (-r $device and -w $device) {
    # unless we are already root
    exec "sudo", $^X, $0, @ARGV unless $> == 0;
}

open my $dev, "+<", $device or die "could not open device $device: $!\n";

my $success = ioctl $dev, HIDIOCGRAWINFO, my $struct = "";

die "could not determine if $device is supported\n" unless $success;

my ($bus_type, $vendor, $product) = unpack "Lss", $struct;

#die "detected device $device is not a Bluetooth device\n"
#    unless $bus_type == BUS_BLUETOOTH;

die "detected device $device is not a Logitech product\n"
    unless $vendor == HID_VENDOR_ID_LOGITECH;

die "detected device $device is not a supported product\n"
    unless exists $message{$product};

syswrite $dev, $message{$product}{$choice};

close $dev;

chmod +x $SCRIPT_FILE

Now, connect your K780 (via Bluetooth or Logitech unifying USB receiver) and check if this script works:

$SCRIPT_FILE on

It should work if you use a K780/K760/K810 keyboard (but only K780 supports unifying USB). If it works, you can add an action to udev:

vim /etc/udev/rules.d/00-k780.rules

SUBSYSTEM=="hidraw", ACTION=="add", RUN+="$SCRIPT_FILE on"

You should replace $SCRIPT_FILE with your path. Now restart udev:

sudo service udev restart

It should work automatically when you disconnect and reconnect. Enjoy!

@tangruize
Copy link

tangruize commented Jul 27, 2020

The $SCRIPT_FILE can be simplified:

#!/bin/bash

set -euo pipefail

if grep -q K780 /sys/class/hidraw/$1/device/uevent; then
    echo -en '\x11\xff\x0c\x1d' | cat - /dev/zero | dd of=/dev/$1 count=20 iflag=count_bytes oflag=nonblock
fi

And modify /etc/udev/rules.d/00-k780.rules:

SUBSYSTEM=="hidraw", ACTION=="add", RUN+="$SCRIPT_FILE $name"

$SCRIPT_FILE can substitute for your script location, like /usr/local/bin/k780-fn-on.sh. $name should remain unchanged.

@umarhussain88
Copy link

bit late to the party, but what worked for me was using the receiver and changing the settings via a windows device with Logitech options. So stupid there isn't a hard key to change.

@aphirst
Copy link

aphirst commented Jul 5, 2021

I just wanted to thank all of you for this excellent and informative thread. I am currently using the simplified script by @tangruize and so far it is working perfectly with my new K780.

@tangruize
Copy link

If you are using a unifying USB receiver and do not want to install solaar, here is a simplified udev rule that can swap FN keys for K780:

/etc/udev/rules.d/00-k780.rules:

ACTION=="change", SUBSYSTEM=="power_supply", ATTR{model_name}=="K780 Multi-Device Wireless Keyboard", ATTR{online}=="1", RUN+="/bin/bash -c 'printf \"\x11\xff\x0c\x1d\" | cat - /dev/zero | dd of=/dev/$$(ls $$0/device/hidraw) count=20 iflag=count_bytes oflag=nonblock' %S%p"

After editing the above udev file, run sudo udevadm trigger to enable to rule.

Reference: https://github.com/sta-c0000/logitech-keyboard-udev-rules

@Roman2K
Copy link

Roman2K commented Jul 17, 2022

@tangruize Thanks for this concise solution and all the work that lead to it. Working perfectly for me!

I created an Arch PKGBUILD for it: Roman2K/arch-k780-fnkey

@tangruize
Copy link

The $SCRIPT_FILE can be simplified:
...

SUBSYSTEM=="hidraw", ACTION=="add", RUN+="$SCRIPT_FILE $name"

I found that it doesn't work sometimes (for bluetooth), and I improved it:

Delete the above rule and script file, and append below contents to /etc/udev/rules.d/00-k780.rules:

ACTION=="bind", KERNEL=="0005:046D:B33B*", SUBSYSTEM=="hid", RUN+="/bin/bash -c 'printf \"\x11\xff\x0c\x1d\" | cat - /dev/zero | dd of=/dev/$$(ls $$0/hidraw) count=20 iflag=count_bytes oflag=nonblock' %S%p"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment