Did you know you can convert a Chromebook into a Linux laptop? This is a great option for older Chromebooks that Google no longer supports, or if you've outgrown ChromeOS and need a more full-featured operating system.
But what about all those special keys at the top of the keyboard?
This script maps Chromebook function keys to system actions on Linux (any X11 + Cinnamon + Debian-based distro like Linux Mint).
In most cases, running it with --all is all you need. Then reboot and test the keys.
#!/bin/sh
# chromebook-fkey-setup.sh
# Maps Chromebook function keys to system actions on Linux Mint (Cinnamon).
# Run once after a fresh install. Safe to re-run.
#
# Usage: chromebook-fkey-setup.sh [OPTIONS]
# Options:
# --brightness Set up screen brightness (udev rule + video group)
# --xkb Write xkb symbols file (requires sudo) and apply to current session
# --apply-xkb Apply xkb remap to current session only (no sudo, used by autostart)
# --xbindkeys Write xbindkeys config and launch daemon
# --autostart Install ~/.config/autostart entries for persistence
# --all Run all setup functions
# --help Show this help message
set -e
SCRIPT_NAME="$(basename "$0")"
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
XKB_SYMBOLS_FILE="/usr/share/X11/xkb/symbols/chromebook"
UDEV_RULES_FILE="/etc/udev/rules.d/90-backlight.rules"
XBINDKEYS_CFG="$HOME/.xbindkeysrc.chromebook"
LOCK_SCREEN_HELPER="$HOME/bin/lock-screen-fkey.sh"
AUTOSTART_XKB="$HOME/.config/autostart/chromebook-fkeys-xkb.desktop"
AUTOSTART_XBINDKEYS="$HOME/.config/autostart/chromebook-fkeys-xbindkeys.desktop"
# ----------------------------------------------------------------------------
# Exit with install hint if a required command is missing.
check_cmd() {
command -v "$1" > /dev/null 2>&1 && return 0
echo "ERROR: $1 is not installed. Install with: sudo apt install $2" >&2
exit 1
}
check_deps() {
check_cmd xbindkeys xbindkeys
check_cmd wmctrl wmctrl
check_cmd xdotool xdotool
}
# ----------------------------------------------------------------------------
usage() {
GRAY='\033[38;5;240m'
MGRAY='\033[38;5;246m'
YLLOW='\033[38;5;226m'
WHITE='\033[1;37m'
RESET='\033[0m'
printf "Usage: %s [OPTIONS]\n" "$SCRIPT_NAME"
cat <<EOF
Options:
--brightness Write backlight udev rule, add user to video group (sudo)
--xkb Write xkb symbols file (sudo) and apply to current session
--apply-xkb Apply xkb remap to current session only (no sudo, used by autostart)
--xbindkeys Write xbindkeys config and start daemon
--autostart Install ~/.config/autostart entries for xkb and xbindkeys
--all Run all of the above
--help Show this help message
Key map:
EOF
printf " F1 <- back\n"
printf " F2 -> forward\n"
printf " F3 @> reload\n"
printf " F4 ☐ maximize/restore\n"
printf " F5 ☐‖ overview\n"
printf " F6 ${GRAY}*${RESET} dim\n"
printf " F7 ${YLLOW}*${RESET} bright\n"
printf " F8 ${GRAY}◄)${RESET} mute\n"
printf " F9 ${MGRAY}◄)${RESET} vol down\n"
printf " F10 ${WHITE}◄))${RESET} vol up\n"
printf " F13 [x] lock screen\n"
cat <<EOF
Notes:
- Run --all once after a fresh install.
- Log out and back in after first run for video group membership to take effect.
EOF
}
# ----------------------------------------------------------------------------
# Writes a udev rule so the video group can write to intel_backlight,
# then adds the current user to the video group.
setup_backlight_udev() {
echo "[brightness] Writing udev rule: $UDEV_RULES_FILE"
sudo tee "$UDEV_RULES_FILE" > /dev/null <<'UDEV'
ACTION=="add", SUBSYSTEM=="backlight", KERNEL=="intel_backlight", \
RUN+="/bin/chgrp video /sys/class/backlight/intel_backlight/brightness", \
RUN+="/bin/chmod g+w /sys/class/backlight/intel_backlight/brightness"
UDEV
echo "[brightness] Adding $USER to video group"
sudo usermod -aG video "$USER"
echo "[brightness] Triggering udev for backlight subsystem"
sudo udevadm trigger --subsystem-match=backlight
echo "[brightness] Permissions: $(ls -la /sys/class/backlight/intel_backlight/brightness)"
}
# ----------------------------------------------------------------------------
# Writes the xkb symbols file to disk and applies it to the current session.
# Requires sudo for the write; apply is session-only with no sudo.
#
# Key map:
# F1 (FK01) -> XF86Back browser/app back
# F2 (FK02) -> XF86Forward browser/app forward
# F3 (FK03) -> XF86Launch7 reload (xdotool ctrl+r via xbindkeys)
# F4 (FK04) -> XF86Launch8 maximize/restore (wmctrl via xbindkeys)
# F5 (FK05) -> XF86Launch5 overview (expo via xbindkeys)
# F6 (FK06) -> XF86MonBrightnessDown screen brightness down
# F7 (FK07) -> XF86MonBrightnessUp screen brightness up
# F8 (FK08) -> XF86AudioMute mute toggle
# F9 (FK09) -> XF86AudioLowerVolume volume down
# F10 (FK10) -> XF86AudioRaiseVolume volume up
# FK13 (kc191)-> XF86Launch6 lock screen (lock-screen-fkey.sh via xbindkeys)
#
# XF86Launch5/6/7/8 are used for keys needing custom xbindkeys commands.
# XF86Reload, XF86ScreenSaver, and XF86FullScreen are grabbed by csd-media-keys
# or applications (e.g. Vivaldi), preventing xbindkeys from owning them.
setup_xkb() {
echo "[xkb] Writing xkb symbols: $XKB_SYMBOLS_FILE"
sudo tee "$XKB_SYMBOLS_FILE" > /dev/null <<'XKB'
// Chromebook function key remaps
partial modifier_keys
xkb_symbols "fkeys" {
key <FK01> { [ XF86Back ] };
key <FK02> { [ XF86Forward ] };
key <FK03> { [ XF86Launch7 ] };
key <FK04> { [ XF86Launch8 ] };
key <FK05> { [ XF86Launch5 ] };
key <FK06> { [ XF86MonBrightnessDown ] };
key <FK07> { [ XF86MonBrightnessUp ] };
key <FK08> { [ XF86AudioMute ] };
key <FK09> { [ XF86AudioLowerVolume ] };
key <FK10> { [ XF86AudioRaiseVolume ] };
key <FK13> { [ XF86Launch6 ] };
};
XKB
apply_xkb
}
# ----------------------------------------------------------------------------
# Applies the xkb remap to the current X session. No sudo required.
# Called directly at login via autostart.
#
# Note: sed-pipe approach fails because setxkbmap -print closes with "};" not
# "}", so the substitution never matches. Instead we extract the active symbols
# include string with awk and build a complete keymap heredoc for xkbcomp.
apply_xkb() {
[ -z "$DISPLAY" ] && echo "[xkb] ERROR: DISPLAY not set." >&2 && return 1
[ ! -f "$XKB_SYMBOLS_FILE" ] && echo "[xkb] ERROR: $XKB_SYMBOLS_FILE not found. Run --xkb first." >&2 && return 1
echo "[xkb] Applying remap to current X session"
CURRENT_SYMBOLS="$(setxkbmap -print | awk '/xkb_symbols/ {
match($0, /include "[^"]+"/)
print substr($0, RSTART+9, RLENGTH-10)
}')"
xkbcomp -w0 -I/usr/share/X11/xkb - "$DISPLAY" <<EOF
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete" };
xkb_symbols { include "${CURRENT_SYMBOLS}+chromebook(fkeys)" };
xkb_geometry { include "pc(pc105)" };
};
EOF
echo "[xkb] Done."
}
# ----------------------------------------------------------------------------
# Writes the xbindkeys config and launches the daemon.
# Handles keys that need custom commands:
# XF86Launch5 -> Cinnamon expo toggle (overview, F5)
# XF86Launch6 -> lock-screen-fkey.sh (padlock, F13)
# XF86Launch7 -> xdotool ctrl+r (reload, F3)
# XF86Launch8 -> wmctrl maximize/restore (F4)
#
# Delay notes:
# - 0.1s on expo/lock: Cinnamon silently drops actions fired immediately on keypress.
# - 0.2s on reload/maximize: focused window must reclaim focus before xdotool/wmctrl fires.
#
# lock-screen-fkey.sh is a separate script because the empty string argument '""'
# required by org.cinnamon.ScreenSaver.Lock cannot be reliably quoted in xbindkeys config.
setup_xbindkeys() {
echo "[xbindkeys] Writing lock screen helper: $LOCK_SCREEN_HELPER"
mkdir -p "$(dirname "$LOCK_SCREEN_HELPER")"
cat > "$LOCK_SCREEN_HELPER" <<'LOCK'
#!/bin/sh
gdbus call --session \
--dest org.cinnamon.ScreenSaver \
--object-path /org/cinnamon/ScreenSaver \
--method org.cinnamon.ScreenSaver.Lock \
'""'
LOCK
chmod +x "$LOCK_SCREEN_HELPER"
echo "[xbindkeys] Writing config: $XBINDKEYS_CFG"
cat > "$XBINDKEYS_CFG" <<EOF
"sleep 0.1 && gdbus call --session --dest org.Cinnamon --object-path /org/Cinnamon --method org.Cinnamon.Eval 'Main.expo.toggle();'"
XF86Launch5
"sleep 0.1 && $LOCK_SCREEN_HELPER"
XF86Launch6
"sleep 0.2 && xdotool key --clearmodifiers ctrl+r"
XF86Launch7
"sleep 0.2 && wmctrl -r :ACTIVE: -b toggle,maximized_vert,maximized_horz"
XF86Launch8
EOF
pkill -f "xbindkeys.*chromebook" 2>/dev/null || true
sleep 0.3
echo "[xbindkeys] Starting daemon"
xbindkeys -f "$XBINDKEYS_CFG"
echo "[xbindkeys] Done."
}
# ----------------------------------------------------------------------------
# Writes a desktop file to the autostart directory.
# Usage: write_autostart <path> <name> <comment> <exec>
write_autostart() {
echo "[autostart] Writing: $1"
cat > "$1" <<EOF
[Desktop Entry]
Type=Application
Name=$2
Comment=$3
Exec=$4
X-GNOME-Autostart-Notify=false
X-GNOME-AutoRestart=false
NoDisplay=true
EOF
}
# Installs autostart entries for the xkb remap and xbindkeys daemon.
# OnlyShowIn omitted: Cinnamon reports X-Cinnamon but spec requires value
# without X- prefix, causing silent skip when OnlyShowIn=X-Cinnamon is set.
setup_autostart() {
mkdir -p "$HOME/.config/autostart"
write_autostart "$AUTOSTART_XKB" \
"Chromebook Function Keys (xkb)" \
"Applies xkb key remaps for Chromebook" \
"$SCRIPT_PATH --apply-xkb"
write_autostart "$AUTOSTART_XBINDKEYS" \
"Chromebook Function Keys (xbindkeys)" \
"Starts xbindkeys daemon for Chromebook" \
"$SCRIPT_PATH --xbindkeys"
echo "[autostart] Done."
echo "[autostart] $AUTOSTART_XKB"
echo "[autostart] $AUTOSTART_XBINDKEYS"
}
# ----------------------------------------------------------------------------
run_all() {
setup_backlight_udev
setup_xkb
setup_xbindkeys
setup_autostart
}
# ----------------------------------------------------------------------------
# Entry point
# ----------------------------------------------------------------------------
if [ $# -eq 0 ]; then
usage
exit 0
fi
for arg in "$@"; do
case "$arg" in
--help) usage; exit 0 ;;
--brightness|--xkb|--apply-xkb|--xbindkeys|--autostart|--all) ;;
*) echo "Unknown option: $arg" >&2; usage >&2; exit 1 ;;
esac
done
check_deps
for arg in "$@"; do
case "$arg" in
--brightness) setup_backlight_udev; apply_xkb ;;
--xkb) setup_xkb ;;
--apply-xkb) apply_xkb ;;
--xbindkeys) setup_xbindkeys ;;
--autostart) setup_autostart ;;
--all) run_all ;;
esac
done
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.