Tablet Mode KDE Plasma 6 with Lenovo Yoga 900-13isk

Hi guys.

Because i struggeled to get tablet mode working with my old 2in1 Lenovo Yoga 900-13isk, i thought posting this, will maybe help others out to get it working.

What was working and not:

  • after flipping the device to tablet mode, touchpad disabling autoamtically, but not enables it itself
  • no tablet mode (bigger taskbar / window icons)
  • no virtual keyboard
  • no auto rotation

What i have done to fix those issues:

Touchpad key on keyboard

Used this fix from here to get the touchpad button fixed in hwdb https://wiki.archlinux.org/title/Lenovo_IdeaPad_Yoga_900

Maybe your keycode differs, you can check them with various tools listed in the documentation, like: libinput debug-events

sudo nano /etc/udev/hwdb.d/90-lenovo-yoga.hwdb

Contents:

#Lenovo YOGA 900-13IS
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrLenovoYOGA900*
KEYBOARD_KEY_bf=f21 # Fn+F6 Disable Touchpad

After a reboot, the touchpad key was working.

Auto rotation

To get auto rotation working as described in the link above, i had to install iio-sensor-proxy with:

sudo pacman -S iio-sensor-proxy

After a reboot it was enabled under Settings → Display → Auto rotate
I disabled “Just in tablet mode“, because i wanted to have rotation working all the time.

I had to use older 3.5-2 version and ignore updates (like newest one 3.8-1) because of an bug that it doesnt work after standby.

Virtual keyboard

To be able to switch virtual keyboard on/off automatically with the following script (see Tablet mode) you have to set virtual keyboard to “plasma” under Settings → keyboard → virtual keyboard

Tablet mode

Tablet mode is currently not supported in kernel for my device, so switching was not working.
I had to write a script which monitors the accelerometer sensor, like so:

[user@eos-yoga900 ~]$ monitor-sensor
Waiting for iio-sensor-proxy to appear
+++ iio-sensor-proxy appeared
=== Has accelerometer (orientation: normal)
=== Has ambient light sensor (value: 10,000000, unit: lux)
=== No proximity sensor
Accelerometer orientation changed: bottom-up
Light changed: 5,000000 (lux)
Light changed: 1,000000 (lux)
Accelerometer orientation changed: normal
Light changed: 10,000000 (lux)

Where bottom-up is flipped and normal = not flipped.

And switch tablet mode, touch pad and virtual keyboard accordingly.
You have todo the following for every user and it does not require root/sudo after setting this up.

They needed to be two seperate scripts, otherwise it would not work correctly and i could not figure out why.

mkdir ~scripts/tablet_mode/
nano ~scripts/tablet_mode/tablet_mode_monitor.py

#!/usr/bin/env python3

import subprocess
import sys
import shlex
from pathlib import Path

TABLET_ON_ORIENTATIONS = {
    "bottom-up",
    "left-up",
    "right-up",
    "top-up",
}

SCRIPT_DIR = Path(__file__).resolve().parent
SETTER = SCRIPT_DIR / "tablet_mode_set.py"

def set_virtual_keyboard(enabled: bool):
    value = "true" if enabled else "false"
    subprocess.run([
        "qdbus6",
        "org.kde.KWin",
        "/VirtualKeyboard",
        "org.freedesktop.DBus.Properties.Set",
        "org.kde.kwin.VirtualKeyboard",
        "enabled",
        value
    ], check=False)

def set_touchpad(enabled: bool):
    print(f"[touchpad] requested -> {'ON' if enabled else 'OFF'}")

    try:
        res = subprocess.run(
            [
                "qdbus6",
                "org.kde.KWin",
                "/org/kde/KWin/InputDevice",
                "org.freedesktop.DBus.Properties.Get",
                "org.kde.KWin.InputDeviceManager",
                "devicesSysNames",
            ],
            capture_output=True,
            text=True,
        )

        devices = [l.strip() for l in res.stdout.splitlines() if l.strip()]

        print(f"[touchpad] devices: {devices}")

        for d in devices:
            print(f"[touchpad] checking {d}")

            name_res = subprocess.run(
                [
                    "qdbus6",
                    "org.kde.KWin",
                    f"/org/kde/KWin/InputDevice/{d}",
                    "org.freedesktop.DBus.Properties.Get",
                    "org.kde.KWin.InputDevice",
                    "name",
                ],
                capture_output=True,
                text=True,
            )

            name = name_res.stdout.strip()
            print(f"[touchpad] name: {name}")

            if any(x in name for x in ("Touchpad", "Synaptics", "ELAN", "MSFT")):
                print(f"[touchpad] MATCH -> {name}")

                subprocess.run(
                    [
                        "qdbus6",
                        "org.kde.KWin",
                        f"/org/kde/KWin/InputDevice/{d}",
                        "org.freedesktop.DBus.Properties.Set",
                        "org.kde.KWin.InputDevice",
                        "enabled",
                        "true" if enabled else "false",
                    ],
                    check=False,
                )

                print(f"[touchpad] set {d} -> {'true' if enabled else 'false'}")

    except Exception as e:
        print(f"[touchpad] EXCEPTION: {e}")


