背景
長男が高校を卒業し、GIGAスクールPCのSurface Go 3を貰い受けたので、元から付属していたJISキーボードを廃棄してUSキーボードを買ったら認識してくれず、Omarchyをインストールするもキーボードが使えないと死ぬので改めてインストールできそうなOSはないか?と探したらCachyOSのHandheld版というものがあることを知ってインストール。
Surface Go 2、キーボード認識してくれないのでOmarchy諦めてCachyOS Handheldを入れる👀 pic.twitter.com/XBgziQwnyb
— Motoki Tokifuji ™ 👨🦽➡️の無能賢者 (@tokifujp) 2026年5月4日
しかし、仮想キーボードでターミナルを叩いた時に異変に気付いた。なんとデフォルトで有効になっている仮想キーボードの plasma-keyboard には Esc, Tab, Ctrl, Alt キーが無いのだ…

かつて男塾塾長の江田島平八はこう言っていた。

諦めの悪いワシはClaudeに聞きながら対応したのだった…(ちなみに👇️のターミナルの作業は面倒くさいので初期で Tailscale と openssh を入れてSSHで作業してます)
目的
仮想キーボードにデフォルトでない Esc, Tab, Ctrl, Alt を追加してVim等のターミナル操作を可能にする。
最終構成
| 項目 | 内容 |
|---|---|
| OS | CachyOS Handheld (KDE Plasma / Wayland) |
| 仮想キーボード | maliit-keyboard (AUR) |
| カスタムレイアウト | /usr/lib/maliit/keyboard2/languages/en/Keyboard_en.qml |
| アップデート保護 | pacman hook |
キーボードレイアウト
[ Esc ][ Tab ][ Ctrl ][ Alt ][ - ][ / ][ ' ][ : ][ [ ][ ] ] [ q ][ w ][ e ][ r ][ t ][ y ][ u ][ i ][ o ][ p ] [ a ][ s ][ d ][ f ][ g ][ h ][ j ][ k ][ l ] [ ⇧ ][ z ][ x ][ c ][ v ][ b ][ n ][ m ][ ⌫ ] [ ?123 ][ , ][ Space ][ . ][ ↵ ]
手順
1. maliit-keyboard のインストール
paru -S maliit-framework maliit-keyboard
2. kwin の InputMethod を maliit に設定
kwriteconfig6 --file kwinrc --group Wayland --key InputMethod \
/usr/share/applications/com.github.maliit.keyboard.desktop
設定確認:
cat ~/.config/kwinrc | grep -i inputmethod # → InputMethod=/usr/share/applications/com.github.maliit.keyboard.desktop
再起動して反映:
sudo reboot
3. レイアウトファイルのバックアップ
sudo cp /usr/lib/maliit/keyboard2/languages/en/Keyboard_en.qml \
/usr/lib/maliit/keyboard2/languages/en/Keyboard_en.qml.bak
4. カスタムレイアウトの適用
usr/lib/maliit/keyboard2/languages/en/Keyboard_en.qml
import QtQuick 2.4
import MaliitKeyboard 2.0
import keys 1.0
KeyPad {
anchors.fill: parent
content: c1
symbols: "languages/Keyboard_symbols.qml"
Column {
id: c1
anchors.fill: parent
spacing: 0
// 追加行: ファンクションキー
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: 0
ActionKey { label: "Esc"; shifted: "Esc"; action: "keysequence"; valueToSubmit: "Escape"; noMagnifier: true; skipAutoCaps: true }
ActionKey { label: "Tab"; shifted: "Tab"; action: "keysequence"; valueToSubmit: "Tab"; noMagnifier: true; skipAutoCaps: true }
ActionKey { label: "Ctrl"; shifted: "Ctrl"; action: "keysequence"; valueToSubmit: "Ctrl"; noMagnifier: true; skipAutoCaps: true }
ActionKey { label: "Alt"; shifted: "Alt"; action: "keysequence"; valueToSubmit: "Alt"; noMagnifier: true; skipAutoCaps: true }
CharKey { label: "-"; shifted: "_"; extended: ["|","\\","~","`"]; extendedShifted: ["|","\\","~","`"] }
CharKey { label: "/"; shifted: "?"; }
CharKey { label: "'"; shifted: "\""; extended: ["`","'"]; extendedShifted: ["`","\""] }
CharKey { label: ":"; shifted: ";"; }
CharKey { label: "["; shifted: "{"; }
CharKey { label: "]"; shifted: "}"; }
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: 0
CharKey { label: "q"; shifted: "Q"; extended: ["1"]; extendedShifted: ["1"]; leftSide: true }
CharKey { label: "w"; shifted: "W"; extended: ["2"]; extendedShifted: ["2"] }
CharKey { label: "e"; shifted: "E"; extended: ["3","è","é","ê","ë","€"]; extendedShifted: ["3","È","É","Ê","Ë","€"] }
CharKey { label: "r"; shifted: "R"; extended: ["4"]; extendedShifted: ["4"] }
CharKey { label: "t"; shifted: "T"; extended: ["5","þ"]; extendedShifted: ["5","Þ"] }
CharKey { label: "y"; shifted: "Y"; extended: ["6","ý","¥"]; extendedShifted: ["6","Ý","¥"] }
CharKey { label: "u"; shifted: "U"; extended: ["7","û","ù","ú","ü"]; extendedShifted: ["7","Û","Ù","Ú","Ü"] }
CharKey { label: "i"; shifted: "I"; extended: ["8","î","ï","ì","í"]; extendedShifted: ["8","Î","Ï","Ì","Í"] }
CharKey { label: "o"; shifted: "O"; extended: ["9","ö","ô","ò","ó"]; extendedShifted: ["9","Ö","Ô","Ò","Ó"] }
CharKey { label: "p"; shifted: "P"; extended: ["0"]; extendedShifted: ["0"]; rightSide: true }
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: 0
CharKey { label: "a"; shifted: "A"; extended: ["ä","à","â","ª","á","å","æ"]; extendedShifted: ["Ä","À","Â","ª","Á","Å","Æ"]; leftSide: true }
CharKey { label: "s"; shifted: "S"; extended: ["ß","$"]; extendedShifted: ["$"] }
CharKey { label: "d"; shifted: "D"; extended: ["ð"]; extendedShifted: ["Ð"] }
CharKey { label: "f"; shifted: "F" }
CharKey { label: "g"; shifted: "G" }
CharKey { label: "h"; shifted: "H" }
CharKey { label: "j"; shifted: "J" }
CharKey { label: "k"; shifted: "K" }
CharKey { label: "l"; shifted: "L"; rightSide: true }
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: 0
ShiftKey {}
CharKey { label: "z"; shifted: "Z" }
CharKey { label: "x"; shifted: "X" }
CharKey { label: "c"; shifted: "C"; extended: ["ç"]; extendedShifted: ["Ç"] }
CharKey { label: "v"; shifted: "V" }
CharKey { label: "b"; shifted: "B" }
CharKey { label: "n"; shifted: "N"; extended: ["ñ"]; extendedShifted: ["Ñ"] }
CharKey { label: "m"; shifted: "M" }
ActionKey { label: "⌫"; shifted: "⌫"; action: "backspace"; noMagnifier: true; skipAutoCaps: true }
}
Item {
anchors.left: parent.left
anchors.right: parent.right
height: panel.keyHeight + Device.row_margin
SymbolShiftKey { id: symShiftKey; anchors.left: parent.left; height: parent.height }
LanguageKey { id: languageMenuButton; anchors.left: symShiftKey.right; height: parent.height }
CharKey { id: commaKey; label: ","; shifted: ","; extended: ["'","\"",";",":","@","&","(",")"]; extendedShifted: ["'","\"",";",":","@","&","(",")"]; anchors.left: languageMenuButton.right; height: parent.height }
SpaceKey { id: spaceKey; anchors.left: commaKey.right; anchors.right: dotKey.left; noMagnifier: true; height: parent.height }
CharKey { id: dotKey; label: "."; shifted: "."; extended: ["?","-","_","!","+","%","#","/"]; extendedShifted: ["?","-","_","!","+","%","#","/"]; anchors.right: enterKey.left; height: parent.height }
ActionKey { id: enterKey; label: "↵"; shifted: "↵"; action: "return"; noMagnifier: true; skipAutoCaps: true; anchors.right: parent.right; height: parent.height }
}
}
}
maliit再起動:
pkill maliit-keyboard
5. pacman hook でアップデート保護
カスタムファイルをバックアップ:
sudo mkdir -p /etc/maliit-keyboard-custom
sudo cp /usr/lib/maliit/keyboard2/languages/en/Keyboard_en.qml \
/etc/maliit-keyboard-custom/Keyboard_en.qml
hook作成:
sudo mkdir -p /etc/pacman.d/hooks sudo tee /etc/pacman.d/hooks/maliit-keyboard-layout.hook << 'EOF' [Trigger] Operation = Upgrade Type = Package Target = maliit-keyboard [Action] Description = Restoring custom maliit keyboard layout... When = PostTransaction Exec = /bin/cp /etc/maliit-keyboard-custom/Keyboard_en.qml /usr/lib/maliit/keyboard2/languages/en/Keyboard_en.qml EOF
トラブルシューティング
キーボードが出てこない
タスクバーのキーボードアイコンがONになっているか確認。テキストフィールドをタップして呼び出す。
maliit-keyboard が起動しない
ps aux | grep maliit
起動していなければ👇️
pkill maliit-keyboard # 念のため
kwin が自動起動するはずなのでログアウト→ログインで解決
kwin の InputMethod がリセットされた
cat ~/.config/kwinrc | grep -i inputmethod
maliit になっていなければ再設定👇️
kwriteconfig6 --file kwinrc --group Wayland --key InputMethod \
/usr/share/applications/com.github.maliit.keyboard.desktop
sudo reboot
システムアップデート後にレイアウトが戻った
hook が動作しているか確認:
cat /etc/pacman.d/hooks/maliit-keyboard-layout.hook ls /etc/maliit-keyboard-custom/
手動で復元:
sudo cp /etc/maliit-keyboard-custom/Keyboard_en.qml \
/usr/lib/maliit/keyboard2/languages/en/Keyboard_en.qml
pkill maliit-keyboard
できあがり🎉

注意事項
plasma-keyboard(qt6-virtualkeyboard ベース) は独自レイアウトシステムを持ちQT_VIRTUALKEYBOARD_LAYOUT_PATHは無効- maliit-keyboard は AUR パッケージ。CachyOS 公式リポジトリには含まれない
- 日本語入力 (
ja_JP) は/usr/lib/maliit/keyboard2/languages/ja/に別レイアウトあり。同様に編集可能 - ターミナル起動したらSurface Go 3って書いてあったので修正しました(´・ω・`)

Surface Go 2じゃなくて3でした(´・ω・`)