Notes on running Linux on a Panasonic Toughbook CF-33

I have a Panasonic Toughbook CF-33 as my backup/outdoor/camping laptop. It’s a rugged laptop with a 2K display and a detachable keyboard/touchpad base. It has a multitouch screen and pen input as well.

These are my notes for running Linux on it because I sometimes forget parts of the setup that are annoying to re-research if I need them again.

Basic stuff

I want to run Linux on this thing with the following requirements:

On-Screen keyboards

The laptop should be usable completely without the keyboard/touchpad base. There are a few distinct phases of usage that each need to be configured.

Firmware

The CF-33 can be configured to provide an on-screen keyboard in EFI. This is enough to operate GRUB and the EFI shell.

initrd

I use full disk encryption so I need an on-screen keyboard in the initrd environment. I use unl0kr for that, and an entry in /etc/crypttab to make it part of the initial ramdisk:

1$ doas apt install unl0kr

The entry in /etc/crypttab looks like this. It needs to be adjusted to match the device UUID and name, but the Debian setup process already configures that. This is mostly about the keyscript part:

sda3_crypt UUID=bc262d7a-3762-4b42-8641-b148bd944111 none luks,discard,x-initrd.attach,keyscript=/usr/share/initramfs-tools/scripts/unl0kr-keyscript

Configure unl0kr in /etc/unl0kr.conf. The defaults are usually OK though.

After configuring it, update the initrd:

1$ doas update-initramfs -u

Framebuffer

To get an on-screen keyboard on the frame buffer, I use buffyboard which I have installed to /usr/local/bin.

This is the wrapper I use to start it:

 1#!/bin/bash
 2
 3set -xu
 4
 5active=$(fgconsole)
 6
 7if [[ "$active" == "7" ]]; then
 8        # Turn it off always if we're in X.
 9        # TODO: Start onboard if necessary.
10        doas systemctl stop buffyboard.service
11else
12        isActive=$(systemctl is-active buffyboard.service)
13        if [[ "$isActive" == "active" ]]; then
14                doas systemctl stop buffyboard.service
15        else
16                doas systemctl start buffyboard.service
17        fi
18fi

The script stops buffyboard if it detects TTY 7 as the active TTY so that it doesn’t conflict with onboard for my X11 setup. The systemd service is buffyboard.service from buffyboard’s repository.

To start buffyboard, I use triggerhappy with the following configuration in /etc/triggerhappy/triggers.d/keyboard.conf:

KEY_PROG3       1       /usr/local/bin/osk.sh

My /etc/doas.conf allows the user nobody (which triggerhappy runs as) to start/stop the buffyboard systemd service like this:

1permit keepenv nopass nobody cmd systemctl args stop buffyboard.service
2permit keepenv nopass nobody cmd systemctl args start buffyboard.service

X11

My X on-screen keyboard of choice is onboard:

1$ doas apt install onboard

Lightdm

My /etc/lightdm/lightdm-gtk-greeter.conf looks like this:

[greeter]
keyboard=onboard

X Session

I use xbindkeys to always have working keyboard bindings, no matter which window manager is running. My .xbindkeysrc looks like this:

"lxterm"
        mod4 + space

"pkill onboard; onboard"
        XF86Launch3

"/bin/bash -c /home/gbe/bin/r.sh"
        mod4 + XF86Eject

"polybar-msg action pulseaudio toggle"
        XF86AudioMute

"polybar-msg action pulseaudio dec"
        XF86AudioLowerVolume

"polybar-msg action pulseaudio inc"
        XF86AudioRaiseVolume

"xscreensaver-command -lock"
        mod4 + ctrl + q

Driver patches

I have patched the panasonic-laptop kernel driver to map the keys labelled “A1” and “A2” on the tablet bezel to the key symbols LAUNCH3 and LAUNCH4. These are LAUNCHx keys instead of MACROxy because X11 apparently doesn’t register those macro keys. I don’t know precisely where I have that information from (probably one of the evdev headers), but it matches my observations.

