Skip to content

Instantly share code, notes, and snippets.

@andrejcremoznik
Last active April 12, 2024 07:09
Show Gist options
  • 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

@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