def main():
    current_state = None

    subprocess.call([str(SETTER), "off"])
    set_virtual_keyboard(False)
    set_touchpad(True)

    proc = subprocess.Popen(
        ["monitor-sensor"],
        stdout=subprocess.PIPE,
        stderr=subprocess.DEVNULL,
        text=True,
        bufsize=1,
    )

    for line in proc.stdout:
        if "Accelerometer orientation changed:" not in line:
            continue

        orientation = line.split(":")[-1].strip()
        desired = "on" if orientation in TABLET_ON_ORIENTATIONS else "off"

        if desired != current_state:
            subprocess.call([str(SETTER), desired])

            if orientation == "bottom-up":
                set_virtual_keyboard(True)
            elif orientation == "normal":
                set_virtual_keyboard(False)
                set_touchpad(True)

            print(f"TabletMode -> {desired} ({orientation})")
            current_state = desired

if __name__ == "__main__":
    main()

Save it with CTRL-X →Y →Enter

nano scripts/tablet_mode/tablet_mode_set.py

#!/usr/bin/env python3

import sys
import subprocess
import gi

gi.require_version('Gio', '2.0')
from gi.repository import Gio, GLib

KDE_VERSION = 6
OBJECT_PATH = '/kwinrc'
INTERFACE_NAME = 'org.kde.kconfig.notify'
SIGNAL_NAME = 'ConfigChanged'

if len(sys.argv) != 2 or sys.argv[1] not in ("on", "off"):
    print("Usage: set_tablet_mode.py on|off", file=sys.stderr)
    sys.exit(1)

mode = sys.argv[1]

current_mode = subprocess.check_output(
    [
        f"kreadconfig{KDE_VERSION}",
        "--file", "kwinrc",
        "--group", "Input",
        "--key", "TabletMode",
        "--default", "auto",
    ],
    text=True
).strip()

if current_mode == mode:
    sys.exit(0)

subprocess.check_call(
    [
        f"kwriteconfig{KDE_VERSION}",
        "--file", "kwinrc",
        "--group", "Input",
        "--key", "TabletMode",
        mode,
    ]
)

connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
Gio.DBusConnection.emit_signal(
    connection,
    None,
    OBJECT_PATH,
    INTERFACE_NAME,
    SIGNAL_NAME,
    GLib.Variant.new_tuple(
        GLib.Variant('a{saay}', {'Input': [b'TabletMode']})
    )
)

Save it with CTRL-X →Y →Enter

Make it executable:

sudo chmod +x ~/scripts/tablet_mode/*.py

Test it manually with:

scripts/tablet_mode/tablet_mode_set.py on

scripts/tablet_mode/tablet_mode_set.py off

If that works, install it as service.

Optional, for other users (ex.: otheruser):

sudo cp /home/myuser/scripts/tablet_mode/tablet_mode_monitor.py
/home/otheruser/scripts/tablet_mode/tablet_mode_monitor.py

sudo chown otheruser:otheruser
/home/otheruser/scripts/tablet_mode/*.py

Make it exeutable for all users:

sudo chmod +x /home/*/scripts/tablet_mode/tablet_mode_monitor.py

login with every user and do:

mkdir -p /home/julian/.config/systemd/user
nano ~/.config/systemd/user/tablet_mode.service

[Unit]
Description=Tablet Mode Monitor Service
After=graphical.target

[Service]
Type=simple
ExecStart=/home/%u/scripts/tablet_mode/tablet_mode_monitor.py
Restart=always
RestartSec=5
WorkingDirectory=/home/%u/scripts/tablet_mode
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=default.target

Replace %u with actual user name.
Save it with CTRL-X →Y →Enter

# reload systemd
systemctl --user daemon-reload

# start service
systemctl --user start tablet_mode.service

# check if it runs
systemctl --user status tablet_mode.service

# run automatically on login
systemctl --user enable tablet_mode.service

Now all should work.

P.S.: if not maybe you have to adapt the scripts to your hardware

Hope this helps