Learning Swedish with Linux, Sway, and an X1 Yoga tablet

This post is about how I configured a tablet-mode for my Lenovo X1 Yoga so that I can use it in my Swedish classes. It includes details on the configuration of Sway, Waybar, and my own custom Textual-based Swedish-English dictionary. Hopefully it will come useful to other Linux users that would like to use their convertible laptop as a tablet.

Introduction

I’ve been using the X1 Yoga for 7 years now. It’s a well-designed powerful device that’s built to very high standards. Like other ā€œYogaā€ devices, the screen can be folded all the way to the back, and since itā€™s a touch screen, it essentially turns the laptop into one big tablet. Admittedly, I have only used the laptop on ā€œtablet-modeā€ very seldom, usually when reading lengthy articles. As a Linux user using Sway and spending most of my time in the terminal, Iā€™m relying heavily on the keyboard and the whole touch thing felt like it wasnā€™t built for my use case.

But then I started taking Swedish classes. Some people used the physical books, but some just downloaded them off libgen and had it on their tablet. Not only are the two books heavier than my laptop, I also find it more comfortable to read from the laptop in front of me, rather than to hold the book for hours. While the laptop is great for reading, it isnā€™t so great when working with the book: often there are tasks like ā€œunderline the words you donā€™t understandā€, ā€œcross the words that donā€™t fitā€, ā€œdraw a line between X and Yā€ etc. The X1 Yoga has a pen built into it, but it feels weird to draw on the screen when the laptop is in laptop mode. I therefore decided to try to build a functioning ā€œtablet-modeā€ and see how this workflow works for me.

Configuration

Sway receives an event when the screen is opened more than 180Ā°. Iā€™ve used it run some commands that reconfigure my UI according to the status:

~/.config/sway/config

bindswitch tablet:on exec 'notify-send "tablet mode on" -t 1000', \
	exec 'gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true', \
	exec 'bash -c "killall waybar; waybar --config /home/bjesus/.config/waybar/tablet --style /home/bjesus/.config/waybar/tablet.css"', \
	exec 'rot8', \
	exec 'squeekboard', \
	exec 'swaymsg input "1386:21173:Wacom_HID_52B5_Finger" map_to_output "eDP-1"', \
	exec 'swaymsg input "1386:21173:Wacom_HID_52B5_Pen" map_to_output "eDP-1"', \
	exec 'swaymsg "[workspace=^] border pixel 15"'
bindswitch tablet:off exec 'notify-send "tablet-mode off" -t 1000', \
	exec 'gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false', \
	exec 'bash -c "killall waybar; waybar"', \
	exec 'killall rot8', \
	exec 'killall squeekboard', \
	exec 'swaymsg input "1386:21173:Wacom_HID_52B5_Finger" map_to_output "eDP-1"', \
	exec 'swaymsg input "1386:21173:Wacom_HID_52B5_Pen" map_to_output "eDP-1"', \
	exec 'swaymsg "[workspace=^] border pixel 0"'

Useful tools

Iā€™m using squeekboard as my on-screen keyboard. It was quite easy to set up and configure custom layouts, so I created a Colemak Swedish layout. The command gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true is used to automatically show the keyboard whenever a text input field is focused.

rot8 is used to automatically rotate the screen when in tablet mode. I donā€™t need it when using the device as a laptop, so I simply kill the process along with the on-screen keyboard.

Iā€™ve noticed that sometimes the screen and its touch interface can go out of sync, so Iā€™m using the map_to_output command to relink them. The border pixel 15 setting gives a 15px border to all windows, making it easier to resize windows without using the keyboard/mouse. I found 15px to be perfect when using my pen or finger, but YMMV.

As you can see, one other thing Sway is doing is to restart Waybar, each time with a different configuration.

Waybar

Waybar is what Iā€™m using to show a little horizontal bar with information on the current workspace, next meeting, time, weather, battery status etc. Iā€™ve been using it for a few years and I love how customizable it is. I needed to make a few changes however: First, I wanted to make things a little bigger, so I can easily click them with my finger. Second, I removed some widgets that arenā€™t used, such as ā€œthe last line from my tmux sessionā€ or ā€œthe next eventā€, and made some widgets clickable - for example, since I donā€™t have the keyboardā€™s airplane mode button available, I made the Wifi widget toggle the wifi status. Third, Iā€™ve added four buttons - kill the current window, open the keyboard, translate the selection, and a launcher to start apps. This features are all binded to keyboard shortcuts, but I donā€™t have my keyboard on tablet-mode, so…

~/.config/waybar/tablet