GNSS receiver

I use QGIS as my GNSS client, and connect it to gpsd with its “GPS” toolbar. Works quite nicely.

Polybar

My polybar setup (~/.config/polybar/config.ini) looks like this:

[colors]
	background = #cccccc
	foreground = #111111
	primary = #4a799c
	disabled = #707880

[bar/main]
	height = 24pt
	dpi = 144

	background = ${colors.background}
	foreground = ${colors.foreground}

	line-size = 3pt

	border-size = 0pt

	padding-left = 0
	padding-right = 1

	module-margin = 1

	separator = |
	separator-foreground = ${colors.disabled}

	font-0 = Ubuntu
	font-1 = Noto Color Emoji:scale=7
	font-2 = Font Awesome 6 Free Solid
	font-3 = Weather Icons:style=Regular
	font-4 = DejaVu Sans

	modules-right = backlight project pulseaudio battery1 battery2 date

	cursor-click = pointer
	cursor-scroll = ns-resize

	enable-ipc = true
	override-redirect = false
	wm-restack = i3

[module/pulseaudio]
	type = internal/pulseaudio

	format-volume = <ramp-volume> <label-volume>

	ramp-volume-0 = 
	ramp-volume-0-foreground = ${colors.primary}

	ramp-volume-1 = 
	ramp-volume-1-foreground = ${colors.primary}

	ramp-volume-2 = 
	ramp-volume-2-foreground = ${colors.primary}

	label-volume = %percentage%%

	label-muted = M
	label-muted-foreground = ${colors.disabled}

	click-right = pavucontrol

[module/backlight]
	type = internal/backlight

	ramp-4 = 🌕
	ramp-3 = 🌔
	ramp-2 = 🌓
	ramp-1 = 🌒
	ramp-0 = 🌑

	format = <ramp>

[module/battery1]
	type = internal/battery
	battery = BAT1

	label-charging = %percentage%%
	label-discharging = %percentage%%
	label-low = %percentage%%

	format-charging = <animation-charging> <label-charging>
	format-discharging = <ramp-capacity> <label-discharging>
	format-low = <animation-low> <label-low>

	ramp-capacity-0 = 
	ramp-capacity-0-foreground = ${colors.primary}

	ramp-capacity-1 = 
	ramp-capacity-1-foreground = ${colors.primary}

	ramp-capacity-2 = 
	ramp-capacity-2-foreground = ${colors.primary}

	ramp-capacity-3 = 
	ramp-capacity-3-foreground = ${colors.primary}

	ramp-capacity-4 = 
	ramp-capacity-4-foreground = ${colors.primary}

	animation-charging-0 = 
	animation-charging-0-foreground = ${colors.primary}

	animation-charging-1 = 
	animation-charging-1-foreground = ${colors.primary}

	animation-charging-2 = 
	animation-charging-2-foreground = ${colors.primary}

	animation-charging-3 = 
	animation-charging-3-foreground = ${colors.primary}

	animation-charging-4 = 
	animation-charging-4-foreground = ${colors.primary}

	animation-charging-framerate = 750

	animation-low-0 = "|"
	animation-low-0-foreground = ${colors.primary}

	animation-low-1 = !
	animation-low-1-foreground = ${colors.primary}

	animation-low-framerate = 500

[module/battery2]
	inherit = module/battery1
	battery = BAT2

[module/project]
	type = custom/script
	exec = /home/gbe/bin/newproject.sh
	click-left = /home/gbe/bin/newproject.sh clicked

[module/ddate]
	type = custom/script
	interval = 600
	exec = ddate +"%{%A, %e %B%} %Y%N (%H)"

[module/date]
	type = internal/date
	interval = 1

	date = %m-%d %H:%M
	date-alt = %d%H%M%Z %b%y

	label = %date%
	label-foreground = ${colors.primary}

[settings]
	screenchange-reload = true
	pseudo-transparency = true