{
  // ...
  "custom/touchbutton": {
    "format": " šŸš€ ",
    "on-click": "wofi -i --show drun -Iam -H 600"
  },
  "custom/closebutton": {
    "format": "   āŒ ",
    "on-click": "swaymsg kill"
  },
  "custom/keyboard": {
    "format": " āŒØļø",
    "on-click": "busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true"
  },
  "custom/translate": {
    "format": " šŸŒŽ ",
    "on-click": "/home/bjesus/.scripts/babylon"
  },
  "network": {
    "format-wifi": " ",
    "format-ethernet": "󰈀 {ifname}: {ipaddr}/{cidr}",
    "format-linked": " {ifname} (No IP)",
    "format-disconnected": "āš  N/A",
    "tooltip-format-wifi": "{essid}: {ipaddr}",
    "on-click": "/home/bjesus/.scripts/toggle-wifi"
  },
  "backlight": {
    "format": "{icon} {percent}",
    "on-scroll-up": "light -U 1",
    "on-scroll-down": "light -A 1",
    "format-icons": ["", ""],
    "on-click": "/home/bjesus/.scripts/set-screen-brightness"
  }
  // ...
}

Iā€™m sure there are more suitable launchers than wofi for this, but Iā€™ve been using it before and it works just fine on touch too. The closebutton uses swaymsg to kill the current window, which is useful for windows without window decorations. The keyboard button opens the keyboard regardless of the current focused element. Lastly, Iā€™ve written a script for toggling the WiFi and for setting the screen brightness ā€” two things Iā€™m fiddling with often to preserve battery life:

~/.scripts/toggle-wifi

#!/bin/sh
if iwctl device wlan0 show | grep Powered | grep on; then
  notify-send "turning wifi off" -t 1000
  iwctl device wlan0 set-property Powered off
else
  notify-send "turning wifi on" -t 1000
  iwctl device wlan0 set-property Powered on
fi

~/.scripts/set-screen-brightness

#!/bin/sh
light -S $(echo -e '01\n05\n10\n20\n30\n40\n50\n60\n70\n80\n90\n100' | wofi --show dmenu --columns 3 -G --cache-file /dev/null)

The first script uses iwd to toggle the WiFi power status. The second one uses wofi to display options of screen brightness, and then passes it to light to actually set the brightness. It’s not a slider and it’s not the most beautiful interface ever, but it’s one line of code and I enjoy the simplicity of reusing tools. You can just touch “60” with your finger to set the brightness to 60%.

brightness picker

The translate button is the one Iā€™m using the most however. It calls this tiny script:

#!/bin/sh
mkdir /tmp/babylon
export SWAYSHOT_HIDE_MESSAGE=true
export SWAYSHOT_SCREENSHOTS=/tmp/babylon
swayshot region
notify-send "$(tesseract /tmp/babylon/* - -l swe | trans --indent 0 --brief :en --no-bidi)"
rm -rf /tmp/babylon

This allows me to mark an area on the screen, pass it through an OCR, translate it, and then display the result as a message. Like this:

Note I did patch Swayshot to use the SWAYSHOT_HIDE_MESSAGE environment variable to not display a message about the screenshot being taken. Simply change the show_message function to this:

show_message() {
	if [ -z $SWAYSHOT_HIDE_MESSAGE ]; then
		if type notify-send >/dev/null  2>&1; then
			notify-send --expire-time=3000 --category=screenshot --icon="$2" "$3" "$1"
		fi
	fi
}

The swe dictionary

When studying Swedish we were recommended using Folkets Lexikon for translating Swedish to English. While the website is a bit clunky (especially on mobile), it lets you download the whole dictionary as XML. I previously wrote a simple CLI tool to search the XML file and print the word definition, but now on tablet mode I felt like it needed some UI. I used Textual to create a Terminal User Interface (TUI) that works also with touch.

The nice thing about using Textual is that I can also use swe now as an app on my Android phone. Termux + Termux:Widget lets me have an icon that starts the command immediately, and exits Termux when it quits. Touch works perfectly too. Textual can even start the interface as a web application, but I havenā€™t made use of that either.

I’ve also extended swe to make use of Wiktionary. By parsing the HTML there and converting it to Markdown while preserving the links, I basically turned swe into a text-only web-browser specific for this website.

Conclusion

When I first started working on this tablet-mode interface I often questioned its usefulness. Would it really be more comfortable than using the device as a laptop? Now when itā€™s done I can say that yes, for me, in a class-room environment where I mostly need to follow some PDF and mark things around it, itā€™s been great to have a tablet interface. Later, when Iā€™m home and I need to do more writing, I use the laptop normally. Having both functionalities in one device ended up being super useful. Lastly, even though Sway and my entire workflow was optimised for working with the keyboard, it ended up being quite simple to reconfigure things so that theyā€™ll work smoothly using touch.